Beginner: data foundations · Python for data · topic 4 of 62 · 30 min
Counting and grouping: collections and statistics
Explanation
Why counting and grouping matter
Most business questions are counts or totals by group. How many orders came from each city? What is the total sale per item? Which complaint type is most common? In SQL this is GROUP BY, in pandas it is groupby, and in Excel it is a pivot table. Learning it in plain Python first shows you exactly what those tools do.
Counter: how many of each
Counter, from the collections module, takes a list and counts how many times each value appears. It behaves like a dict from value to count. Asking for a value that never appeared gives 0, not a KeyError.
from collections import Counter
cities = ["Pune", "Delhi", "Pune", "Mumbai", "Pune", "Delhi"]
c = Counter(cities)
print(c) # Counter({'Pune': 3, 'Delhi': 2, 'Mumbai': 1})
print(c["Pune"], c["Goa"]) # 3 0
print(c.most_common(2)) # [('Pune', 3), ('Delhi', 2)]most_common breaks ties by the order in which values were first seen. If you need a fixed rule, such as alphabetical order for ties, sort yourself: sorted(c.items(), key=lambda kv: (-kv[1], kv[0])). The minus sign on the count sorts the biggest count first.
Grouping with defaultdict
Grouping means putting rows into buckets by a key, such as city. With a normal dict you must check whether the bucket exists before adding to it. defaultdict(list) creates an empty list automatically the first time you use a new key, so the loop stays short.
from collections import defaultdict
orders = [("Pune", 250), ("Delhi", 900), ("Pune", 120), ("Chennai", 640), ("Delhi", 300)]
by_city = defaultdict(list)
for city, amount in orders:
by_city[city].append(amount)
print(dict(by_city)) # {'Pune': [250, 120], 'Delhi': [900, 300], 'Chennai': [640]}Group, then aggregate
To aggregate means to squash a group of values into one number: a sum, a count, an average, a maximum. Group first, then aggregate each bucket. For a running total you can also skip the lists and use defaultdict(int), which starts every new key at 0.
totals = {city: sum(amounts) for city, amounts in by_city.items()}
print(totals) # {'Pune': 370, 'Delhi': 1200, 'Chennai': 640}
totals2 = defaultdict(int)
for city, amount in orders:
totals2[city] += amount
print(dict(totals2) == totals) # TrueA preview of the statistics module
Python's statistics module gives common summaries in one call. The mean is the ordinary average, the median is the middle value after sorting, and the mode is the most common value. Module 2 explains when each one is the right choice and teaches you to compute them by hand.
import statistics
marks = [72, 85, 85, 90, 64]
print(statistics.mean(marks)) # 79.2
print(statistics.median(marks)) # 85
print(statistics.mode(marks)) # 85The same idea in pandas and SQL
import pandas as pd
df = pd.DataFrame({"city": ["Pune", "Delhi", "Pune", "Chennai", "Delhi"],
"amount": [250, 900, 120, 640, 300]})
print(df["city"].value_counts()) # Pune 2, Delhi 2, Chennai 1
print(df.groupby("city")["amount"].sum()) # Chennai 640, Delhi 1200, Pune 370Common mistakes
- KeyError when adding to a normal dict: totals[city] += amount fails for a new city. Fix: use defaultdict(int), or totals[city] = totals.get(city, 0) + amount.
- Counting text that is not clean: "Pune", "pune" and "Pune " become three different keys. Fix: clean first, for example city.strip().title().
- Trusting tie order from most_common when the order matters. Fix: sort with an explicit key such as (-count, name).
- Calling statistics.mean on an empty list, which raises StatisticsError. Fix: check that the list has values first.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Counting and grouping: collections and statistics · 1/7
In this lesson you will answer the two most common data questions: how many of each, and what is the total per group.
Prefer a video by a teacher? Find free videos on “python collections Counter defaultdict group by tutorial” on YouTube ↗
In real life: Complaint types at a bank call centre
A bank's customer care team logs each complaint with a type, such as "UPI failed", "card blocked" or "KYC update". Every week an analyst counts the types, finds the top three, and totals the refund amounts per branch. The sample below is made up, but the method is exactly what such a weekly report does.
from collections import Counter, defaultdict
complaints = ["UPI failed", "card blocked", "UPI failed", "KYC update", "UPI failed", "card blocked"]
print(Counter(complaints).most_common(2))
refunds = [("Andheri", 500), ("Thane", 1200), ("Andheri", 300)]
per_branch = defaultdict(int)
for branch, amount in refunds:
per_branch[branch] += amount
print(dict(per_branch))Practice (0/3 done)
Write count_values(items) that returns a plain dict mapping each value to how many times it appears. Example: count_values(["a", "b", "a"]) returns {"a": 2, "b": 1}.
Write total_per_group(rows, group_key, value_key) that returns a dict mapping each group to the sum of value_key in that group. Example: rows of {"city": ..., "amount": ...} grouped by "city" summing "amount".
Write top_k(items, k) that returns the k most common values as a list of (value, count) tuples, biggest count first. When counts tie, put the values in alphabetical order. If there are fewer than k distinct values, return them all.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Writing data code that others can trustPython for data · same module
- collections and itertoolsPython Programming · Errors, modules and the standard library
- Counting and totals: aggregate functionsBackend Development · Databases and SQL basics
- GROUP BYSQL and Databases · Aggregation: GROUP BY and HAVING
- Types of data, and the centre: mean, median, modeStatistics made simple
- GROUP BY and HAVINGBackend Development · SQL in depth
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
c = Counter(["a", "b", "a"]). What is c["z"]?
Answer: 0
A Counter returns 0 for values it has never seen.
Why use defaultdict(list) for grouping?
Answer: It creates an empty list automatically for a new key
Without it, you must check whether a key exists before appending.
What does "group, then aggregate" mean?
Answer: Put rows into buckets by a key, then turn each bucket into one number such as a sum
It is the idea behind SQL GROUP BY, pandas groupby and Excel pivot tables.
Counting cities gives {"Pune": 3, "pune": 1, "Pune ": 2}. What is the best fix?
Answer: Clean the text first, for example city.strip().title()
The three keys are the same city written differently. Cleaning merges them into Pune: 6.
marks = [40, 50, 90]. What is statistics.median(marks)?
Answer: 50
Sorted, the middle value of 40, 50, 90 is 50. The mean would be 60.
With d = defaultdict(int), what happens on d["x"] += 1 when "x" is not a key yet?Related
Answer: d["x"] becomes 1
defaultdict(int) first creates d["x"] = 0, then adds 1.
What does SELECT city, SUM(amount) FROM orders GROUP BY city return?Related
Answer: One row per city with its total
GROUP BY city makes one group per city, and SUM runs on each group.
students has 8 rows in 5 different cities. How many rows does SELECT city, COUNT(*) FROM students GROUP BY city return?Related
In right-skewed data such as house prices, how do the mean and median usually compare?Related
Answer: The mean is bigger
The long tail of very expensive houses pulls the mean above the median.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 1 more topic in “Python for data” before its exam: Writing data code that others can trust.
- Module 1 exam: Python for data · 14 questions · 20 min, without help. 5 topics of this module still to finish first.
- Then the next module, Statistics made simple. 62 topics left in the course before the final exam.