1.4 Setting Up Python and Jupyter
The first three sections established the reasoning layer: frame a decision, preserve the analysis lifecycle, and declare dataset grain and schema. We now need a working environment that does not hide how a result was produced.
This course uses Python with JupyterLab. A notebook combines executable code cells, rendered results, and explanatory Markdown cells. It is excellent for teaching and exploration—but its live memory can make an analysis appear reproducible when it is not.
Create an isolated environment
A Python virtual environment keeps this course’s packages separate from unrelated projects. From a new project directory, run:
python -m venv .venvActivate it on Windows PowerShell:
.\.venv\Scripts\Activate.ps1On macOS or Linux:
source .venv/bin/activateThen install the initial tools:
python -m pip install --upgrade pip
python -m pip install jupyterlab numpy pandas matplotlib scikit-learnUsing python -m pip ties installation to the currently selected Python interpreter. After installation, verify the interpreter and versions instead of assuming the terminal used the intended environment:
python --version
python -m pip show numpy pandas matplotlib scikit-learn jupyterlabStart JupyterLab from the project root:
python -m jupyter labDo not place secrets, customer data, or access tokens in a notebook. Outputs can remain embedded when a notebook is shared. Use sanitized course datasets and environment variables where configuration is needed.
Understand kernels and hidden state
The kernel is the Python process that holds variables and imported modules while the notebook runs. Editing a code cell changes visible text; it does not change kernel memory until the cell is executed.
Consider these cells:
discount = 0.20total = 100 * (1 - discount)
totalIf you run both, total is 80. If you edit the first cell to discount = 0.10 without rerunning it, the visible notebook suggests 10%, but the kernel still stores 20%. Running only the second cell again still produces 80. This mismatch is hidden state.
Make the notebook run top to bottom
A trustworthy notebook should pass a simple test: restart the kernel, clear prior memory, and run all cells in order without manual repair. Organize it so dependencies are visible:
- Imports appear near the beginning.
- Configuration and paths are declared once.
- Data loading follows configuration.
- Validation runs before analysis.
- Derived results follow their inputs.
- Final outputs can be regenerated rather than hand-edited.
Use relative paths rooted in the project instead of paths tied to one person’s desktop:
from pathlib import Path
PROJECT_ROOT = Path.cwd()
DATA_PATH = PROJECT_ROOT / "data" / "raw" / "deliveries.csv"Path.cwd() means the current working directory. This example assumes JupyterLab was started from the project root; document that assumption in the README. For reusable production code, path resolution may instead be anchored to a package or configuration file.
Fail early when an assumption is violated:
import pandas as pd
deliveries = pd.read_csv(DATA_PATH)
required_columns = {"order_id", "zone", "delivery_minutes"}
assert len(deliveries) > 0, "The dataset is empty"
assert required_columns <= set(deliveries.columns), "Required columns are missing"An assertion is not a complete validation system, but it is better than discovering three charts later that the wrong file was loaded.
Reproducibility controls more than code
Reproducibility means that another person—or you in the future—can use the documented inputs and process to obtain the same result, within any explicitly described tolerance. Four conditions commonly break it:
| Condition | Uncontrolled failure | Control |
|---|---|---|
| Environment | Package behavior changes | Record Python and dependency versions |
| Input | A source file is silently replaced | Version or checksum the data snapshot |
| Randomness | Sampling or models change on every run | Set and document random seeds |
| Execution | Hidden notebook state affects output | Restart kernel and run all cells |
A random seed controls a pseudo-random sequence. It helps repeat a split or simulation, but it does not repair biased data or guarantee identical behavior across every library and hardware combination. Record it as one condition, not as a magic stamp of correctness.
Build a handoff, not a personal artifact
At minimum, a small analysis project should explain:
- The decision and analytical question.
- The data source, extraction date, scope, and restrictions.
- The Python and direct dependency versions.
- The command or sequence that runs the analysis from a clean state.
- The files that are generated and the checks that indicate success.
If real data cannot be shared, provide an appropriately sanitized sample and clear acquisition instructions. Never replace missing provenance with invented details.
Chapter 1 has built the contract for the rest of the course: every calculation answers a defined question, every row has a declared meaning, every claim stays within its evidence, and every output can be traced and rerun. Chapter 2 will use this contract while introducing NumPy arrays, shapes, axes, data types, and vectorized computation.