חזרה

תעשו לי טובה ותעבדו עם Counter

קצת על שימוש בתת מחלקה בספרייה collections של פייתון, לספירת מופעים מהירה של ערכים בתוך מערך. בתמונה: פאי 🍰


לאחרונה יצא לי לעבור על הרבה קוד, עם הרבה קטעים שמנתחים סטטיסטיקות ממקורות מידע מסוימים, וכמובן איך לא - מציגים את המידע בדשבורדים עם גרפים ותרשימים ו'פאי' (תרשים עוגה).

ניקח כדוגמא אם יש לי מנגנון אנליטיקס, שאוסף מידע על המקורות מהם הגיעו גולשים לאתר. כשאני יתשאל את הדטבייס אני יקבל רשימת ביקורים באתר, ויצור ממנה מערך של המקורות. כזה, לדוגמא:

['facebook', 'facebook', 'google', 'twitter', 'google', 'facebook']

הצורך הוא ליצור רשימה של כל הערכים שמופיעים במערך, וספירה של כמות ההופעות של כל אחד מהם במערך. שיהיה תוצאה כזו:

[('facebook': 3), ('google': 2), ('twitter': 1)]

בקוד שראיתי, זה מומש בערך כך:

sources = ['facebook', 'facebook', 'google', 'twitter', 'google', 'facebook']
sources_sum = {}
for source in sources:
  if source not in sources_sum:
    sources_sum[source] = 1
  else:
    sources_sum[source] += 1
results = [(source, sources_sum[source]) for source in sources_sum]
print(results)  # [('facebook', 3), ('google', 2), ('twitter', 1)]

מה זה Counter?

בספרייה collections שהיא ספרייה מובנית בפייתון (3+) נמצא את Counter. התיאור בתיעוד הוא:

A Counter is a dict subclass for counting hashable objects. It is a collection where elements are stored as dictionary keys and their counts are stored as dictionary values.  Counts are allowed to be any integer value including zero or negative counts.  The Counterclass is similar to bags or multisets in other languages.

בפעולה הבסיסית שלו הוא פשוט עושה את זה: מוציא ממערך את מספר המופעים של כל ערך שבתוכו.
עם Counter ניתן ליצור את התוצאה דלעיל בשורה אחת פשוטה (ולדעתי מאוד קריאה):

from collections import Counter
    
sources = ['facebook', 'facebook', 'google', 'twitter', 'google', 'facebook']
results = dict(Counter(sources)).items()
print(results)  # dict_items([('facebook', 2), ('google', 2), ('twitter', 1)])

Counter מחזיר אובייקט Counter שממנו ניתן ליצור dict - ממנו ניתן לחלץ מערך של tuples - כל המפתחות והערכים שלהם עם ידי המתודה items().

אני בטוח שמבחינת ביצועים יהיו יתרונות לטובת ה-Counter וזו עוד סיבה להמשתמש בו כמובן, אבל מה שתפס אותי זו הקריאות והקוד הקצר.
ריפקטורינג של קטעים כאלה, יכול לקצר קטעי קוד ארוכים ומפרכים, ולתת למפתחים חדשים שעובדים על הקוד להבין יותר מה הם עושים.

יש ל-Counter עוד הרבה יתרונות ומתודות מעניינות (כמו האופציה להשוואה בין שני Counters - שזה משווה את התוכן שלהם, האופציה להוספה של Counter ל-Counter כך שבעצם נוצר Counter חדש עם שילוב של הערכים משני ה-Counters, המתודה ()most_common שמביאה במהירות את הערך הגבוה ביותר ב-Counter ועוד) את הכול אפשר ללמוד מהתיעוד הברור, אני מצרף בסוף גם כמה בלוג פוסטים טובים שקראתי עליו.
אז מי כאן שכותב פייתון, ויכתוב בזכותי עוד Counter בעולם - והיה זה שכרי.

יום טוב וקוד טוב 😉


כמה מראי מקומות בסיסיים ל-Counter:


אם יש לכם איזו שאלה ❔✨ או כל תגובה 💬, הארה 💡 והערה ❕ שהיא על הפוסט - אשמח מאוד! אם תכתבו אותה בהערות כאן למטה
פשוט להתחבר עם חשבון גיטהב ולהגיב 🎉