Hoofdstuk 13 — Introductie tot pandas: Series en DataFrame
Let op: De tekst is in het Nederlands; alle Python codevoorbeelden zijn in het Engels.
Doel van dit hoofdstuk:
- Begrijpen wat
pandasis en wanneer je het gebruikt ten opzichte van puur Python of NumPy. - Kennismaken met de twee kernobjecten:
SeriesenDataFrame. - Leren hoe je tabulaire data maakt, inspecteert, selecteert en bewerkt.
- Inzicht krijgen in indexen, uitlijning (alignment), dtypes en omgaan met missende waarden op basisniveau.
Aanbevolen voorkennis: Python‑basis, NumPy‑arrays (Hoofdstuk 6–11).
13.1 Wat is pandas en waarom?
pandas is een bibliotheek voor data‑analyse in Python, geoptimaliseerd voor gestructureerde (tabelvormige) data. Het biedt:
Series(1D gelabelde array) enDataFrame(2D tabel met rijen en kolommen).- Krachtige indexering, selectie, groepering, reshaping en I/O (CSV, Excel, Parquet, SQL).
- Vectorized operations met automatische uitlijning op labels (indexen).
Installatie (indien nodig):
pip install pandas
Basisimport:
import pandas as pd
import numpy as np
13.2 Series: een gelabelde 1D‑array
Een Series is vergelijkbaar met een 1D NumPy‑array, maar met labels (index) en rijkere functionaliteit.
13.2.1 Series maken
import pandas as pd
s = pd.Series([10, 20, 30], index=["a", "b", "c"], name="scores")
print(s)
# a 10
# b 20
# c 30
# Name: scores, dtype: int64
Belangrijke attributen: s.index, s.values, s.dtype, s.name.
Je kunt ook vanuit een dict maken (keys worden index):
data = {"apples": 3, "bananas": 5, "oranges": 2}
stock = pd.Series(data)
13.2.2 Selectie en slicing
s["a"] # label-based; returns scalar 10
s[["a", "c"]] # fancy indexing -> Series
s[0] # position-based; discouraged, gebruik .iloc
s.loc["a"] # label-based expliciet
s.iloc[0] # position-based expliciet
s["a":"c"] # label-slice inclusief eindlabel
s.iloc[0:2] # position-slice exclusief einde (zoals Python/NumPy)
13.2.3 Vectorbewerkingen en uitlijning
Operaties zijn elementgewijs en worden uitgelijnd op labels.
s1 = pd.Series([1, 2, 3], index=["a", "b", "c"])
s2 = pd.Series([10, 20, 30], index=["b", "c", "d"])
s1 + s2
# a NaN
# b 11.0
# c 23.0
# d NaN
# dtype: float64
Missende combinaties geven NaN. Gebruik fill_value met method chaining via .add e.d.:
(s1.add(s2, fill_value=0))
# a 1.0
# b 12.0
# c 33.0
# d 30.0
13.2.4 Basisstatistiek en methodes
s.mean(), s.median(), s.std()
s.min(), s.max()
s.isna(), s.notna()
s.fillna(0)
s.astype("float32")
13.3 DataFrame: een 2D tabel
Een DataFrame is een verzameling Series die kolommen vormen, met een gedeelde rij‑index.
13.3.1 DataFrame maken
Constructors vanuit dict, lijst van dicts, NumPy, of CSV/Parquet/…
import numpy as np
df = pd.DataFrame({
"name": ["Alice", "Bob", "Charlie"],
"age": [25, 32, 29],
"score": [88.5, 92.0, 79.0],
})
# Met aangepaste index
people = pd.DataFrame(
[[25, 88.5], [32, 92.0], [29, 79.0]],
index=["Alice", "Bob", "Charlie"],
columns=["age", "score"],
)
Veelgebruikte attributes en inspectie:
df.shape, df.dtypes, df.index, df.columns
df.head() # eerste 5 rijen
df.tail(3) # laatste 3 rijen
df.info() # kolomtypes, non-null counts
df.describe() # numerieke samenvatting
13.3.2 Selectie: kolommen en rijen
# Kolommen
ages = df["age"] # Series
subset = df[["name", "score"]] # DataFrame
# Rijen
first_row = df.iloc[0] # Series (row)
rows_1_3 = df.iloc[1:3] # DataFrame slice
row_bob = people.loc["Bob"] # via indexlabel
# Rijen + kolommen (2D selectie)
val = df.loc[0, "score"] # scalar
block = df.loc[0:1, ["name", "score"]]
Regels:
- Gebruik
.loc[rows, cols]voor labelgebaseerd,.iloc[rows, cols]voor positiegebaseerd. - Enkelvoudige kolomselectie
df["col"]geeft een Series; dubbele hakendf[["c1", "c2"]]geeft een DataFrame.
13.3.3 Filteren en booleaanse indexering
mask = (df["age"] >= 30) & (df["score"] > 90)
df[mask]
# Met loc, explicieter en uitbreidbaar
df.loc[(df["age"] >= 30) & (df["score"] > 90), ["name", "score"]]
13.3.4 Nieuwe kolommen en vectorbewerkingen
df["passed"] = df["score"] >= 85
# Vectorbewerkingen uitgelijnd op index
df["score_norm"] = (df["score"] - df["score"].mean()) / df["score"].std()
# Met NumPy ufuncs
df["age_log"] = np.log(df["age"]) # werkt elementgewijs
13.3.5 Dtypes en conversies
df.dtypes
# Conversie
df["age"] = df["age"].astype("int16")
df = df.astype({"score": "float32"})
# Categoricals
cats = pd.Series(["low", "med", "high"]).astype("category")
13.3.6 Missende waarden (NaN) basis
df2 = pd.DataFrame({"a": [1, np.nan, 3], "b": [np.nan, 5, 6]})
df2.isna() # booleaanse DataFrame
df2.dropna() # rijen met NaN weg
df2.fillna({"a": 0, "b": df2["b"].mean()}) # per kolom vulling
13.3.7 Sorteren en herindexeren
df.sort_values(by=["score", "age"], ascending=[False, True])
df.sort_index() # sorteer op rijindex
# Herindexeren
df_re = df.reindex([0, 1, 2, 3]) # voegt evt. NaN toe voor ontbrekende rijen
13.4 Indexfilosofie en uitlijning
- Indexen labelen rijen/kolommen en sturen selectie, join/merge, en alignment.
- Operaties tussen Series/DataFrames lijnen uit op indexlabels; ontbrekende labels →
NaN. - Reset/zet index waar nuttig:
df_reset = people.reset_index(names="person")
df_set = df_reset.set_index("person")
13.5 In‑/uitvoer (teaser)
Uitgebreide I/O volgt in Hoofdstuk 14. Kort voorbeeld:
# CSV lezen en schrijven
df = pd.read_csv("data.csv")
df.to_csv("out.csv", index=False)
# Parquet (vereist pyarrow of fastparquet)
# df.to_parquet("out.parquet")
13.6 Veelvoorkomende valkuilen en tips
- Gebruik
.locen.iloci.p.v.df[row][col](dat maakt onnodige kopieën en kan SettingWithCopyWarnings geven). - Let op automatische uitlijning; het is vaak gewenst, maar kan tot NaN leiden als labels niet overeenkomen.
- Werk zoveel mogelijk kolomgewijs (vectorized) in plaats van rij‑loops voor performance en duidelijkheid.
- Gebruik
df.info()endf.dtypesvroeg om types en missende waarden te begrijpen.
13.7 Samenvatting
Je leerde de basis van pandas met Series en DataFrame: maken, selecteren, filteren, berekenen, en omgaan met missende waarden. In de volgende hoofdstukken duiken we in I/O (CSV/Excel/Parquet/SQL), wrangling en groeperen/pivoteren.