2008-10-01 6 views
6

이 부분은 알고리즘 로직 질문 (수행 방법), 부분 구현 질문 (최선을 다하는 방법)입니다. 나는 장고와 함께 일하고 있기 때문에 나는 그걸 공유 할 것이라고 생각했다.Django/Python - 다 대다 관계에서 공통 세트로 객체 그룹화

파이썬에서는 문제가 다소 how-do-i-use-pythons-itertoolsgroupby과 관련이 있다고 언급 할 가치가 있습니다.

는 두 장고 모델 파생 클래스 주어진있어 가정 :

from django.db import models 

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

from django.db import models 

class Mods(models.Model): 
    ... 

어떻게이 개조의 공통 세트 자동차로 그룹화 자동차의 목록을 얻을 수 있습니까?

e.e.

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' } }, 
] 

내가 좋아하는 뭔가 생각을 해 봤는데 : : 나는 클래스 likeso 싶어하지만

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 

때문에 (아마도 다른 이유들)이 작동하지 않습니다 GROUPBY하지 않습니다 개조 세트로 그룹화하는 것 같습니다. 나는 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 

, 그 쿼리에 대해 : 당신이 일을 전체 개체를 필요로하는 경우 이제 자동차 ID와 모드 ID의 당신의 목록을 가지고 있음을

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에 의해 키가있는 사람들을 위해 dict 조회를 만듭니다. 그런 다음 Bob은 귀하의 속담 인 아버지의 형제입니다.

2

체크 . 그것은 단지 템플릿을위한 것이지만, 이런 종류의 분류는 어쨌든 프리젠 테이션 레이어에 속한 것 같습니다.

+0

답장을 보내 주셔서 감사합니다. 나는 재편성을 보았지만 (말도 안되는) 문제는 초기 그룹화 후에 더 많은 논리가 수행된다는 것이다. 그래도 좋은 팁입니다. 내가 재편성 할 때 주위에 디자인 할 수 있는지 알 수있을거야. –

1

여기에 몇 가지 문제가 있습니다.

groupby를 호출하기 전에 목록을 정렬하지 않았습니다. 필수 항목입니다. itertools documentation :

일반적으로 iterable은 이미 동일한 키 기능으로 정렬되어야합니다.

그런 다음 groupby에서 반환 한 목록을 복제하지 마십시오. 다시, 문서 상태 :

반환 된 그룹 자체는 groupby()와 기본 iterable을 공유하는 반복기입니다.소스가 공유되어 있기 때문에 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 %의 경우에는 하나의 Mod가있을 것입니다. 24 %의 경우, 2 명. 케이스의 1 %에서 0 또는 3 개 이상이있을 것입니다. 모든 모드의 경우, 적어도 하나의 고유 한 자동차가 있지만 Mod는 수많은 자동차에 적용될 수 있습니다.

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

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

그것은 Car.modset에 의해 그룹에 사소한에 자동차를 변경 :

내가 생각 (하지만 구현되지 않음) 같은 - 그래서 뭔가를했다, 그런 말로 미루어 보아, 나는 Javier가 제안한 것처럼 재 그룹을 사용할 수 있습니다. 그것은 간단하고 합리적으로 우아한 해결책 인 것처럼 보입니다. 생각은 많이 감사 할 것입니다.