The Modern Python Stack
Half of historical data-science pain was never about data — it was Python environments. That era is over if you let it be. The 2026 setup is one tool, one lockfile, and a project layout that separates exploration from code you intend to keep.
One tool: uv
uv replaced the pip/venv/conda tangle for most practitioners: it installs Python versions, creates environments, resolves dependencies fast, and pins everything in a lockfile so a colleague — or you, in six months — gets the same environment.
# new project, isolated environment, pinned deps
uv init churn-analysis && cd churn-analysis
uv add polars scikit-learn matplotlib jupyter
uv run jupyter lab
Two rules carry the day: never install into system Python, and one environment per project. Every "it works on my machine" story starts by breaking one of them.
Notebooks are for questions, scripts are for answers
Jupyter is the right tool for exploration: look at data, try things, plot, iterate. It is the wrong tool for anything that must run twice reliably — hidden state and out-of-order execution make notebooks lie to you politely. The working rhythm:
- Explore in a notebook until something works.
- Promote what worked into a module under
src/, imported back into the notebook. - Anything scheduled, shared, or load-bearing becomes a script or pipeline — never a notebook on cron.
churn-analysis/
├── pyproject.toml # deps + config, managed by uv
├── data/ # raw/ and processed/, gitignored
├── notebooks/ # numbered: 01-explore.ipynb, 02-features.ipynb
├── src/churn/ # the code you keep
└── tests/ # yes, even for analysis code
The libraries that matter
- Polars — the modern default for dataframes: fast, multi-threaded, and strict enough to catch mistakes early. Pandas remains everywhere and must be readable; write new work in either, but know both (Part 3).
- DuckDB — SQL over local files at absurd speed; the single-node answer to "do we need Spark?" (usually: no).
- scikit-learn — classical ML's lingua franca (Part 6).
- PyTorch — the deep-learning default (Part 7).
- matplotlib / seaborn / plotly — charts from rough to publishable (Part 5).
Version control from day one
Analysis code is code: git init before the first notebook, data/ and credentials in .gitignore, and small commits with honest messages. Strip notebook output before committing (nbstripout automates it) so diffs show thinking, not base64 blobs. Your future self is the collaborator you're being kind to.
Checkpoint
You're done with setup when this works end-to-end: fresh clone → uv sync → uv run jupyter lab → notebook imports from src/ and reads a file from data/. That loop, boring as it looks, is the difference between a laptop full of scripts and a body of work.