April 14, 2026

Setup eines Data Science Projekts

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 uv findet 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 = true

5. 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.txt

6. 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 lint

7. 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/
.env

10. 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

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert