Hoofdstuk 14 — Data inlezen en schrijven: CSV, Excel, Parquet en SQL
Let op: De tekst is in het Nederlands; alle Python codevoorbeelden zijn in het Engels.
Doel van dit hoofdstuk:
- Data inlezen uit en wegschrijven naar veelgebruikte formaten: CSV, Excel, Parquet en SQL.
- Leren welke parameters belangrijk zijn voor correcte parsing (scheidingsteken, encoding, datums, dtypes, missende waarden).
- Begrijpen van voor- en nadelen per formaat en performance-/geheugentips.
Aanbevolen voorkennis: Hoofdstuk 13 (pandas‑basis), Python/NumPy.
14.1 CSV: de klassieker
CSV is een tekstformaat met kolommen gescheiden door een delimiter (vaak , of ;). Het is universeel, maar mist schema en types.
14.1.1 Lezen met pd.read_csv
import pandas as pd
# Basic
df = pd.read_csv("data.csv")
# Veelgebruikte opties
# - sep: scheidingsteken (bijv. ';' in Nederlandse exporten)
# - encoding: bronencoding (bijv. 'utf-8', 'latin-1')
# - header/names: eerste rij als header of kolomnamen expliciet aangeven
# - usecols: subset van kolommen lezen
# - dtype: kolomtypes forceren
# - parse_dates: kolommen als datums parsen
# - na_values: extra strings als NaN behandelen
# - on_bad_lines: 'error' (default), 'warn' of 'skip'
# - engine: 'c' (snel), 'python' (toleranter)
# - nrows/skiprows: subset van rijen
# - compression: 'gzip', 'zip', 'bz2', ...
sales = pd.read_csv(
"sales_2024.csv",
sep=';',
encoding='utf-8',
usecols=["date", "product", "qty", "price"],
dtype={"product": "string", "qty": "int32", "price": "float32"},
parse_dates=["date"],
na_values=["", "NA", "NULL", "-"],
)
Datum parsing tips:
parse_dates=["date"]laat pandas zelf detecteren. Voor exotische formaten kun jedayfirst=Trueofdate_format(nieuwe pandas versies) gebruiken.- Grote snelheidswinst is mogelijk met
date_format="%Y-%m-%d"indien constant formaat.
Grote bestanden:
# Streamen met chunksize om geheugen te sparen
it = pd.read_csv("big.csv", chunksize=100_000)
total_qty = 0
for chunk in it:
total_qty += chunk["qty"].sum()
14.1.2 Schrijven met DataFrame.to_csv
# Schrijf zonder indexkolom, met semicolon delimiter en UTF-8
sales.to_csv("sales_out.csv", index=False, sep=';', encoding='utf-8')
# Gecomprimeerd
sales.to_csv("sales_out.csv.gz", index=False, compression='gzip')
Valkuilen:
- Locale notaties (decimalen met
,en duizendtallen met.); gebruikdecimal=','enthousands='.'bij het lezen. - Automatische type-inferentie kan strings/ID’s als numeriek zien; forceer
dtype. - CSV heeft geen schema → verlies van types bij round-trips mogelijk.
14.2 Excel: werkbladen en tabbladen
Excel-bestanden zijn binair (XLSX). Je hebt meestal een extra engine nodig.
Installatie:
pip install openpyxl # voor .xlsx lezen/schrijven
14.2.1 Lezen met pd.read_excel
# Specifieke sheet, kolommen en dtype
orders = pd.read_excel(
"orders.xlsx",
sheet_name="2024-Q1",
usecols=["order_id", "date", "amount"],
dtype={"order_id": "string", "amount": "float32"},
parse_dates=["date"],
engine="openpyxl",
)
# Meerdere sheets in één keer → dict van DataFrames
all_sheets = pd.read_excel("orders.xlsx", sheet_name=None, engine="openpyxl")
14.2.2 Schrijven met DataFrame.to_excel
# Eén sheet
orders.to_excel("orders_out.xlsx", index=False, sheet_name="Q1", engine="openpyxl")
# Meerdere sheets
with pd.ExcelWriter("orders_multi.xlsx", engine="openpyxl") as xw:
orders.to_excel(xw, index=False, sheet_name="Q1")
orders.sample(100).to_excel(xw, index=False, sheet_name="Sample")
Valkuilen:
- Excel kan cellen als tekst/numeriek wisselend formatteren; dwing
dtypein pandas. - Grootte en performance minder efficiënt dan kolom-georiënteerde formaten.
14.3 Parquet: kolom-georiënteerd, snel en gecomprimeerd
Parquet is een kolomarig, gecomprimeerd, schematisch formaat. Ideaal voor analytische workloads.
Installatie:
pip install pyarrow # aanbevolen
# of
pip install fastparquet
14.3.1 Lezen met pd.read_parquet
import pandas as pd
df = pd.read_parquet("dataset.parquet")
# Alleen bepaalde kolommen
small = pd.read_parquet("dataset.parquet", columns=["date", "amount"])
14.3.2 Schrijven met DataFrame.to_parquet
# Compressie en engine instellen
orders.to_parquet("orders.parquet", engine="pyarrow", compression="snappy", index=False)
14.3.3 Partitionering en mappen
Veel data‑engineering workflows schrijven partities per sleutel (bijv. jaar/maand) als directorystructuur:
/transactions/
year=2024/
month=01/
part-0000.parquet
month=02/
part-0000.parquet
year=2025/
month=01/
Lezen met partitiekolommen (pyarrow nodig):
# Vereist pyarrow.dataset onder water
# df = pd.read_parquet("transactions/", engine="pyarrow")
Voordelen Parquet:
- Snellere I/O, kolomselectie, compressie (snappy/zstd/gzip), behoud van dtypes en datums. Nadelen:
- Vereist extra dependency; minder universeel deelbaar dan CSV.
14.4 SQL: relationele databases
pandas kan lezen/schrijven via SQLAlchemy of DB-API connecties naar o.a. SQLite, PostgreSQL, MySQL.
Installatie voorbeeld (SQLite inbegrepen bij Python):
pip install sqlalchemy
14.4.1 Verbinden (SQLAlchemy engine)
from sqlalchemy import create_engine
engine = create_engine("sqlite:///example.db")
14.4.2 Lezen: pd.read_sql, read_sql_query, read_sql_table
import pandas as pd
# Volledige tabel
users = pd.read_sql_table("users", con=engine)
# Query (met parameter binding voor veiligheid)
query = "SELECT * FROM users WHERE country = :country AND age >= :min_age"
users_de = pd.read_sql_query(query, con=engine, params={"country": "DE", "min_age": 18})
14.4.3 Schrijven: DataFrame.to_sql
# Maak een DataFrame en schrijf naar SQL
import pandas as pd
new_users = pd.DataFrame({
"user_id": [1, 2, 3],
"name": ["Alice", "Bob", "Charlie"],
"age": [25, 32, 29],
"country": ["NL", "DE", "BE"],
})
# if_exists: 'fail' (default), 'replace', 'append'
new_users.to_sql("users", con=engine, if_exists="append", index=False)
Prestatietips:
- Gebruik
chunksizebij to_sql en read_sql voor grote datasets. - Stel
dtypeexpliciet in bij to_sql voor kolomtypes (SQLAlchemy types).
Veiligheid:
- Gebruik nooit string‑concatenatie voor parameters in SQL; gebruik
params/bound parameters.
14.5 Encodings, NA’s, en locale issues
- Encodings: probeer altijd UTF‑8; als het niet lukt, test
latin-1of bron‑encoding van de export. - Missende waarden: specificeer
na_values(CSV), controleerna_filter, en pasfillna/dropnatoe. - Decimalen en duizendtallen:
decimal=',',thousands='.'bij lezen. - Dtypes: voorkom ongewenste object‑kolommen; gebruik
dtypeof achterafastype.
14.6 Performance en geheugen
- Gebruik kolomsubset (
usecolsvoor CSV/Excel,columnsvoor Parquet) om minder te lezen. - Voor zeer grote CSV’s: overweeg conversie naar Parquet voor sneller hergebruik.
- Werk met
chunksizeen verwerk streamend. - Let op
low_memorybij read_csv: standaard True kan type‑inconsistenties geven; zet uit en specificeerdtypeals mogelijk.
14.7 Mini‑workflow: CSV → schoonmaken → Parquet → SQL
import pandas as pd
from sqlalchemy import create_engine
# 1) Lees CSV met opties
raw = pd.read_csv(
"transactions.csv",
sep=';',
parse_dates=["date"],
dtype={"product_id": "string", "qty": "int32", "price": "float32"},
na_values=["", "NA", "NULL"]
)
# 2) Eenvoudige schoonmaak
raw = raw.dropna(subset=["product_id", "qty"]) # vereiste velden
raw["amount"] = raw["qty"] * raw["price"]
# 3) Schrijf naar Parquet voor snel hergebruik
raw.to_parquet("transactions.parquet", compression="snappy", index=False)
# 4) Laad subset kolommen en schrijf naar SQL
subset = pd.read_parquet("transactions.parquet", columns=["date", "product_id", "amount"])
engine = create_engine("sqlite:///analytics.db")
subset.to_sql("transactions", con=engine, if_exists="replace", index=False)
14.8 Samenvatting
Je leerde:
- CSV/Excel lezen en schrijven met cruciale parameters voor delimiter, encoding, dtypes en datums.
- Parquet gebruiken voor performance, compressie en schema‑behoud.
- SQL‑bronnen aan te spreken via SQLAlchemy met veilige parameterbinding en
to_sql/read_sql. Met deze I/O‑basis kun je betrouwbare data‑pijplijnen opzetten en datasets efficiënt hergebruiken.