group_by_demographic gives you {"Junior": [r1, r2, r3], "Senior": [r4, r5]}. Your advisor wants the count and average satisfaction per group — the cross-tab cell values. How do you get from a list of response dicts to a single number?
Loop over the group's list, sum the satisfaction scores, divide by count. I can do that with a for loop now.
Exactly. And the output is a nested structure — a dict inside a dict. {"Junior": {"count": 3, "avg_satisfaction": 3.87}}. Python dicts can hold any value, including other dicts. That's what makes them the right structure for a cross-tab:
groups = group_by_demographic(responses, "year")
summary = {}
for group, items in groups.items():
scores = [float(r["satisfaction"]) for r in items]
summary[group] = {"count": len(scores), "avg": sum(scores) / len(scores)}
print(summary)groups.items() — that's the key-value pair iteration I saw in Day 13? So group is the demographic label and items is the list of response dicts?
Exactly right. .items() yields (key, value) pairs — here ("Junior", [list of dicts]). Unpack them into two names and you have both the label and the data in one line:
def demographic_summary(responses: list, field: str) -> dict:
groups = group_by_demographic(responses, field)
summary = {}
for group, items in groups.items():
scored = score_per_response(items)
scores = [s["score"] for s in scored]
summary[group] = {"count": len(scores), "avg_satisfaction": round(sum(scores) / len(scores), 2)}
print(f"Summary for {len(summary)} groups")
return summaryThe output is a nested dict — {"Junior": {"count": 42, "avg_satisfaction": 3.87}}. I can iterate that, format it with format_summary_line, and paste it straight into my methods table.
Your entire pivot-table workflow just became a chain of four function calls.
I used group_by_demographic and score_per_response together without rewriting any logic. Everything from this week and last week composes.
sum(scores) / len(scores) raises ZeroDivisionError if scores is empty. If a group somehow has no valid scores, the function crashes. In Week 3 you will wrap this in try/except — for now, filter_complete_responses upstream prevents the empty case.
A dict value can be another dict: {"Junior": {"count": 42, "avg": 3.87}}. Iterate d.items() to get (key, value) pairs:
for group, items in groups.items():
# group = 'Junior', items = [list of dicts]group_by_demographic → {group: [responses]}.items(), compute count and average per group{"count": N, "avg_satisfaction": X} as nested dictround(x, 2) vs :.2fround() returns a float (for computation). :.2f formats for display only. Use round() when storing data; :.2f when building output strings.
group_by_demographic gives you {"Junior": [r1, r2, r3], "Senior": [r4, r5]}. Your advisor wants the count and average satisfaction per group — the cross-tab cell values. How do you get from a list of response dicts to a single number?
Loop over the group's list, sum the satisfaction scores, divide by count. I can do that with a for loop now.
Exactly. And the output is a nested structure — a dict inside a dict. {"Junior": {"count": 3, "avg_satisfaction": 3.87}}. Python dicts can hold any value, including other dicts. That's what makes them the right structure for a cross-tab:
groups = group_by_demographic(responses, "year")
summary = {}
for group, items in groups.items():
scores = [float(r["satisfaction"]) for r in items]
summary[group] = {"count": len(scores), "avg": sum(scores) / len(scores)}
print(summary)groups.items() — that's the key-value pair iteration I saw in Day 13? So group is the demographic label and items is the list of response dicts?
Exactly right. .items() yields (key, value) pairs — here ("Junior", [list of dicts]). Unpack them into two names and you have both the label and the data in one line:
def demographic_summary(responses: list, field: str) -> dict:
groups = group_by_demographic(responses, field)
summary = {}
for group, items in groups.items():
scored = score_per_response(items)
scores = [s["score"] for s in scored]
summary[group] = {"count": len(scores), "avg_satisfaction": round(sum(scores) / len(scores), 2)}
print(f"Summary for {len(summary)} groups")
return summaryThe output is a nested dict — {"Junior": {"count": 42, "avg_satisfaction": 3.87}}. I can iterate that, format it with format_summary_line, and paste it straight into my methods table.
Your entire pivot-table workflow just became a chain of four function calls.
I used group_by_demographic and score_per_response together without rewriting any logic. Everything from this week and last week composes.
sum(scores) / len(scores) raises ZeroDivisionError if scores is empty. If a group somehow has no valid scores, the function crashes. In Week 3 you will wrap this in try/except — for now, filter_complete_responses upstream prevents the empty case.
A dict value can be another dict: {"Junior": {"count": 42, "avg": 3.87}}. Iterate d.items() to get (key, value) pairs:
for group, items in groups.items():
# group = 'Junior', items = [list of dicts]group_by_demographic → {group: [responses]}.items(), compute count and average per group{"count": N, "avg_satisfaction": X} as nested dictround(x, 2) vs :.2fround() returns a float (for computation). :.2f formats for display only. Use round() when storing data; :.2f when building output strings.
You need to produce the cross-tab section of your thesis — count and average satisfaction per demographic group. Write `demographic_summary(responses, field)` that calls `group_by_demographic` to bucket the responses, then computes `count` and `avg_satisfaction` for each group. Return a nested dict like `{'Junior': {'count': 3, 'avg_satisfaction': 3.87}}`.
Tap each step for scaffolded hints.
No blank-editor panic.