(0 1.2
1 1.5
2 1.4
Name: voltage, dtype: float64,
time_s voltage_V
0 0 1.2
1 1 1.5
2 2 1.4)
Day 5: Pandas, scikit-learn, and good coding practices
Studierendentage Physik, Uni Heidelberg, 23.-27.03.2026
pandas is a Python library for working with structured (tabular) data.
Quick reference: https://pandas.pydata.org/docs/user_guide/10min.html
Think of it as:
(0 1.2
1 1.5
2 1.4
Name: voltage, dtype: float64,
time_s voltage_V
0 0 1.2
1 1 1.5
2 2 1.4)
Series: one labeled columnDataFrame: labeled 2D table(0 1.7
1 2.3
2 3.5
3 3.3
4 4.3
Name: y, dtype: float64,
x y sigma_y
0 1 1.7 0.5
1 2 2.3 0.3,
x y
0 1 1.7
1 2 2.3
2 3 3.5
3 4 3.3
4 5 4.3)
( x y sigma_y weight
0 1 1.7 0.5 4.000000
1 2 2.3 0.3 11.111111
2 3 3.5 0.4 6.250000
3 4 3.3 0.4 6.250000
4 5 4.3 0.6 2.777778,
x y sigma_y weight
2 3 3.5 0.4 6.250000
3 4 3.3 0.4 6.250000
4 5 4.3 0.6 2.777778)
loc and iloc(x 3.0
y 3.5
sigma_y 0.4
Name: c, dtype: float64,
x 1.0
y 1.7
sigma_y 0.5
Name: a, dtype: float64,
x y
b 2 2.3
d 4 3.3)
loc: select by labelsiloc: select by integer position( x y sigma_y
count 5.000000 5.000000 5.000000
mean 3.000000 3.020000 0.440000
std 1.581139 1.025671 0.114018
min 1.000000 1.700000 0.300000
25% 2.000000 2.300000 0.400000
50% 3.000000 3.300000 0.400000
75% 4.000000 3.500000 0.500000
max 5.000000 4.300000 0.600000,
x y sigma_y
4 5 4.3 0.6
2 3 3.5 0.4
3 4 3.3 0.4
1 2 2.3 0.3
0 1 1.7 0.5)
import numpy as np
from IPython.display import display
df = pd.DataFrame({
"x": [1, 2, 3, 4],
"y": [1.7, np.nan, 3.5, 3.3],
"sigma_y": [0.5, 0.3, 0.4, np.nan]
})
mask = df.isna()
filled = df.fillna({"y": df["y"].mean(), "sigma_y": 0.5})
clean = df.dropna()
print("Case 1: detect missing ")
display(mask)
print("Case 2: fill missing values")
display(filled)
print("Case 3: remove incomplete rows")
display(clean)Case 1: detect missing
| x | y | sigma_y | |
|---|---|---|---|
| 0 | False | False | False |
| 1 | False | True | False |
| 2 | False | False | False |
| 3 | False | False | True |
Case 2: fill missing values
| x | y | sigma_y | |
|---|---|---|---|
| 0 | 1 | 1.700000 | 0.5 |
| 1 | 2 | 2.833333 | 0.3 |
| 2 | 3 | 3.500000 | 0.4 |
| 3 | 4 | 3.300000 | 0.5 |
Case 3: remove incomplete rows
| x | y | sigma_y | |
|---|---|---|---|
| 0 | 1 | 1.7 | 0.5 |
| 2 | 3 | 3.5 | 0.4 |
import matplotlib.pyplot as plt
df = pd.DataFrame({
"time_s": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"count_rate": [245, 252, 248, 260, 255, 262, 258, 266, 263, 270]
})
ax = df.plot(x="time_s", y="count_rate", marker="o", legend=False, figsize=(6.5, 3.5))
ax.set_xlabel("time (s)")
ax.set_ylabel("count rate")
ax.set_title("Quick plot from a DataFrame")
ax.grid(True, alpha=0.25)
plt.show()
| mean | std | count | |
|---|---|---|---|
| shift | |||
| afternoon | 235.0 | 4.242641 | 2 |
| morning | 251.5 | 4.949747 | 2 |
| night | 259.0 | 2.828427 | 2 |
| thickness_mm | count_rate | net_count_rate | |
|---|---|---|---|
| 0 | 0 | 52644 | 52594 |
| 1 | 3 | 43823 | 43773 |
| 2 | 6 | 35922 | 35872 |
| 3 | 9 | 29968 | 29918 |
| 4 | 12 | 24771 | 24721 |
written to: output/day5_pb_with_net.csv
preview of the exported file:
| thickness_mm | count_rate | net_count_rate | |
|---|---|---|---|
| 0 | 0 | 52644 | 52594 |
| 1 | 3 | 43823 | 43773 |
| 2 | 6 | 35922 | 35872 |
| 3 | 9 | 29968 | 29918 |
| 4 | 12 | 24771 | 24721 |
index=False avoids writing the pandas row index as an extra columnWe measure the period of a pendulum for different lengths.
Load the dataset:
Tasks:
head(), shape, and columns.T_squared = period_s**2.T_squared versus length_m.We use the public iris dataset from the seaborn example repository:
https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv
Create a scatter plot similar to the classic iris example from scikit-learn.
iris_df.sepal_length on the x-axis and sepal_width on the y-axis.scikit-learn is a standard library for machine learning in Python.
Typical workflow:
Goal: predict the Iris flower class from measured observables.
The Iris dataset has 3 classes: setosa, versicolor, or virginica
The measured observables are:

