Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Hoofdstuk 15 — Data schoonmaken en wrangling: missing values, typen en categorieën

Let op: De tekst is in het Nederlands; alle Python codevoorbeelden zijn in het Engels.

Doel van dit hoofdstuk:

  • Systematisch omgaan met missende waarden: detecteren, filteren, invullen en imputeren.
  • Datatypes begrijpen en beheren: numeriek, string, boolean, datetime, timedelta en categorisch.
  • Tekstkolommen opschonen en normaliseren; duplicates en outliers aanpakken.
  • Praktische wrangling‑patronen toepassen met method‑chaining (intro) voor leesbare pipelines.

Aanbevolen voorkennis: Hoofdstuk 13–14 (pandas basis en I/O).

15.1 Missende waarden herkennen en analyseren

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'id': [1, 2, 3, 4],
    'age': [25, None, 31, 28],
    'score': [88.0, 92.5, None, 86.0],
    'city': ['Amsterdam', 'Rotterdam', None, 'Utrecht']
})

# Detectie
mask_na = df.isna()
count_na_per_col = df.isna().sum()
rows_with_any_na = df[df.isna().any(axis=1)]

# Verwijderen
drop_any = df.dropna()             # rijen met een NaN weg
subset_req = df.dropna(subset=['age', 'score'])

Wanneer verwijderen, wanneer vullen?

  • Verwijderen als de missings zeldzaam zijn en willekeurig verspreid (MCAR).
  • Vullen/imputeren als informatieverlies te groot is of de kolom essentieel is.

15.2 Missende waarden vullen (fillna) en eenvoudige imputatie

# Eenvoudig vullen per kolom
filled = df.fillna({
    'age': df['age'].median(),
    'score': df['score'].mean(),
    'city': 'Onbekend'
})

# Forward/backward fill op tijdreeksen of gesorteerde data
filled_ffill = df.sort_values('id').fillna(method='ffill')
filled_bfill = df.sort_values('id').fillna(method='bfill')

# Alleen numeriek vullen
num_cols = df.select_dtypes(include='number').columns
df[num_cols] = df[num_cols].fillna(df[num_cols].median())

Let op: Eenvoudige invulling kan statistieken vertekenen; documenteer keuzes en overweeg geavanceerde imputatie in ML‑pipelines (zie Deel VI).

15.3 Dtypes beheren en converteren

# Inspectie
print(df.dtypes)

# Numeriek
df['age'] = df['age'].astype('float32')

# String (pandas 1.0+ string dtype)
df['city'] = df['city'].astype('string')

# Boolean met NA‑ondersteuning
s = pd.Series([True, False, None], dtype='boolean')

# Datetime en timedelta
sales = pd.DataFrame({'date': ['2025-01-01','2025-02-03'], 'days': [10, 20]})
sales['date'] = pd.to_datetime(sales['date'], format='%Y-%m-%d')
sales['deadline'] = sales['date'] + pd.to_timedelta(sales['days'], unit='D')

15.4 Categorical dtypes: geheugen en performance

Categorische kolommen slaan herhalende labels efficiënt op en versnellen sommige operaties (groupby/sort).

sizes = pd.Series(['S','M','M','L','S','XL','M'])
sizes_cat = sizes.astype('category')

# Orde instellen
sizes_ord = pd.Categorical(sizes, categories=['S','M','L','XL'], ordered=True)

# Toepassing: binned levels
import numpy as np
scores = pd.Series([72, 88, 91, 67, 85])
bins = [0, 80, 90, 100]
labels = ['low', 'med', 'high']
levels = pd.cut(scores, bins=bins, labels=labels).astype('category')

15.5 Tekstopschoning en normalisatie

Veel real‑world datasets bevatten rommelige strings.

products = pd.Series(['  Apple iPhone  ', 'SAMSUNG Galaxy', 'xiaomi-REDMI', None], dtype='string')

clean = (products
         .str.strip()
         .str.replace('-', ' ', regex=False)
         .str.title()
         .str.replace('Iphone', 'iPhone', regex=False)
        )

# Splitsen en extractie met regex
emails = pd.Series(['alice@example.com','bob@test.org'], dtype='string')
usernames = emails.str.extract(r'^(?P<user>[^@]+)@(?P<domain>.+)$')

15.6 Duplicaten en outliers

# Duplicaten
unique_df = df.drop_duplicates()
# Duplicaten op subset
unique_city = df.drop_duplicates(subset=['city'])

# Eenvoudige outlier detectie met z‑score
import numpy as np
vals = pd.Series([10, 11, 10.5, 200, 9.8, 10.1])
z = (vals - vals.mean()) / vals.std()
outliers = vals[abs(z) > 3]

15.7 Wrangling‑patronen met method‑chaining

# Voorbeeld pipeline: kolommen selecteren -> missings vullen -> nieuwe variabelen -> filter -> sort
result = (
    df
    .assign(age_filled=lambda d: d['age'].fillna(d['age'].median()))
    .assign(score_z=lambda d: (d['score'] - d['score'].mean()) / d['score'].std())
    .dropna(subset=['city'])
    .query('age_filled >= 18')
    .sort_values(['score_z'], ascending=False)
)

Tip: Gebruik tussenstappen met .pipe(func, ...) om grotere ketens te structureren (zie Hoofdstuk 19 voor meer over pipe).

15.8 Samenvatting

  • Herken en behandel missende waarden met isna, dropna, fillna, en forward/backward fills.
  • Beheer dtypes expliciet: numeriek, string, boolean, datetime/timedelta, en category voor efficiënte labels.
  • Reinig tekstkolommen met .str‑methodes; verwijder duplicaten en signaleer outliers.
  • Combineer stappen in leesbare wrangling‑pipelines met method‑chaining.