Skip to content

07-02: Working with CSV Files

CSV (Comma-Separated Values) is the most common format for tabular data — spreadsheets, exports from databases, and datasets. Python's built-in csv module handles it cleanly, and Pandas makes complex operations trivial.


What is a CSV file?

name,age,city,score
Alice,30,New York,88.5
Bob,25,London,92.0
Charlie,35,Tokyo,79.3
  • First row is usually a header
  • Each row is one record
  • Fields are separated by a delimiter (usually , but can be ;, \t, |)
  • Text fields with commas inside are quoted: "Smith, John"

Reading CSV — Built-in csv Module

Basic reader

import csv

with open("students.csv", "rt", encoding="utf-8", newline="") as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)
        # ['Alice', '30', 'New York', '88.5']

# Note: always pass newline="" to open() with csv to handle \r\n correctly

Skip header row

import csv

with open("students.csv", "rt", encoding="utf-8", newline="") as f:
    reader = csv.reader(f)
    header = next(reader)          # consume the first row
    print("Columns:", header)

    for row in reader:
        name, age, city, score = row
        print(f"{name} (age {age}) from {city}, score: {score}")

DictReader — rows as dictionaries

import csv

with open("students.csv", "rt", encoding="utf-8", newline="") as f:
    reader = csv.DictReader(f)
    for row in reader:
        # row is an OrderedDict/dict keyed by header names
        print(row["name"], row["score"])

# Load all rows into a list
with open("students.csv", "rt", encoding="utf-8", newline="") as f:
    records = list(csv.DictReader(f))

print(records[0])   # {'name': 'Alice', 'age': '30', ...}

Convert types (CSV values are always strings)

import csv

with open("students.csv", "rt", encoding="utf-8", newline="") as f:
    reader = csv.DictReader(f)
    students = []
    for row in reader:
        students.append({
            "name":  row["name"],
            "age":   int(row["age"]),
            "city":  row["city"],
            "score": float(row["score"]),
        })

# Now work with proper types
ages = [s["age"] for s in students]
print(f"Average age: {sum(ages) / len(ages):.1f}")

high_scorers = [s["name"] for s in students if s["score"] >= 85]
print("High scorers:", high_scorers)

Writing CSV

Basic writer

import csv

header = ["name", "age", "city", "score"]
data = [
    ["Alice",   30, "New York", 88.5],
    ["Bob",     25, "London",   92.0],
    ["Charlie", 35, "Tokyo",    79.3],
]

