Summarizing Data Records

Introduction

Congratulations and welcome to Practical Data Processing with Python Collections! Look at how far our toolkit has come: we tallied and grouped values with dictionaries, reshaped lists with comprehensions, and turned messy raw text into clean, typed records.

Every one of those steps was preparation. Records are not the goal; they are the raw material for answers. A manager looking at a list of sales does not want to read four dictionaries; they want to know the total, the average, and which item performed best and worst. That final translation, from structured data to a readable summary, is what this lesson is about.

By the end of this lesson, we will be able to:

  • Extract a single numeric field from a list of records into a flat list
  • Compute totals and averages with sum() and len()
  • Find the highest and lowest records, names included, using max() and min() with a key function
  • Print a tidy, currency-formatted report

The Records We Will Summarize

Let us begin with the data. Our example is a small list of sales transactions, where each record is a dictionary pairing a product name with the amount of money it brought in.

sales = [
    {"product": "Notebook", "amount": 45.00},
    {"product": "Pen", "amount": 12.50},
    {"product": "Folder", "amount": 30.00},
    {"product": "Marker", "amount": 18.75},
]

This is exactly the shape a parser produces: a list of records that all share the same keys. Two details make summarizing possible here:

  • Consistent keys: every record has both "product" and "amount", so we can safely ask each one for the same field without guarding against missing keys.
  • Real numbers: "amount" holds floats such as 45.00, not strings such as "45.00". Numbers can be added and compared; text cannot, at least not in the way we want.

Extracting a Numeric Column with a Comprehension

sum() needs an iterable of numeric values, not a list of dictionaries, so our first move is to pull the "amount" field out of every record into a flat list. That extraction is precisely what a comprehension does best.

amounts = [sale["amount"] for sale in sales]

As you may recall from the list comprehension tools we have already used, we read this as "for each sale in sales, give me sale["amount"]". The result is the flat list [45.0, 12.5, 30.0, 18.75], in the same order as the original records.

Two things are worth noticing here. First, 45.00 prints as 45.0 because Python stores floats without trailing zeros; we will fix the appearance later. Second, the comprehension builds a new list and leaves sales completely untouched, which matters because we still need those dictionaries when we want the product names.

It is also worth knowing that sum(), like most built-in tools that process a sequence of values, actually accepts any iterable, not only a list. A generator expression such as sum(sale["amount"] for sale in sales) would compute the very same total without ever building an intermediate list in memory. We build the amounts list here anyway because we are about to reuse those plain numbers again right away, first for the average and later in this lesson.

Totals and Averages with sum() and len()

Finding Extreme Records with max(), min(), and a Key Function

Now for a subtler question. We could call max(amounts) and get 45.0, but that number arrives orphaned: it no longer knows which product earned it. To keep the name attached, we ask for the biggest record instead.

highest = max(sales, key=lambda sale: sale["amount"])
lowest = min(sales, key=lambda sale: sale["amount"])

Here max() and min() iterate directly over the dictionaries in sales, and key tells them which value to compare. Two pieces of this line are new, so let us take them slowly.

The key argument answers a question max() cannot answer on its own: dictionaries have no natural order, so Python needs to be told what to measure each record by. Whatever we pass as key is a small function that Python calls once for each record, and the value it hands back is what gets compared. The record whose key value is largest is the one max() returns, whole and unchanged.

The lambda is how we write that small function right where we need it, without giving it a name. Reading lambda sale: sale["amount"] left to right: lambda announces a tiny function, sale is the name it gives to the one record it receives, and sale["amount"] is the value it hands back. In plain words: "given one sale, look at its amount." Nothing else in this course asks us to write functions; this one-line form is all key needs. The table below shows what it produces for each record:

RecordKey valueResult
{"product": "Notebook", ...}45.0winner for max
{"product": "Pen", ...}12.5winner for min
{"product": "Folder", ...}30.0
{"product": "Marker", ...}18.75

As the table shows, highest becomes the whole Notebook dictionary and lowest becomes the whole Pen dictionary, so both the amount and the product name remain within reach.

Formatting Numbers for a Readable Report

Assembling and Reading the Full Report

Common Pitfalls

Aggregation code fails in a handful of predictable ways, and recognizing them early saves real debugging time.

  • Omitting key: max(sales) tries to compare whole dictionaries against each other and raises a TypeError.
  • Empty record lists: total / len(amounts) raises a ZeroDivisionError when there are no records, so check the length first.
  • Untyped values: if a parser left amounts as strings, sum() raises a TypeError; conversion belongs upstream, as we saw in the last lesson.
  • Misreading :.2f: it formats the printed text only, so a value rounded for display is still unrounded in memory.
  • Quote collisions: writing {highest["amount"]} inside a double-quoted f-string closes the string too soon; use single quotes for the key.

The typed-values pitfall is the most common of all because it links parsing and aggregation: parsing quality directly determines whether aggregation works.

Conclusion and Next Steps

We just built a complete summarizing pipeline in four moves: extract a numeric column from the records with a comprehension, aggregate it into a total and an average with sum() and len(), locate the extreme records with max() and min() plus a key function so the names travel along with the numbers, and format everything into lines a person can actually read. This pattern scales without changes: swap "amount" for any numeric field, and the same six lines report on temperatures, scores, or response times.

The practices ahead follow that exact path: pulling a field out of records and totaling it, computing the average, finding the top and bottom records with a key function, and finally assembling the multi-line report yourself. Together, these skills take messy raw text all the way to a polished summary: counting, grouping, filtering, parsing, and aggregating. Nicely done, and let us put that reporting pattern into practice!

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal