جانغو / بيثون - تجميع الكائنات عن طريق مجموعة مشتركة من كثير لكثير من العلاقات

StackOverflow https://stackoverflow.com/questions/160298

سؤال

وهذا هو السؤال جزء خوارزمية منطق (كيف نفعل ذلك)، مسألة تنفيذ جزء (كيف نفعل ذلك أفضل!). أنا أعمل مع جانغو، حتى ظننت أنني كنت مشاركة في ذلك.

في بيثون، ومن الجدير بالذكر أن المشكلة ترتبط إلى حد ما كيف do- ط استخدام الثعابين-itertoolsgroupby.

لنفترض أنك أعطيت فئتين المشتقة طراز جانغو:

from django.db import models

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

و

from django.db import models

class Mods(models.Model):
   ...

وكيف يمكن للمرء الحصول على قائمة من السيارات، التي تم تجميعها حسب سيارات مع مجموعة مشتركة من وزارة الدفاع؟

وأي بمعنى. أريد الحصول على 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' } },
]

ولقد تم التفكير في شيء من هذا القبيل:

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. كل شيء أن أقول، وأنا واثق هناك شيء بسيط وأنيق الى ان هناك ستكون على حد سواء المنير وإلقاء الضوء.

هتاف وشكرا!

هل كانت مفيدة؟

المحلول

هل حاولت فرز القائمة أولا؟ الخوارزمية المقترحة التي يجب أن تعمل، ولكن مع الكثير من الزيارات قاعدة البيانات.

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

والآن بعد أن كنت قد حصلت على القوائم الخاصة بك من هويات السيارات وهويات وزارة الدفاع، إذا كنت في حاجة إلى الأشياء كاملة للعمل مع، هل يمكن أن تفعل استعلام واحد لكل للحصول على قائمة كاملة لكل نموذج وإنشاء dict البحث عن تلك، مرتبطا بموجب البطاقة الشخصية - ثم، في اعتقادي، بوب هو شقيق والد المثل الخاص بك

نصائح أخرى

يعيد تجميع . انها فقط لقوالب، ولكن أعتقد أن هذا النوع من تصنيف ينتمي إلى طبقة العرض على أي حال.

لديك بعض المشاكل هنا.

وأنت لم فرز القائمة الخاصة بك قبل استدعاء groupby، وهذا هو المطلوب. من itertools الوثائق :

<اقتباس فقرة>   

وعموما، يحتاج إلى iterable بالفعل يتم فرز على نفس الوظيفة الرئيسية.

وبعد ذلك، كنت لا تكرار القائمة التي أرجعها 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))

إذا الأداء هو مصدر قلق (أي الكثير من السيارات على صفحة أو موقع حركة المرور العالية)، <وأ href = "http://groups.google.com/group/django-developers/browse_thread/thread/ 9a672d5bbbe67562 "يختلط =" نوفولو noreferrer "> denormalization المنطقي، ويبسط مشكلتك كأثر جانبي.

ويجب أن تدرك أن denormalizing كثير لكثير العلاقات قد تكون صعبة بعض الشيء على الرغم من. أنا لم تصل إلى أي من هذه أمثلة التعليمات البرمجية حتى الان.

وشكرا لكم جميعا على الردود المفيدة. لقد تم توصيل بعيدا في هذه المشكلة. حل "أفضل" لا يزال يتملص لي، ولكني بعض الأفكار.

وأود أن أذكر أن الإحصاءات من مجموعة البيانات أعمل معه. في 75٪ من الحالات، سوف يكون هناك واحد وزارة الدفاع. في 24٪ من الحالات، وهما. في 1٪ من الحالات سيكون هناك الصفر، أو ثلاثة أو أكثر. لكل وزارة الدفاع، هناك واحد على الأقل سيارة فريدة من نوعها، على الرغم من وزارة الدفاع يمكن تطبيقها على العديد من سيارات.

وأما وقد قلت ذلك، لقد اعتبر (ولكن لم تنفذ) شيء من هذا القبيل، لذلك:

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

ووتغيير السيارات ل

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

وانها تافهة لمجموعة من Car.modset: يمكنني استخدام يعيد تجميع، على النحو الذي اقترحه خافيير، على سبيل المثال. يبدو الحل الأبسط وأنيقة إلى حد معقول. أن الأفكار محل تقدير كبير.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top