الدالة itertools.groupby()‎ في بايثون

من موسوعة حسوب
مراجعة 06:14، 19 يونيو 2018 بواسطة عبد اللطيف ايمش (نقاش | مساهمات) (←‏مصادر)
(فرق) → مراجعة أقدم | المراجعة الحالية (فرق) | مراجعة أحدث ← (فرق)

تعيد الدّالة itertools.groupby()‎ مُكرّرًا يُعيد مفاتيح مُتسلسلة ومجموعات (groups) تنتمي إلى هذه المفاتيح من الكائن القابل للتّكرار المعطى.

العمليّة التي تقوم بها الدّالة مُشابهة لكيفيّة عمل المُرشّح uniq في أنظمة Unix. إذ تُولّد نقطة توقّف (break) أو مجموعة جديدة في كلّ مرّة تتغيّر فيها قيمة الدّالةِ المفتاح (لذا يجب في الغالب ترتيب البيانات باستعمال نفس الدّالة المفتاح). وطريقة العمل هذه تختلف عن طريقة عمل عبارة GROUP BY في لغة SQL التي تجمع العناصر المُشتركة بغضّ النّظر عن ترتيب البيانات.

البنية العامة

itertools.groupby(iterable, key=None)

المعاملات

iterable

الكائن القابل للتّكرار المرغوب تجميع عناصره المُشتركة.

key

الدّالة التي تُعيد المفتاح الذي سيُستعمل لتجميع عناصر الكائن القابل للتّكرار لكل عنصر من عناصره. إن لم تُمرَّر للمُعامل أيّة قيمة، أو مُرّرت له القيمة None، فستكون قيمة المُعامل key الافتراضيّة عبارة عن دالّة هُويّة (identity function) تُعيد العنصر دون تغيير. يجب على الكائن iterable أن يكون مُرتّبًا على نفس الدّالة key.

القيمة المعادة

مُكرّر يحتوي على عدّة صفوف من عنصرين، العنصر الأول يُمثّل المفتاح، والعنصر الثّاني يُمثّل مجموعة القيم المُرتبطة بالمفتاح.

المجموعة المُعادة تكون مُكرّرًا يُشارك الكائن القابل للتّكرار الداخليّ مع الدّالة itertools.groupby()‎. ولأنّ المصدر مُشارَك، فستختفي المجموعات كلّما تقدّم الكائن المُعاد (عبر التّكرار). لذا إن كانت البيانات مُهمّة حتى بعد المرور على المجموعات المُعادة، فسيتوجّب عليك تخزينها في قائمة كما يلي:

groups = []
uniquekeys = []
data = sorted(data, key=keyfunc)
for k, g in groupby(data, keyfunc):
    groups.append(list(g))      # خزّن مُكرّر المجموعة كقائمة
    uniquekeys.append(k)

أمثلة

المثال التّالي يوضّح كيفيّة عمل هذه الدّالة، نستعمل في هذا المثال إحاطات القوائم (List comprehensions) لإنشاء قاموس من المجموعات المُعادة، مفاتيح القاموس تكون هي نفسها المفاتيح التي تُعاد من طرف الدّالة key‎ (في هذه الحالة العناصر هي نفسها المفاتيح) وتكون قيمة كل مفتاحٍ في القاموس عبارة عن قائمة بالعناصر المتعلّقة بالمفتاح (نحصل على قائمة العناصر بالدّالة list()‎):

>>> it = 'AAAABBBCCAABBB'
>>> groups = itertools.groupby(it)
>>> [{key: list(group)} for key, group in groups]
[{'A': ['A', 'A', 'A', 'A']},
 {'B': ['B', 'B', 'B']},
 {'C': ['C', 'C']},
 {'A': ['A', 'A']},
 {'B': ['B', 'B', 'B']}]

وكما تُلاحظ، فالتّجميع هنا مُتقطّع لأنّ الكائن القابل للتّكرار المُعطى ليس مُرتّبًا. لذا لو رتّبناه بالدّالة sorted()‎ قبل أن نُقسّمه إلى مجموعات فسنحصل على مجموعات أشمل دون تكرار للمفتاح:

>>> it = sorted('AAAABBBCCAABBB')
>>> it
['A', 'A', 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'B', 'B', 'C', 'C']
>>> groups = itertools.groupby(it)
>>> [{key: list(group)} for key, group in groups]
[{'A': ['A', 'A', 'A', 'A', 'A', 'A']},
 {'B': ['B', 'B', 'B', 'B', 'B', 'B']},
 {'C': ['C', 'C']}
]

يُمكن استعمال المُعامل key‎ لتقسيم قاموس أشخاص إلى مجموعات حسب أعمارهم مثلًا، وذلك عبر استعمال تعبير lambda يستقبل القاموس ويُعيد عمر الشّخص كما يلي:

>>> people = [{'name': 'Ahmed', 'age': 18},
...           {'name': 'Mohamed', 'age': 18},
...           {'name': 'Yasser', 'age': 18},
...           {'name': 'Yossuf', 'age': 24},
...           {'name': 'Fatima', 'age': 27}
...           ]
 
>>> grouped_people = itertools.groupby(people, key=lambda x: x['age'])
 
>>> for age, group in grouped_people:
...     print(f'{age}: {list(group)}')
...

18: [{'name': 'Ahmed', 'age': 18},
     {'name': 'Mohamed', 'age': 18},
     {'name': 'Yasser', 'age': 18}]

24: [{'name': 'Yossuf', 'age': 24}]

27: [{'name': 'Fatima', 'age': 27}]

ملاحظات

الدّالة مُكافئة لما يلي:

class groupby:
    # [k for k, g in groupby('AAAABBBCCDAABBB')] --> A B C D A B
    # [list(g) for k, g in groupby('AAAABBBCCD')] --> AAAA BBB CC D
    def __init__(self, iterable, key=None):
        if key is None:
            key = lambda x: x
        self.keyfunc = key
        self.it = iter(iterable)
        self.tgtkey = self.currkey = self.currvalue = object()
    def __iter__(self):
        return self
    def __next__(self):
        while self.currkey == self.tgtkey:
            self.currvalue = next(self.it)    # Exit on StopIteration
            self.currkey = self.keyfunc(self.currvalue)
        self.tgtkey = self.currkey
        return (self.currkey, self._grouper(self.tgtkey))
    def _grouper(self, tgtkey):
        while self.currkey == tgtkey:
            yield self.currvalue
            try:
                self.currvalue = next(self.it)
            except StopIteration:
                return
            self.currkey = self.keyfunc(self.currvalue)

انظر أيضًا

مصادر