with open("output.csv", "wt", encoding="utf-8", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(header)    # write header
    writer.writerows(data)     # write all rows at once

DictWriter — write from dicts

import csv

students = [
    {"name": "Alice",   "age": 30, "score": 88.5},
    {"name": "Bob",     "age": 25, "score": 92.0},
    {"name": "Charlie", "age": 35, "score": 79.3},
]

fields = ["name", "age", "score"]

with open("output.csv", "wt", encoding="utf-8", newline="") as f:
    writer = csv.DictWriter(f, fieldnames=fields)
    writer.writeheader()        # write header row automatically
    writer.writerows(students)

Appending rows

import csv

new_student = ["Diana", 28, "Paris", 95.5]

with open("students.csv", "at", encoding="utf-8", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(new_student)

Custom Dialects and Delimiters

import csv

# Tab-separated (TSV)
with open("data.tsv", "rt", encoding="utf-8", newline="") as f:
    reader = csv.reader(f, delimiter="\t")
    for row in reader:
        print(row)

# Semicolon-separated (common in European locales)
with open("data.csv", "rt", encoding="utf-8", newline="") as f:
    reader = csv.reader(f, delimiter=";")
    for row in reader:
        print(row)

# Custom quote character
with open("data.csv", "rt", encoding="utf-8", newline="") as f:
    reader = csv.reader(f, quotechar="'")
    for row in reader:
        print(row)

# Write with pipe delimiter
with open("out.csv", "wt", newline="") as f:
    writer = csv.writer(f, delimiter="|")
    writer.writerow(["a", "b", "c"])

Handling Edge Cases

Fields that contain commas or quotes

# csv module handles this automatically with quoting
import csv

data = [
    ["Alice Smith", 30, "New York, NY"],   # city has a comma
    ['Bob "The Rock" Jones', 25, "LA"],    # name has quotes
]

with open("tricky.csv", "wt", newline="") as f:
    writer = csv.writer(f)
    writer.writerows(data)

# File will look like:
# Alice Smith,30,"New York, NY"
# "Bob ""The Rock"" Jones",25,LA

Missing values

import csv

with open("data.csv", "rt", newline="") as f:
    reader = csv.DictReader(f)
    for row in reader:
        score = row.get("score", "N/A")
        age = int(row["age"]) if row["age"] else None
        print(f"{row['name']}: score={score}, age={age}")

Large files — process line by line

import csv

total = 0
count = 0

with open("large_data.csv", "rt", encoding="utf-8", newline="") as f:
    reader = csv.DictReader(f)
    for row in reader:
        total += float(row["value"])
        count += 1

print(f"Sum: {total:.2f}, Count: {count}")
# Memory efficient — reads one row at a time, doesn't load whole file

import pandas as pd

# Read CSV into a DataFrame
df = pd.read_csv("students.csv")
print(df)
print(df.dtypes)

# Pandas infers types automatically
# Read with specific options
df = pd.read_csv(
    "students.csv",
    sep=",",               # delimiter
    encoding="utf-8",
    header=0,              # row 0 is the header
    index_col=None,        # don't use any column as index
    usecols=["name", "score"],  # only load these columns
    dtype={"age": int, "score": float},
    na_values=["N/A", ""],     # treat these as NaN
    skiprows=0,
    nrows=100,             # read only first 100 rows
)

# Write DataFrame to CSV
df.to_csv("output.csv", index=False)   # index=False: don't write row numbers
df.to_csv("output.csv", index=False, float_format="%.2f")

# Quick analysis
print(df.head())           # first 5 rows
print(df.describe())       # statistics
print(df["score"].mean())  # column mean

Practical Examples

Filter and save

import csv

# Read, filter rows where score >= 80, save to new file
with open("students.csv", "rt", newline="") as infile, \
     open("high_scorers.csv", "wt", newline="") as outfile:

    reader = csv.DictReader(infile)
    writer = csv.DictWriter(outfile, fieldnames=reader.fieldnames)
    writer.writeheader()

    for row in reader:
        if float(row["score"]) >= 80:
            writer.writerow(row)

Aggregate by group

import csv
from collections import defaultdict

# Average score by city
city_scores = defaultdict(list)

with open("students.csv", "rt", newline="") as f:
    reader = csv.DictReader(f)
    for row in reader:
        city_scores[row["city"]].append(float(row["score"]))

for city, scores in city_scores.items():
    avg = sum(scores) / len(scores)
    print(f"{city}: {avg:.1f} (n={len(scores)})")

CSV to list of dicts (utility function)

import csv

def read_csv(path, delimiter=",", encoding="utf-8"):
    """Read CSV and return list of dicts with auto type conversion."""
    with open(path, "rt", encoding=encoding, newline="") as f:
        return list(csv.DictReader(f, delimiter=delimiter))

def write_csv(path, records, fieldnames=None, encoding="utf-8"):
    """Write list of dicts to CSV."""
    if not records:
        return
    fieldnames = fieldnames or list(records[0].keys())
    with open(path, "wt", encoding=encoding, newline="") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)
        writer.writeheader()
        writer.writerows(records)

students = read_csv("students.csv")
high = [s for s in students if float(s["score"]) >= 80]
write_csv("high_scorers.csv", high)

Quick Summary

Task Code
Read CSV rows as lists csv.reader(f)
Read CSV rows as dicts csv.DictReader(f)
Write list of lists csv.writer(f) → writerows()
Write list of dicts csv.DictWriter(f, fieldnames) → writerows()
Custom delimiter csv.reader(f, delimiter="\t")
With Pandas pd.read_csv("file.csv")
Always use newline="" in open() for CSV

Exercises: 07-02: Exercises — CSV Files


⬅️ Previous: 07-01: File I/O — Reading and Writing Files ➡️ Next: 07-03: Working with JSON Files