Wanneer je data per categorie wilt samenvatten โ bijv. gemiddelde fooi per dag, totale verkoop per product โ gebruik je groupby. Deze notebook behandelt:
.groupby()met aggregatiefuncties.apply()met aangepaste functies en lambdaโs.map()voor waarde-mappingSamenvoegen van DataFrames met
pd.concat()enpd.merge()
import numpy as np
import pandas as pd
import seaborn as sns
tips = sns.load_dataset("tips")
tips.head().groupby() โ Groeperen en aggregerenยถ
De groupby werkt in drie stappen: split โ apply โ combine.
Verdeel het DataFrame in groepen op basis van een kolom
Pas een aggregatiefunctie toe op elke groep
Combineer de resultaten tot รฉรฉn DataFrame
# Mean bill and tip per day
tips.groupby("day", observed=True)[["total_bill", "tip"]].mean().round(2)# Multiple aggregations at once with .agg()
tips.groupby("day", observed=True)["tip"].agg(["mean", "median", "std", "count"]).round(2)# Different aggregations per column
tips.groupby("sex", observed=True).agg(
mean_bill=("total_bill", "mean"),
total_tip=("tip", "sum"),
visit_count=("total_bill", "count"),
).round(2)# Group by multiple columns
tips.groupby(["sex", "smoker"], observed=True)["tip"].mean().round(2)sex smoker
Male Yes 3.05
No 3.11
Female Yes 2.93
No 2.77
Name: tip, dtype: float64# reset_index() converts group labels back to regular columns
result = tips.groupby("day", observed=True)["tip"].mean().reset_index()
result.columns = ["day", "mean_tip"]
result.apply() โ Aangepaste functiesยถ
Wanneer een standaard aggregatie niet volstaat, gebruik je .apply() met een zelfgeschreven functie of een lambda.
# Add a new column: tip as a percentage of the bill
tips["tip_pct"] = tips["tip"] / tips["total_bill"] * 100
tips[["total_bill", "tip", "tip_pct"]].head()# .apply() with a named function
def categorize_tip(pct):
"""Classify a tip percentage as low, normal or generous."""
if pct < 10:
return "low"
elif pct < 20:
return "normal"
else:
return "generous"
tips["tip_category"] = tips["tip_pct"].apply(categorize_tip)
tips["tip_category"].value_counts()tip_category
normal 178
generous 39
low 27
Name: count, dtype: int64# Lambda inline: more compact for simple transformations
tips["bill_rounded"] = tips["total_bill"].apply(lambda x: round(x / 5) * 5)
tips[["total_bill", "bill_rounded"]].head().map() โ Waarden omzetten via een dictionaryยถ
# Map abbreviated day names to full English names
day_mapping = {"Thur": "Thursday", "Fri": "Friday", "Sat": "Saturday", "Sun": "Sunday"}
tips["day_name"] = tips["day"].map(day_mapping)
tips[["day", "day_name"]].drop_duplicates()# Stack two DataFrames vertically
df1 = pd.DataFrame({"name": ["Alice", "Bob"], "score": [88, 74]})
df2 = pd.DataFrame({"name": ["Charlie", "Diana"], "score": [92, 81]})
combined = pd.concat([df1, df2], ignore_index=True)
print(combined) name score
0 Alice 88
1 Bob 74
2 Charlie 92
3 Diana 81
pd.merge() โ Tabellen koppelen op een sleutelkolomยถ
merge() is het equivalent van een SQL JOIN.
students = pd.DataFrame(
{
"student_id": [1, 2, 3, 4],
"name": ["Alice", "Bob", "Charlie", "Diana"],
"program_id": [101, 102, 101, 103],
}
)
programs = pd.DataFrame(
{
"program_id": [101, 102, 103],
"program_name": ["Computer Science", "Electronics", "Mechanical Engineering"],
}
)
# Inner join: only rows with a match in both tables
result = pd.merge(students, programs, on="program_id", how="inner")
result# Left join: all rows from the left table, NaN where there is no match
extra_student = pd.DataFrame(
{
"student_id": [5],
"name": ["Eve"],
"program_id": [999], # does not exist in programs
}
)
students_extended = pd.concat([students, extra_student], ignore_index=True)
result_left = pd.merge(students_extended, programs, on="program_id", how="left")
result_lefttitanic = sns.load_dataset("titanic")Oefening 1 โ Bereken het overlevingspercentage per klasse (pclass). (Tip: het gemiddelde van de kolom survived geeft het percentage, want 0 = niet overleefd, 1 = overleefd.)
# your solution hereOefening 2 โ Bereken per combinatie van sex en pclass de gemiddelde leeftijd, de gemiddelde prijs (fare) en het aantal passagiers. Gebruik .agg() met benoemde aggregaties.
# your solution hereOefening 3 โ Voeg een kolom age_category toe met de waarden "child" (< 18), "adult" (18โ60) of "senior" (> 60). Gebruik .apply() met een functie.
df = titanic.copy()
df["age"] = df["age"].fillna(df["age"].median())
# your solution hereOefening 4 โ Gebruik .map() om een kolom class_name te maken die pclass (1, 2, 3) omzet naar "First class", "Second class", "Third class".
# your solution hereOefening 5 โ Voeg de Titanic-dataset samen met een fictief DataFrame van havens:
ports = pd.DataFrame({
"embarked": ["S", "C", "Q"],
"city": ["Southampton", "Cherbourg", "Queenstown"],
"country": ["UK", "France", "Ireland"],
})Gebruik een left join op de kolom embarked. Hoeveel rijen hebben een NaN voor city na de join?
ports = pd.DataFrame(
{
"embarked": ["S", "C", "Q"],
"city": ["Southampton", "Cherbourg", "Queenstown"],
"country": ["UK", "France", "Ireland"],
}
)
# your solution here