2008-10-01 3 views
6

これは、アルゴリズムの論理的な質問(方法)、部分実装の質問(どのように最善を尽くすか)です。私はDjangoと仕事をしているので、私はそれを分かち合うと思った。Django/Python - 多対多の関係から共通セットを使ってオブジェクトをグループ化する

Pythonでは、問題がhow-do-i-use-pythons-itertoolsgroupbyに多少関連していることに言及することは重要です。どのように1は、改造の共通セットを車でグループ化され、車のリストを取得ん

from django.db import models 

class Car(models.Model): 
    mods = models.ManyToManyField(Representative) 

from django.db import models 

class Mods(models.Model): 
    ... 

:?

を使用すると、2つのDjangoのモデルの派生クラスを与えられていると仮定しますか

I.e.私はクラスlikesoを取得したい:

Cars_by_common_mods = [ 
    { mods: { 'a' }, cars: { 'W1', 'W2' } }, 
    { mods: { 'a', 'b' }, cars: { 'X1', 'X2', 'X3' }, }, 
    { mods: { 'b' }, cars: { 'Y1', 'Y2' } }, 
    { mods: { 'a', 'b', 'c' }, cars: { 'Z1' } }, 
] 

私のような何かを考えてきた:GROUPBYにはない(おそらく他の理由の中で)ので、動作しないこと、しかし

def cars_by_common_mods(): 
    cars = Cars.objects.all() 

    mod_list = []  

    for car in cars: 
    mod_list.append({ 'car': car, 'mods': list(car.mods.all()) } 

    ret = [] 

    for key, mods_group in groupby(list(mods), lambda x: set(x.mods)): 
    ret.append(mods_group) 

    return ret 

改造セットでグループ化しているようです。 mod_listはgroupbyで動作するようにソートされていなければなりません。言い換えれば、私は啓発と照明の両方になるシンプルでエレガントなものがあると確信しています。

乾杯&ありがとう!

答えて

4

リストを最初にソートしようとしましたか?あなたが提案したアルゴリズムは、データベースのヒットが多いにもかかわらず、うまくいくはずです。これらのクエリについて

import itertools 

cars = [ 
    {'car': 'X2', 'mods': [1,2]}, 
    {'car': 'Y2', 'mods': [2]}, 
    {'car': 'W2', 'mods': [1]}, 
    {'car': 'X1', 'mods': [1,2]}, 
    {'car': 'W1', 'mods': [1]}, 
    {'car': 'Y1', 'mods': [2]}, 
    {'car': 'Z1', 'mods': [1,2,3]}, 
    {'car': 'X3', 'mods': [1,2]}, 
] 

cars.sort(key=lambda car: car['mods']) 

cars_by_common_mods = {} 
for k, g in itertools.groupby(cars, lambda car: car['mods']): 
    cars_by_common_mods[frozenset(k)] = [car['car'] for car in g] 

print cars_by_common_mods 

、:

import collections 
import itertools 
from operator import itemgetter 

from django.db import connection 

cursor = connection.cursor() 
cursor.execute('SELECT car_id, mod_id FROM someapp_car_mod ORDER BY 1, 2') 
cars = collections.defaultdict(list) 
for row in cursor.fetchall(): 
    cars[row[0]].append(row[1]) 

# Here's one I prepared earlier, which emulates the sample data we've been working 
# with so far, but using the car id instead of the previous string. 
cars = { 
    1: [1,2], 
    2: [2], 
    3: [1], 
    4: [1,2], 
    5: [1], 
    6: [2], 
    7: [1,2,3], 
    8: [1,2], 
} 

sorted_cars = sorted(cars.iteritems(), key=itemgetter(1)) 
cars_by_common_mods = [] 
for k, g in itertools.groupby(sorted_cars, key=itemgetter(1)): 
    cars_by_common_mods.append({'mods': k, 'cars': map(itemgetter(0), g)}) 

print cars_by_common_mods 

# Which, for the sample data gives me (reformatted by hand for clarity) 
[{'cars': [3, 5], 'mods': [1]}, 
{'cars': [1, 4, 8], 'mods': [1, 2]}, 
{'cars': [7],  'mods': [1, 2, 3]}, 
{'cars': [2, 6], 'mods': [2]}] 

今、あなたはあなたがで動作するように、完全なオブジェクトが必要な場合、あなたは、単一の操作を行うことができ、車のIDとモッズIDのあなたのリストを持っていること各モデルの完全なリストを取得するためにそれぞれを照会して、それらのIDでキー入力された検索のためにdictを作成してください。そうすれば、ボブはあなたの父親の兄弟です。

2

チェックregroup。それはテンプレートのためだけですが、私はこの種の分類がプレゼンテーション層に属していると思います。

+0

返信いただきありがとうございます。私は再グループを見ましたが、(無言の)問題は、最初のグループ分けの後に行うべきより多くの論理があるということです。しかし、それは良いヒントです。私が再グループの周りにそれをデザインできるかどうかがわかります。 –

1

ここにはいくつか問題があります。

groupbyを呼び出す前にリストをソートしていませんでした。これは必須です。 itertools documentation

一般的に、イテレートは同じキー機能で既にソートされている必要があります。

次に、groupbyによって返されたリストを複製しません。再び、ドキュメントの状態:

返されるグループ自体は、基底のiterableを groupby()で共有するイテレータです。ソースが共有されているため、groupbyオブジェクトがアドバンスされると、先のグループ は表示されなくなります。そのデータが後で必要になるのであれば、それは リストとして保存する必要があります。

groups = [] 
uniquekeys = [] 
for k, g in groupby(data, keyfunc): 
    groups.append(list(g))  # Store group iterator as a list 
    uniquekeys.append(k) 

そして最終的ミスがキーとしてセットを使用しています。彼らはここで働かない。クイックフィックスはソートされたタプルにそれらをキャストすることです(よりよい解決策があるかもしれませんが、今はそれを考えることはできません)。

だから、あなたの例では、最後の部分は次のようになります。パフォーマンスが問題(つまり、ページ上の車の多く、または高トラフィックサイト)である場合

sortMethod = lambda x: tuple(sorted(set(x.mods))) 
sortedMods = sorted(list(mods), key=sortMethod) 
for key, mods_group in groupby(sortedMods, sortMethod): 
    ret.append(list(mods_group)) 
+0

私はいつもこの答えに戻ります。ハハ –

1

を、denormalizationは理にかなっていますあなたの問題を副作用として単純化します。

多対多関係の非正規化はややこしいかもしれないことに注意してください。私はそのようなコード例をまだ実行していません。

0

ありがとうございました。私はこの問題を解消しています。 「最高の」解決策はまだ私を逃していますが、私はいくつかの考えを持っています。

私が扱っているデータセットの統計について言及する必要があります。 75%のケースでは1つのModがあります。 24%の症例では、2つの。ケースの1%では、ゼロまたは3つ以上が存在します。すべてのModには、少なくとも1つの固有のCarがありますが、Modは多数のCarsに適用されます。

class ModSet(models.Model): 
    mods = models.ManyToManyField(Mod) 

class Car(models.Model): 
    modset = models.ForeignKey(ModSet) 

それはCar.modsetによってグループに些細だに車を変更:

は私が考えられます(ただし実装されていない)のような、そう何かをしました、と言った私は、たとえば、Javierによって提案されたように、再編成を使用できます。それはより単純で合理的にエレガントな解決策に見えます。思考は非常に高く評価されるだろう。