跳到正文

Jupyter Notebook入门:安装、单元格与魔法命令

Python 做数据分析,第一步通常不是学语法,而是先拿到一个能边写边看结果的交互环境。Jupyter 就是这件事的标准答案:代码按「单元格(cell)」执行,变量留在内存里,图和数据框直接印在下方。

代价是它的运行模型和普通脚本完全不同。notebook 里保存的代码和内存里真正生效的代码可以是两份东西,这是绝大多数「昨天还能跑通,今天结果变了」事故的来源。先把环境装对,再把这个模型搞清楚。

最省事的是用 pip 装 JupyterLab:

终端窗口
pip install jupyterlab

只想要经典界面就装 Notebook 包:

终端窗口
pip install notebook

两者可以共存,分别用 jupyter labjupyter notebook 启动。Notebook 7 之后底层换成了 JupyterLab 的组件,界面差别不大,新项目直接用 JupyterLab。

一个课题一个虚拟环境,避免包版本互相污染:

终端窗口
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install jupyterlab pandas scikit-learn

这里有个高频陷阱:Jupyter 的代码是在内核(kernel)里执行的,内核不一定是终端里激活的那个环境。如果你系统里早就装过 Jupyter,新建的 venv 里只装了 pandas,那么 jupyter lab 启动的是系统那份 Jupyter,内核也是系统 Python,import pandas 立刻报 ModuleNotFoundError。稳妥做法是在环境内装 Jupyter,并用 python -m jupyter lab 启动,让解释器和内核必然对上。

conda 用户走另一条路:

终端窗口
conda create -n ds python=3.12
conda activate ds
conda install jupyterlab pandas scikit-learn

conda 的优势是 numpy、scipy 这类带 C 扩展的包有预编译版本,不用现场编译;代价是依赖解析慢。把环境写进 environment.yml 再分享,同门才能装出一模一样的版本。同一个环境里不要混用 conda 和 pip 装同一批包,两套依赖记录会互相打架。

终端窗口
jupyter lab

终端会打印一个带 token 的地址:

[I 2026-09-22 10:03:11.482 ServerApp] Jupyter Server 2.14.0 is running at:
[I 2026-09-22 10:03:11.482 ServerApp] http://localhost:8888/lab?token=8f3c1d2e9a7b4c5d

浏览器一般会自动打开。如果跑在实验室的远程服务器上,用 jupyter lab --no-browser 启动,再在本地做端口转发:

终端窗口
ssh -L 8888:localhost:8888 user@server

这样本地的 8888 端口就接到了服务器上,接着在本地浏览器打开那个带 token 的地址。

一个 notebook 由若干单元格组成,最常用的是 Code(执行代码)和 Markdown(写说明和公式)两种。新手最容易卡住的是模式:编辑模式才能打字,命令模式才能用单元格级快捷键。

操作 快捷键 说明
编辑模式 / 命令模式切换 Enter / Esc 命令模式下才能用下表快捷键
运行并跳到下一格 Shift + Enter 用得最多的一个键
运行并留在本格 Ctrl + Enter 反复调试同一段代码时用
在上方 / 下方插入单元格 A / B 需在命令模式
删除单元格 D D 连按两次
在 Markdown 与 Code 间切换 M / Y 需在命令模式

运行过的 Code 单元格左侧会出现 In [3] 这样的序号。注意它是执行顺序,不是从上到下的排列顺序——记住这一点,下面的问题就都好理解了。

先在 Cell 1 里定义变量并运行:

rate = 0.05
years = 10
print(1000 * (1 + rate) ** years)
1628.894626777442

然后回到 Cell 1,把 rate 改成 0.08,但不运行它,直接运行下面的 Cell 2:

print(1000 * (1 + rate) ** years)
1628.894626777442

输出还是旧值。此刻文件里保存的代码写着 rate = 0.08,内存里生效的却仍是 rate = 0.05。你把 notebook 发给同门,他从头运行一遍,得到的数字和你屏幕上的不一样——而且没人能一眼看出哪里不对。

反过来更危险的情况是:清洗逻辑改了却忘了重跑,后面所有统计量都建立在一份已被覆盖的数据上,全程没有任何报错。

三条习惯基本能消除这类问题:

  • 得到重要结果之前,执行 Kernel → Restart Kernel and Run All Cells,从干净内存完整跑一遍。
  • 单元格顺序按依赖关系从上到下排列,不要靠「先跑下面再跑上面」凑结果。
  • 耗时的中间结果存成文件,不要指望内存里的变量一直在。

IPython 提供一批以 % 开头的命令,只在 notebook 和 IPython 里生效。常用的几个:

%timeit 会自动多轮运行取平均,比手写 time.time() 可靠:

%timeit sum(i * i for i in range(1000))
105 µs ± 2.1 µs per loop (mean ± std. dev. of 7 runs, 10,000 loops each)

%%time 作用于整个单元格,只跑一次,适合给数据加载、模型拟合计时:

%%time
total = sum(i * i for i in range(10_000_000))
print(total)
333333283333335000000
CPU times: user 1.05 s, sys: 0 ns, total: 1.05 s
Wall time: 1.05 s

%matplotlib inline 让图直接嵌在输出里。IPython 5 之后 inline 已经是默认后端,不写也能出图,显式写出来的唯一意义是提醒自己别在同一个 notebook 里来回切后端——装了 ipympl 之后用 %matplotlib widget 会切成可交互画布,两种后端混着用经常得到空白图。

其余几个顺手记住:

  • %pwd%ls 查看当前目录和文件,等价于 shell 的 pwdls
  • %who 列出当前内存里的变量名,忘了自己定义过什么时很有用。
  • %run script.py 把外部脚本当单元格执行,脚本里定义的函数直接进入当前命名空间。
  • %load_ext autoreload%autoreload 2,改完 .py 模块不用重启内核,下次调用自动重新加载。
  • %pip install 包名 安装到当前内核所在的环境,比 !pip install 可靠,后者用的是启动 Jupyter 时那个 shell 的解释器。

用文本编辑器打开任意 .ipynb,看到的是一个 JSON 文件(下面省略了部分字段):

{
"cells": [
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": { "text/plain": ["1628.894626777442"] },
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": ["rate = 0.05\n", "1000 * (1 + rate) ** years"]
}
],
"metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" } },
"nbformat": 4,
"nbformat_minor": 5
}

关键在 outputsexecution_count运行结果会被写进文件,图片以 base64 编码塞在同一个字段里。这带来三个后果:

  1. 代码改了没重跑,文件里存的是旧输出,翻看的人看到的是过期结论;
  2. execution_count 每次运行都变,git diff 里全是噪声,图片 base64 差一个像素就是几万字符的差异;
  3. 图多了之后仓库体积涨得很快。

三种应对方式,按投入从低到高:

  • 提交前用 Edit → Clear All Outputs 清空输出,或者装 nbstripout 挂成 git filter,提交时自动清空。
  • 用 Jupytext 把 notebook 和同名的 .py 文件配对,git 里只跟踪脚本,notebook 由脚本生成。
  • 把 notebook 当探索工具,最终分析固化成 .py 脚本或 Quarto 文档,让结果可以一键重跑。R 语言的 /r/reporting/quarto/ 讲的是同一套思路,只是那边的计算引擎默认是 knitr。

导出方面,File → Save and Export Notebook As 支持 HTML 和 Markdown,写论文附录用 HTML 版最省事。导出 PDF 需要本机有完整的 LaTeX 环境,配起来比写分析本身还费时。

jupyter: command not found 包没装进当前环境,或者可执行文件目录不在 PATH。用 python -c "import jupyterlab" 确认装在哪个解释器里,然后用 python -m jupyter lab 启动。

单元格一直显示 [*] 多半是死循环或者卡在网络请求。菜单 Kernel → Interrupt Kernel 中断;实在不行 Restart Kernel,代价是变量全清空,得从头运行。

改了 .py 模块,import 进来的还是旧代码 Python 只在第一次 import 时读取文件。要么重启内核,要么用前面说的 %autoreload 2

图里的中文变成方块 Matplotlib 默认字体不含中文字形,解决办法见 /python/visualization/matplotlib-basics/

环境有了,接着要熟悉 Python 数据科学的计算底座。下一篇 /python/basics/numpy/ 讲 ndarray 的创建、索引和广播,那是 pandas 与 scikit-learn 共同依赖的东西。