CSV: Spreadsheet Data as Text
Every bank, shop and spreadsheet program can export a CSV file: rows of values separated by commas, with a first row that names the columns. CSV stands for comma-separated values, and it is the most common way data leaves one program and enters another. Python's csv module reads it straight into the list of dictionaries from Chapter 6, and your own bank statement becomes data a program can answer questions about.
Think of a spreadsheet with its grid lines rubbed out. The commas are where the lines used to be, and each line of text is a row.
What a CSV Is
A CSV file is plain text. Open one in a text editor, not a spreadsheet, and you see exactly this. Save it as statement.csv in your python-course folder: a month of spending, as a bank might export it, with spending as negative amounts and income as positive.
date,description,amount,category 2026-09-01,Grocery store,-54.30,food 2026-09-02,Monthly bus pass,-45.00,transport 2026-09-03,Coffee,-4.20,food 2026-09-05,Salary,2400.00,income 2026-09-07,Electricity bill,-61.75,home 2026-09-09,Cinema,-12.50,fun
Why Not Just Split on Commas?
It is tempting to read each line and split it on commas. That works until a value contains a comma of its own. A CSV writer handles that by putting the value in double quotes, and a plain split cuts straight through the quotes. The line below should give four values; splitting it gives five, with the description torn in half.
>>> line = '2026-09-12,"Dinner, drinks",-38.00,fun'
>>> line.split(",")
['2026-09-12', '"Dinner', ' drinks"', '-38.00', 'fun']
>>> len(line.split(","))
5
The csv module understands quoting, and every other corner of the format, so there is no reason to write your own.
csv.DictReader
csv.DictReader reads a CSV file and hands you each row as a dictionary. The keys come from the header row, so row["amount"] is the amount of that row, whatever column it is in. The header row itself is used for the keys and is not returned as data. One detail matters: open the file with newline="", which lets the csv module handle line endings itself, as its documentation asks.
import csv with open("statement.csv", encoding="utf-8", newline="") as f: for row in csv.DictReader(f): print(row) break
{'date': '2026-09-01', 'description': 'Grocery store', 'amount': '-54.30', 'category': 'food'}
Everything Is Text
Look closely at that output: the amount is '-54.30', in quotes. Every value in a CSV arrives as text, exactly like the answer from input in Chapter 2. To do arithmetic, convert it. It is money, so Decimal(row["amount"]) makes it an exact Decimal straight from the text, which is precisely the way Chapter 2 recommended.
The Statement, Totalled
Now the whole job. The program reads every row, skips income, and for spending adds the amount to the overall total and to its category's total. Amounts in the file are negative for spending, so the program flips the sign with a minus to count money spent as a positive number. The answers match Chapters 4 and 6: 177.75 spent in total, 58.50 of it on food.
import csv from decimal import Decimal spent = Decimal("0") by_category = {} with open("statement.csv", encoding="utf-8", newline="") as f: for row in csv.DictReader(f): amount = Decimal(row["amount"]) if amount >= 0: continue cost = -amount spent = spent + cost category = row["category"] by_category[category] = by_category.get(category, Decimal("0")) + cost print(f"Spent in total: {spent}") for category, total in by_category.items(): print(f" {category}: {total}")
Spent in total: 177.75 food: 58.50 transport: 45.00 home: 61.75 fun: 12.50
Two tools from earlier chapters do the heavy lifting here. continue skips the rest of the round for income rows. And by_category.get(category, Decimal("0")) returns the category's running total, or zero the first time the category appears, which replaces the if ... not in check from Chapter 6 with a single line.
- "Numbers in a CSV arrive as numbers." Every field arrives as text. Convert it: int, float, or Decimal for money.
- "Splitting each line on commas is good enough." It breaks the first time a value contains a comma. The csv module handles quoting correctly.
- "The header row is the first row of data." DictReader uses it for the keys and does not return it as data.
- CSV is how data moves between spreadsheets, banks, shops and programs. With this page you can read almost all of it.
- Your own bank export is now something you can ask questions of in a dozen lines.
Knowledge Check
What does csv.DictReader do with the header row?
- Returns it as the first row of data, like any other
- Uses it as the keys for every row's dictionary
- Ignores it completely and numbers the columns instead
- Checks that every row has exactly the same values
What type is row["amount"] when read by DictReader?
- A Decimal, because the value looks like money
- A float, because the text contains a decimal point
- A str, like every value read from a CSV
- An int, because the amount is a whole number of cents
Why does line.split(",") go wrong on 2026-09-12,"Dinner, drinks",-38.00,fun?
- Split cannot handle text that contains a minus sign
- CSV files must be read with open, not split
- It also cuts at the comma inside the quoted description
- The line is too long for split to process in one go
Why does the statement program write cost = -amount?
- Spending is negative in the file, and it counts spending as positive
- Because Decimal cannot add negative numbers together
- To remove the income rows from the calculation
- Because the file stores its amounts in the wrong order
You got correct