Tables as text
A CSV (comma-separated values) file stores a table as text: one row per line, cells separated by commas, usually with a header row naming the columns. Price histories, trade blotters and exports from almost any tool come this way. It looks easy to parse with line.split(","), but a cell may itself contain a comma, in which case it is wrapped in quotes: AAPL,"Q3, adjusted". Splitting by hand breaks on that, so use the standard csv module, and open the file with newline="" so it handles line breaks inside cells correctly.
import csv
# Write a small table with csv.writer
with open("prices.csv", "w", newline="", encoding="utf-8") as file:
writer = csv.writer(file)
writer.writerow(["date", "symbol", "close", "note"])
writer.writerow(["2025-01-02", "AAPL", 184.25, "Q3, adjusted"])
writer.writerow(["2025-01-03", "AAPL", 185.5, ""])
# The comma inside the note is quoted for us
with open("prices.csv", encoding="utf-8") as file:
print(file.read().splitlines()[1]) # 2025-01-02,AAPL,184.25,"Q3, adjusted"Reading rows
csv.reader(file) yields each row as a list of strings. csv.DictReader(file) reads the header first and then yields each row as a dictionary keyed by column name, which is easier to read and survives columns being reordered. Either way every cell is text: convert with float(...) or int(...) before doing arithmetic. Pass delimiter=";" (or "\t") for files that use another separator.
import csv
# DictReader gives each row as a dict; cells are strings
closes = []
with open("prices.csv", newline="", encoding="utf-8") as file:
for row in csv.DictReader(file):
closes.append(float(row["close"]))
print(closes) # [184.25, 185.5]
# csv.reader gives plain lists, header included
with open("prices.csv", newline="", encoding="utf-8") as file:
rows = list(csv.reader(file))
print(rows[0]) # ['date', 'symbol', 'close', 'note']
print(rows[1]) # ['2025-01-02', 'AAPL', '184.25', 'Q3, adjusted']Writing rows
csv.writer(file).writerow(values) writes one row and quotes cells when needed; numbers are converted to text for you. csv.DictWriter(file, fieldnames=[...]) writes dictionaries, and writeheader() writes the column names first. Open the file with "w" and newline=""; without newline="" Windows can end up with blank lines between rows.
import csv
# DictWriter: header first, then one dict per row
trades = [{"symbol": "AAPL", "quantity": 200}, {"symbol": "MSFT", "quantity": 50}]
with open("trades.csv", "w", newline="", encoding="utf-8") as file:
writer = csv.DictWriter(file, fieldnames=["symbol", "quantity"])
writer.writeheader()
writer.writerows(trades)
with open("trades.csv", encoding="utf-8") as file:
print(file.read().splitlines()) # ['symbol,quantity', 'AAPL,200', 'MSFT,50']import csv
# Cells are text until you convert them
with open("prices.csv", newline="", encoding="utf-8") as file:
row = next(csv.DictReader(file))
print(repr(row["close"]), row["close"] * 2) # '184.25' 184.25184.25
print(float(row["close"]) * 2) # 368.5
# Another separator: semicolons
with open("euro.csv", "w", newline="", encoding="utf-8") as file:
csv.writer(file, delimiter=";").writerow(["AAPL", "184,25"])
with open("euro.csv", newline="", encoding="utf-8") as file:
print(next(csv.reader(file, delimiter=";"))) # ['AAPL', '184,25']184.25, 200 and AAPL are all just characters until you convert them. Forgetting that gives "184.25" * 200 (the text repeated 200 times) instead of an error. Convert each column once, right where you read it, and check the header names rather than assuming positions.- Open with
newline=""; do not split or join CSV lines by hand. csv.DictReaderreads rows as dicts; every cell is a string.csv.writer/csv.DictWriterwrite rows and quote cells for you.
Next up: JSON data, for nested records that do not fit a flat table.
Lab · what's graded
- Group closing prices by symbol with
read_closes25 pts - Save trades as a CSV file with
write_trades25 pts - Total the traded value per symbol with
total_by_symbol25 pts - Copy one symbol's rows to a new file with
filter_rows25 pts