Chapter 08 / 10

数据可视化

matplotlib 内联图表、pandas 富显示、Seaborn 统计图,让数据在 Notebook 中"说话"

内联图表基础设置

在使用 matplotlib 之前,需要先设置内联显示模式:

# 在 Notebook 顶部执行一次即可
%matplotlib inline

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

# 提高图表分辨率(Retina 屏推荐)
%config InlineBackend.figure_format = 'retina'

# 设置中文字体(防止乱码)
plt.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False

基础折线图

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 300)
y1 = np.sin(x)
y2 = np.cos(x)

fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(x, y1, label='sin(x)', color='#f37626', linewidth=2)
ax.plot(x, y2, label='cos(x)', color='#4fc3f7', linewidth=2, linestyle='--')
ax.set_title('三角函数对比', fontsize=14)
ax.set_xlabel('x (弧度)')
ax.set_ylabel('y')
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()  # 图表直接显示在单元格下方
TIP 使用 fig, ax = plt.subplots() 的面向对象 API,而不是 plt.plot() 过程式 API。在 Notebook 中同时运行多个图表时,面向对象 API 更不容易产生意外的全局状态污染。

子图(Subplots)

fig, axes = plt.subplots(2, 3, figsize=(14, 7))
fig.suptitle('多子图演示', fontsize=16)

x = np.linspace(0, 10, 100)

# 第一行:折线图、散点图、柱状图
axes[0,0].plot(x, np.sin(x)); axes[0,0].set_title('折线图')
axes[0,1].scatter(x, np.random.randn(100)); axes[0,1].set_title('散点图')
axes[0,2].bar(['A','B','C','D'], [3,7,2,5]); axes[0,2].set_title('柱状图')

# 第二行:直方图、箱线图、饼图
data = np.random.randn(500)
axes[1,0].hist(data, bins=30, color='#f37626'); axes[1,0].set_title('直方图')
axes[1,1].boxplot([np.random.randn(100) for _ in range(3)]); axes[1,1].set_title('箱线图')
axes[1,2].pie([30,25,20,25], labels=['A','B','C','D']); axes[1,2].set_title('饼图')

plt.tight_layout()
plt.show()

pandas DataFrame 富显示

pandas 的 DataFrame 在 Jupyter 中会自动渲染成格式化的 HTML 表格:

import pandas as pd

# DataFrame 直接显示为格式化表格
df = pd.DataFrame({
    '语言': ['Python', 'JavaScript', 'Java', 'Go', 'Rust'],
    '流行度': [85, 78, 65, 55, 48],
    '薪资指数': [92, 80, 75, 88, 95]
})

# 直接输入变量名即可,无需 print
df
pandas DataFrame 表格显示
pandas DataFrame 在 Jupyter 中以 HTML 表格形式渲染,比纯文本更易读

DataFrame 样式设置

# 高亮最大值
df.style.highlight_max(color='#f37626', axis=0)

# 数值条形背景
df.style.bar(subset=['流行度', '薪资指数'], color='#4fc3f7')

# 设置格式
df.style.format({'流行度': '{:.0f}%', '薪资指数': '{:.0f}K'})

Seaborn 统计图

import seaborn as sns

# 使用 Seaborn 内置数据集
tips = sns.load_dataset('tips')

fig, axes = plt.subplots(1, 3, figsize=(15, 5))

# 散点图 + 回归线
sns.regplot(data=tips, x='total_bill', y='tip', ax=axes[0])
axes[0].set_title('账单 vs 小费')

# 分组箱线图
sns.boxplot(data=tips, x='day', y='total_bill', hue='sex', ax=axes[1])
axes[1].set_title('各天账单分布')

# 热力图(相关矩阵)
corr = tips[['total_bill', 'tip', 'size']].corr()
sns.heatmap(corr, annot=True, cmap='coolwarm', ax=axes[2])
axes[2].set_title('相关矩阵')

plt.tight_layout()
plt.show()

ipywidgets 交互式控件

ipywidgets 让图表变成可交互的工具:

from ipywidgets import interact, IntSlider
import matplotlib.pyplot as plt
import numpy as np

def plot_sine(frequency=1, amplitude=1):
    x = np.linspace(0, 2 * np.pi, 300)
    y = amplitude * np.sin(frequency * x)

    plt.figure(figsize=(8, 3))
    plt.plot(x, y, color='#f37626', linewidth=2)
    plt.ylim(-3, 3)
    plt.title(f'sin({frequency}x) × {amplitude}')
    plt.grid(True, alpha=0.3)
    plt.show()

# 自动生成滑块控件
interact(plot_sine,
         frequency=IntSlider(min=1, max=10, value=1),
         amplitude=IntSlider(min=1, max=3, value=1))