These observables are the input for the classifier.
((150, 4),
(150,),
array(['setosa', 'versicolor', 'virginica'], dtype='<U10'),
['sepal length (cm)',
'sepal width (cm)',
'petal length (cm)',
'petal width (cm)'])
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.neural_network import MLPClassifier
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=42, stratify=y
)
clf = make_pipeline(
StandardScaler(),
MLPClassifier(hidden_layer_sizes=(20,), max_iter=2000, random_state=42)
)
clf.fit(X_train, y_train)
accuracy = clf.score(X_test, y_test)
print(f"test accuracy = {accuracy:.3f}")test accuracy = 0.933
Confusion matrix:
[[15 0 0]
[ 0 14 1]
[ 0 2 13]]
Classification report:
precision recall f1-score support
setosa 1.00 1.00 1.00 15
versicolor 0.88 0.93 0.90 15
virginica 0.93 0.87 0.90 15
accuracy 0.93 45
macro avg 0.93 0.93 0.93 45
weighted avg 0.93 0.93 0.93 45
Continue with the same iris dataset and the same train/test split.
Train two additional classifiers:
KNeighborsClassifierRandomForestClassifierTasks:
Why it matters:
Focus today:
KISS: Keep It Simple, Stupid.
Use the simplest solution that solves the problem well.
In practice:
thickness_mm, not t1)Reference: KISS principle
YAGNI: You Ain’t Gonna Need It.
Do not implement features before they are needed.
In practice:
Reference: YAGNI
DRY: Don’t Repeat Yourself.
Avoid copying the same logic into several places.
In practice:
Reference: DRY principle
One function or code block should have one clear job.
In practice:
load_data(), fit_model(), make_plot() over one long functionOne-time setup:
Clone a remote repository to your machine and create a local working copy.
Most common day-to-day commands:
Helpful safety command:
Discard local changes in a file, or with --staged remove it from the staging area.
Useful rule:
Better than:
Better:
analysis.py, fix the CSV loading error. Keep the function signature unchanged.”Reference: OpenAI Codex Prompting Guide
Good prompts often specify:
Useful constraints:
One of the most useful additions is:
For example:
This often gives much better answers than just:
The first prompt does not need to be perfect.
Often the best workflow is:
Useful follow-up prompts:
For more complex tasks:
Reusable prompt patterns:
These patterns often work better than: