Chapter 3, Environment
Notebooks, scripts, and the workflow
Data analysis is exploration: you test an idea, look at the result, and decide the next step. Notebooks exist for this cycle, and they have a trap that everyone falls into once.
Code for this chapter: ambiente/cap03_notebooks.py
Script or notebook
A script runs from top to bottom, from start to finish, every time. This is great for a program you will run on the server, but bad for exploring data, because reprocessing everything to look at one column is slow. A notebook is divided into cells that you execute one at a time, and the result is visible right below, like in a lab notebook.
| Option | When I use |
|---|---|
| JupyterLab | Local exploration, the standard of the area |
| Jupyter in VS Code | Notebook within the same editor where I write the rest of the code |
| Google Colab | When I don't want to install anything, or need a GPU |
| Common script | When the code will become routine, test, or service |
To use JupyterLab in a project with uv, install it as a development dependency and open:
uv add --dev jupyterlab ipykernel
uv run jupyter lab
The kernel and the hidden state trap
Each notebook is linked to a kernel, the Python process that remembers all the variables that you created. The cells do not need to run in order: you can execute the fifth, go back, and re-execute the second. This is exactly what causes the problem. The notebook may show a result that only exists because of a cell you edited later, and the text you read on the screen no longer reproduces the result:
celulas = ["total = 10", "dobro = total * 2"]
kernel = {}
for celula in celulas:
exec(celula, kernel)
celulas[0] = "total = 99"
exec(celulas[0], kernel)
print("o que a tela mostra:", kernel["dobro"])
novo_kernel = {}
for celula in celulas:
exec(celula, novo_kernel)
print("depois de reiniciar e rodar tudo:", novo_kernel["dobro"])
o que a tela mostra: 20
depois de reiniciar e rodar tudo: 198
I edited the first cell to 99 and re-ran just it. The dobro on the screen remained 20, a value that no longer corresponds to the visible code. Only by restarting the kernel and running everything in order does the true value appear, 198.
The rule before calling a notebook ready
Restart the kernel and run all the cells, in order. If something breaks or changes, the notebook depended on a hidden state. I do this before sharing any notebook.
The best of both worlds: cells in a `.py` file
VS Code and JupyterLab (with the extension jupytext) understand a common Python file with markers # %%: each marker opens a cell. The file remains a script that runs entirely, with the advantage of readable differences in Git, which the format .ipynb (a giant JSON with embedded outputs) does not provide:
# %% [markdown]
# # Notas de vendas
# Cada bloco `# %%` é uma célula no VS Code e no JupyterLab (com jupytext).
# %%
import numpy as np
vendas = np.array([120.0, 80.5, 99.9, 150.0])
# %%
print(vendas.mean().round(2))
# %%
print(vendas.max())
Measuring time in the notebook
In Jupyter, the "magic"
%timeitruns the code multiple times and returns a reliable average, much better than atime.time()isolated. In a common script, the equivalent is the moduletimeit, which chapter 18 uses.