INFO 执行上述代码后,Notebook 会在图表下方显示两个滑块。拖动滑块,图表会实时更新——无需重新运行单元格!这是 Jupyter 交互式计算的精髓。

IPython.display 模块

from IPython.display import display, HTML, Image, Audio, Video

# 显示 HTML
display(HTML('<h3 style="color:#f37626">自定义 HTML</h3>'))

# 显示本地图片
display(Image(filename='plot.png', width=400))

# 显示网络图片
display(Image(url='https://example.com/image.png', width=300))

# 在同一单元格显示多个输出
display(df.head())
display(df.describe())

常见图表类型代码模板

import matplotlib.pyplot as plt
import numpy as np

# ── 热力图(相关矩阵可视化)──
import seaborn as sns
import pandas as pd

data = pd.DataFrame(np.random.randn(100, 4), columns=['A','B','C','D'])
corr = data.corr()

fig, ax = plt.subplots(figsize=(6, 5))
sns.heatmap(corr,
    annot=True,      # 在格子里显示数值
    fmt='.2f',       # 数值格式:保留2位小数
    cmap='RdYlGn',   # 颜色映射:红-黄-绿
    vmin=-1, vmax=1,  # 数值范围固定 -1 到 1
    ax=ax
)
ax.set_title('特征相关矩阵')
plt.tight_layout()
plt.show()

# ── 双 Y 轴图(展示不同量级的两组数据)──
fig, ax1 = plt.subplots(figsize=(10, 4))
x = np.arange(12)
revenue = np.random.randint(100, 500, 12)
growth = np.random.uniform(-0.1, 0.3, 12)

ax1.bar(x, revenue, color='#4fc3f7', alpha=0.7, label='收入(万元)')
ax1.set_ylabel('收入(万元)', color='#4fc3f7')

ax2 = ax1.twinx()  # 创建共享 X 轴的第二 Y 轴
ax2.plot(x, growth, 'o-', color='#f37626', linewidth=2, label='增长率')
ax2.set_ylabel('增长率', color='#f37626')
ax2.yaxis.set_major_formatter(plt.FuncFormatter(lambda y, _: f'{y:.0%}'))

fig.legend(loc='upper left', bbox_to_anchor=(0.1, 0.9))
plt.title('月度收入与增长率')
plt.tight_layout()
plt.show()

图表保存与分辨率控制

# 保存高分辨率图表用于发布
fig, ax = plt.subplots(figsize=(8, 5))
ax.plot([1, 2, 3], [4, 1, 3])

# dpi=300 适合印刷,dpi=150 适合网页
fig.savefig('output.png', dpi=150, bbox_inches='tight')
fig.savefig('output.pdf', bbox_inches='tight')  # PDF 矢量格式
fig.savefig('output.svg')  # SVG 矢量,适合网页

# 同时显示并保存(inline 模式下 plt.show() 后 figure 会被清除)
# 必须先 savefig 再 show,顺序不能反
fig.savefig('before_show.png')  # ✅ 先保存
plt.show()                         # ✅ 再显示
常见错误:savefig 在 show 之后

在 Jupyter inline 模式下,plt.show() 会清空当前 figure 对象并将其渲染为输出。如果在 plt.show() 之后调用 fig.savefig(),会保存一张空白图片。正确顺序是:先 savefig,再 show。或者保存 fig 对象引用:fig, ax = plt.subplots(),用 fig.savefig() 而不是 plt.savefig(),两者在多图并存时行为不同。

可视化输出的底层原理

%matplotlib inline 的原理
这个魔法命令告诉 matplotlib 使用 inline 后端——将图表渲染为 PNG/SVG 字节流并通过 IPython 的 display 协议嵌入到输出区域,而非弹出独立窗口。这是 Jupyter 图表内联显示的核心机制。%matplotlib widget 则使用 ipympl 后端,支持交互式缩放和平移。
Pandas 的 HTML 富文本渲染
DataFrame 的 __repr__ 方法返回 HTML 字符串(带样式的 HTML 表格),Jupyter 的 display 系统检测到 HTML MIME 类型后,直接将其注入到 Out[n] 区域的 DOM 中。这就是为什么在 Jupyter 中看到的 DataFrame 是美观的表格,而在普通终端看到的是等宽文本。同样原理适用于任何实现了 _repr_html_ 方法的对象。
ipywidgets 的通信机制
ipywidgets 使用 Widget 模型——前端(浏览器 JavaScript)和后端(Kernel Python 对象)通过 WebSocket 同步状态。当用户拖动滑块时,JavaScript 将新值发送给 Kernel;Kernel 更新 Python 对象的 value 属性并触发 observe 回调(或 interact 的目标函数);函数执行后的新图表再通过 display 协议发回浏览器。这个双向通信是实时交互的基础。

本章小结

本章核心要点