In diesem Beitrag wird ich ein praxisnahes Setup für ein professionelles Data-Science-Projekt erklärt – von der Ordnerstruktur über Git, Linting, Konfiguration bis hin zu Pre-Commit-Hooks.
0. Projekt initialisieren
Zur Initialisierung des Projekts gibt es verschiedene Möglichkeiten, abhängig von den verwendeten Tools. Empfehlenswert ist von Anfang an uv zu nutzen.
Ein ausführlicher Beitrag zum Projektsetup mit
uvfindet man hier.
1. Git Repository
Einen ausführlicheren Beitrag zu Git findet man hier.
Jedes Data-Science-Projekt sollte von Anfang an versioniert sein. Der einfachste Weg ein neues Repository zu erstellen ist dieses direkt auf Github anzulegen und erst danach lokal zu klonen.
git clone https://github.com/<dein-username>/git_workflow.git 2. Empfohlene Ordnerstruktur
Empfehlenswert ist klare und einheitliche Ordnerstruktur.
project-name/
│
├── data/ # Alle projektspezifischen Daten (nicht versioniert!)
│ ├── raw/ # Originaldaten aus Quellen (unverändert, read-only)
│ │ # z.B. CSV-Exports, API-Dumps, Messdaten
│ ├── interim/ # Zwischenergebnisse von Datenaufbereitung
│ │ # z.B. bereinigte, aber noch nicht finale Datensätze
│ └── processed/ # Final verarbeitete, modellfertige Daten
│ # Diese Daten werden von Modellen direkt verwendet
│
├── notebooks/ # Jupyter Notebooks für Exploration & Prototyping
│ ├── 01_exploration.ipynb # Explorative Datenanalyse (EDA, Visualisierung, Checks)
│ ├── 02_feature_engineering.ipynb # Entwicklung und Validierung von Features
│ └── 03_modeling.ipynb # Modelltraining, erste Experimente & Vergleiche
│ # Wichtig: kein produktiver Code, nur Exploration!
│
├── src/ # Produktiver Python-Code des Projekts
│ └── project_name/ # Python-Package (Name = Projektname)
│ ├── __init__.py # Markiert das Verzeichnis als Python-Package
│ │
│ ├── config.py # Zentrale Konfigurationslogik
│ │ # z.B. Laden & Validieren von config.yaml
│ │
│ ├── data/ # Datenzugriff & Datenaufbereitung
│ │ ├── load.py # Laden von Roh- und Zwischendaten
│ │ │ # z.B. CSV, Datenbanken, APIs
│ │ └── preprocess.py # Bereinigung, Transformation, Validierung von Daten
│ │
│ ├── features/ # Feature Engineering
│ │ # z.B. Feature-Erzeugung, -Selektion, -Transformation
│ │
│ ├── models/ # Modell-Definitionen & Training
│ │ # z.B. Trainingspipelines, Hyperparameter, Persistenz
│ │
│ ├── evaluation/ # Modellbewertung & Metriken
│ │ # z.B. Cross-Validation, Reports, Plots
│ │
│ └── utils/ # Hilfsfunktionen
│ # z.B. Logging, Pfad-Handling, Wiederverwendbares
│
├── tests/ # Automatisierte Tests (pytest)
│ ├── test_data.py # Tests für Datenladen & Preprocessing
│ └── test_models.py # Tests für Modelle & Trainingslogik
│ # Struktur orientiert sich an src/
│
├── configs/ # Konfigurationsdateien
│ └── config.yaml # Projektkonfiguration
│ # z.B. Pfade, Modellparameter, Random Seeds
│
├── .github/ # GitHub-spezifische Konfiguration
│ └── workflows/
│ └── ci.yml # CI-Pipeline
│ # z.B. Linting, Tests, Build bei jedem Push/PR
│
├── .gitignore # Dateien & Ordner, die nicht versioniert werden
│ # z.B. Daten, virtuelle Environments, Artefakte
│
├── .pre-commit-config.yaml # Pre-Commit Hooks
│ # z.B. Ruff, Formatter, Tests vor jedem Commit
││
├── DOCKERFILE # Dockerfile
│
├── pyproject.toml # Zentrale Tool- & Projektkonfiguration
│ # z.B. Ruff, Formatter, Build-System, Metadaten
│
├── Makefile # Standardisierte Projektbefehle
│ # z.B. make install, make lint, make test
│
├── README.md # Projektdokumentation & Einstiegspunkt
│ # Setup, Struktur, Nutzung, Konfiguration
│
└── requirements.txt # Python-Abhängigkeiten
# Alternativ zu Poetry / uv bei einfachen Projekten
Wichtig ist die Trennung von Notebooks und produktivem Code:
Notebooks dienen ausschließlich der Exploration und Prototypenbildung. Jegliche Logik, die reproduzierbar oder produktiv genutzt wird, gehört in src/ und wird von Notebooks lediglich importiert.
Je nach Projektumfang können weitere Komponenten sinnvoll sein:
- Terraform (Infrastruktur)
- Docker (Reproduzierbarkeit, Deployment)
- DVC / Git-LFS (Versionierung großer Daten)
- MLflow / Weights & Biases (Experiment-Tracking)
- dbt (Analytics Engineering)
3. Zentrale Konfiguration
Einen ausführlicheren Beitrag zu Konfigurationsdateien findet man hier.
config.yaml
Die Datei config.yaml wird für nicht-technische Parameter verwendet
data:
raw_path: data/raw
processed_path: data/processed
model:
n_estimators: 200
max_depth: 8
Einlesen im Code:
import yaml
with open("configs/config.yaml") as f:
config = yaml.safe_load(f)
In größeren Projekten empfiehlt es sich, mehrere Konfigurationsdateien zu verwenden:
- config.dev.yaml
- config.test.yaml
- config.prod.yaml
Etwas eleganter ist es eine eigene config.py zu schreiben:
from pathlib import Path
import yaml
# Abhängig davon wo die Config.yaml liegt:
_ROOT_DIR = Path(__file__).resolve().parents[2]
_CONFIG_PATH = _ROOT_DIR / "config.yaml"
def load_config() -> dict:
if not _CONFIG_PATH.exists():
raise FileNotFoundError(
f"Missing config file: {_CONFIG_PATH}"
)
with _CONFIG_PATH.open("r", encoding="utf-8") as f:
return yaml.safe_load(f)
if __name__ == "__main__":
cfg = load_config()
print("Config loaded successfully.")
print(cfg)In den einzelnen Pythondateien kann man die Konfiguration dann so laden:
from projektname.config import load_config
cfg = load_config()
print(cfg["project"]["id"])pyproject.toml
Die Datei pyproject.toml wird für zentrale Projekteinstellungen verwendet, insb. für:
- Tooling
- Linting
- Formatting
- Build-System
Linting & Formatting mit Ruff
In pyproject.toml wird beispielsweise die Ruff für Linting und Formatting konfiguriert:
pyproject.toml
[tool.ruff]
line-length = 88
select = ["E", "F", "I", "B"]
fix = true5. Environments & Dependencies
Einen ausführlicheren Beitrag zu virtuellen Environments findet man hier.
Für jedes Projekt sollte ein entsprechendes virtuelles Environment angelegt werden. Das geht mit VENV, conda oder UV.
# Venv
python -m venv myenv
source myenv/bin/activate # (Windows: myenv\bin\activate)
# Conda
conda create --name myenv python=3.10
conda activate myenv
# UV
uv venv .venv
source .venv/bin/activate # (Windows: .venv\Scripts\activate)requirements.txt
Um die Abhängigkeiten eines virtuellen Environments zu dokumentieren verwendet man standardmäßig die Datei „requirements.txt“.
pip freeze > requirements.txt6. Makefile
Einen ausführlicheren Beitrag zu Makefile findet man hier.
Ein Makefile macht Abläufe standardisiert.
install:
pip install -r requirements.txt
lint:
ruff check src tests
format:
ruff format src tests
test:
pytest
all: lint test
Aufruf:
make lint7. Pre-Commit Hooks – Qualität erzwingen
Einen ausführlicheren Beitrag zu Pre-Commit Hooks findet man hier.
Installation
pip install pre-commit
pre-commit install.pre-commit-config.yaml
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.7
hooks:
- id: ruff
- id: ruff-format
8. Tests
Auch in Data-Science-Projekten sind Tests zentral:
- Sicherstellen, dass Datenpipelines stabil bleiben
- Verhindern Fehler bei Feature-Engineering
- Refactoring ohne Risiko
- Reproduzierbarkeit von Modellen
Beispiel:
def test_load_data_returns_dataframe():
df = load_data("data/raw/sample.csv")
assert df.shape[0] > 0
9. .gitignore
Unbedingt notwendig ist die Datei .gitignore, damit ungewollte Dateien wie Daten, Artefakte, Environments oder Passwörter nicht auf Git gepushed werden:
.venv/
__pycache__/
.ipynb_checkpoints/
data/raw/
data/interim/
data/processed/
.env10. README.md
Minimaler Inhalt:
# Project Name
## Motivation
Kurze Beschreibung des Projekts
## Setup
make install
## Projektstruktur
Kurze Erklärung der wichtigsten Ordner
## Konfiguration
configs/config.yaml
## Entwicklung
make lint
make test