2026-06-14-数据可视化 Vol.3 可视化库
matplotlib / Seaborn
一、全局配置
字体配置:通过
rcParams字典全局修改 matplotlib 的运行时配置参数。font.sans-serif指定无衬线字体族的回退列表,matplotlib 按列表顺序查找第一个系统中已安装的字体。axes.unicode_minus设为False是为了让 matplotlib 使用 ASCII 连字符-替代 Unicode 负号−, 因为很多中文字体缺这个 Unicode 字符,会显示为方块 □。 这些配置放在import之后、任何绘图之前,对所有后续图表生效。
import matplotlib.pyplot as plt # 导入 pyplot 模块,约定别名为 plt
# ===== 字体配置 =====plt.rcParams['font.sans-serif'] = ['SimHei'] # 【字体配置】无衬线字体族列表,SimHei=黑体(Windows自带), # 也可用 'Microsoft YaHei'(微软雅黑)、'WenQuanYi Micro Hei'(Linux文泉驿)、 # 'PingFang SC'(macOS苹方);matplotlib 按列表顺序查找第一个可用字体
plt.rcParams['axes.unicode_minus'] = False # 【字体配置】关闭 Unicode 负号,用 ASCII '-' 替代 Unicode '−', # 避免中文字体缺失该字符导致负号显示为方块 □
# ===== 画布默认值 =====plt.rcParams['figure.figsize'] = (8, 5) # 全局默认画布尺寸:宽8英寸、高5英寸(所有未显式指定 figsize 的图都用这个)plt.rcParams['figure.dpi'] = 100 # 全局默认分辨率:100 dots per inch,控制屏幕上图片的像素密度 # 最终像素数 = figsize × dpi,如 8×100=800px 宽二、Figure / Axes 创建区
子图: API 是
plt.subplots(),它一次性创建 Figure(画布容器)和 Axes(坐标轴/子图区域)对象。nrows和ncols定义子图网格的行列数,返回的axes是一个二维 NumPy 数组,通过axes[row, col]索引操作每个子图。 这是"面向对象"写法,比老的plt.subplot()更灵活——你可以对每个子图单独设置标题、刻度、图例等,互不干扰。 最后用plt.tight_layout()自动计算并调整子图之间的间距,防止标签文字重叠/被裁切。
单图
fig, ax = plt.subplots(figsize=(8, 5), dpi=100)# 【子图】创建一个包含单个 Axes 的 Figure;fig 是整张画布,ax 是画布上的唯一坐标轴区域# figsize 参数会覆盖全局 rcParams 中的默认值,单位是英寸 (width, height)# dpi=100 表示该图的分辨率为 100 像素/英寸,最终像素 = 800×500多子图(网格布局)
fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(10, 8))# 【子图】创建 2行×2列 共4个子图的网格,axes 是 shape=(2,2) 的 ndarray# figsize=(10,8) 指定整个画布的大小(宽10,高8英寸),每个子图自动均分画布空间
axes[0, 0].plot(x1, y1, color='blue') # 【子图】通过行列索引 [行, 列] 访问第1个子图,在上面画折线图axes[0, 1].bar(x2, y2, color='orange') # 【子图】访问第2个子图(第0行第1列),在上面画柱状图axes[1, 0].scatter(x3, y3, color='green') # 【子图】访问第3个子图(第1行第0列),在上面画散点图axes[1, 1].hist(data, bins=20) # 【子图】访问第4个子图(第1行第1列),在上面画直方图
# 遍历所有子图做统一设置(如添加标题、去边框)for i, row in enumerate(axes): # 遍历行 for j, ax in enumerate(row): # 遍历列 ax.set_title(f'子图 {i+1},{j+1}') # 【子图】给每个子图分别设置标题 ax.spines['top'].set_visible(False) # 去顶边框 ax.spines['right'].set_visible(False) # 去右边框
plt.tight_layout() # 【子图】自动调整子图间距(padding), # 防止相邻子图的标签/标题重叠,是最后一步布局操作plt.subplots 解包规则(易错点)
# nrows=1, ncols=1 时,axes 是单个对象,直接赋值fig, ax = plt.subplots(1, 1) # ✅ ax 是 Axes 对象,可直接 .plot()
# nrows=1, ncols>1 时,axes 是一维数组fig, axes = plt.subplots(1, 3) # ✅ axes 是 shape=(3,) 的一维数组axes[0].plot(x, y) # ✅ 用一维索引访问
# nrows>1, ncols>1 时,axes 是二维数组fig, axes = plt.subplots(2, 3) # ✅ axes 是 shape=(2,3) 的二维数组axes[0, 1].plot(x, y) # ✅ 用二维索引 [行, 列] 访问不规则子图布局(使用 subplot_mosaic,matplotlib 3.3+)
# 用字符串定义布局:每个字母代表一个子图,同一字母跨越多格表示合并fig, axes = plt.subplot_mosaic([ ['A', 'A', 'B'], # 第1行:A 占2列,B 占1列 ['A', 'A', 'C'], # 第2行:A 继续占2列,C 占1列 ['D', 'E', 'F'], # 第3行:D、E、F 各占1列], figsize=(10, 8))# axes 是 dict,key 是布局字母:{'A': ax_A, 'B': ax_B, ...}axes['A'].plot(x1, y1) # 【子图】A 是合并了2行×2列的大子图axes['B'].bar(x2, y2) # 【子图】B 是右上角单格axes['D'].scatter(x3, y3) # 【子图】D 是左下角单格其他写法
plt.figure(figsize=(8, 5)) # 先创建 Figureplt.subplot(2, 2, 1) # 再逐个添加子图:(行数, 列数, 序号),序号从1开始plt.plot(x, y)plt.subplot(2, 2, 2) # 切换到第2个子图plt.bar(x, y)# 缺点:状态机写法,难以精确控制每个子图的属性三、坐标轴与刻度区
刻度配置:刻度分为"刻度位置"(ticks,即数据坐标系中刻度线出现的位置)和"刻度标签"(tick labels, 即刻度线上方/旁边的文字)。配置分为四个层次:
①
set_xticks()/set_yticks()— 设定刻度在数据轴上的位置(数值列表);②
set_xticklabels()/set_yticklabels()— 覆盖刻度标签文字(与刻度位置一一对应);③
tick_params()— 统一控制所有刻度的视觉样式(旋转角度、字体大小、颜色、刻度线长度等);④
set_xlim()/set_ylim()— 设定坐标轴的数值范围,超出范围的刻度不会显示。
# ===== 轴标签 =====ax.set_xlabel('时间', fontsize=12)# 【刻度配置】设置 x 轴标签文字为"时间",字号12pt;该标签显示在 x 轴下方居中位置
ax.set_ylabel('数值', fontsize=12)# 【刻度配置】设置 y 轴标签文字为"数值",字号12pt;该标签显示在 y 轴左侧居中位置
# ===== 图表标题 =====ax.set_title('图表标题', fontsize=14, fontweight='bold')# 【刻度配置】设置图表标题,fontweight='bold' 加粗,标题显示在坐标轴顶部居中
# ===== 坐标轴范围 =====ax.set_xlim(0, 100)# 【刻度配置】锁定 x 轴显示范围从 0 到 100,超出范围的数据点不会被裁剪但不可见# 参数等价于 ax.set_xlim(left=0, right=100)
ax.set_ylim(0, 1)# 【刻度配置】锁定 y 轴显示范围从 0 到 1
# ===== 刻度位置(ticks 在哪里) =====ax.set_xticks([0, 25, 50, 75, 100])# 【刻度配置】在 x 轴的 0、25、50、75、100 这5个位置放置刻度线和标签# 刻度位置是数据坐标,必须落在 set_xlim 范围内才能显示
ax.set_yticks([0, 0.2, 0.4, 0.6, 0.8, 1.0])# 【刻度配置】在 y 轴的这些位置放置刻度线;如果不调此参数,matplotlib 会自动选择合适的刻度位置
# ===== 刻度标签(ticks 上显示什么文字) =====ax.set_xticklabels(['一月', '二月', '三月', '四月', '五月'])# 【刻度配置】用自定义文字覆盖刻度标签,与 set_xticks 的位置一一对应# 第1个位置(0)显示"一月",第2个位置(25)显示"二月"...# 必须先调 set_xticks 再调 set_xticklabels,否则标签数量和位置可能不匹配
# ===== 刻度样式(tick_params 统一控制外观) =====ax.tick_params(axis='x', rotation=45, labelsize=10, colors='gray')# 【刻度配置】axis='x' 只作用于 x 轴刻度(也可设为 'y' 或 'both')# rotation=45:刻度标签文字逆时针旋转45度,用于标签文字过长时防止重叠# labelsize=10:刻度标签字号设为10pt# colors='gray':刻度标签和刻度线颜色设为灰色
ax.tick_params(axis='both', which='major', length=6, width=1.5)# 【刻度配置】which='major' 控制主刻度线长度和宽度(which='minor' 控制次刻度线)# length=6 刻度线向外延伸6点;width=1.5 刻度线宽度1.5点
# ===== 坐标轴脊线(spines)美化 =====ax.spines['top'].set_visible(False) # 【刻度配置】隐藏顶部边框脊线,实现"开口式"坐标轴风格ax.spines['right'].set_visible(False) # 【刻度配置】隐藏右侧边框脊线(Edward Tufte 推荐的数据-墨水比优化)# spines 的 key 有4个:'top', 'bottom', 'left', 'right'# 还可调 spine 位置:ax.spines['left'].set_position(('data', 0)) 将 y 轴移到 x=0 处
# ===== 网格线 =====ax.grid(True, linestyle='--', alpha=0.3)# 【刻度配置】显示网格线,linestyle='--' 虚线风格,alpha=0.3 透明度30%(降低视觉干扰)# 网格线的位置与刻度位置对齐,帮助读者从数据标记点对到坐标轴数值
# ===== 图例 =====ax.legend(loc='upper right', frameon=False)# 【刻度配置】显示图例,loc='upper right' 放在右上角;# frameon=False 取消图例的边框矩形(更简洁)# 图例的每一项对应之前 plot/bar/scatter 时设置的 label 参数四、常见图型
# 1. 柱形图ax.bar(x, y, color='#3A86FF', width=0.6, edgecolor='black')# x: 每个柱子的 x 轴位置(类别标签的数值映射)# y: 每个柱子的高度# width=0.6: 柱宽(默认0.8),值越小柱子越窄、间距越大# edgecolor='black': 柱子边框颜色,使柱子边界清晰
# 2. 横向条形图ax.barh(y, x, height=0.6, color='#FF6B6B')# barh = horizontal bar,y 是类别位置,x 是对应数值(与 bar 的 x,y 互换)# height 控制条形的"厚度"(在横向图中相当于柱宽)
# 3. 直方图ax.hist(data, bins=20, density=False, edgecolor='black')# data: 一维原始数据数组,matplotlib 自动分箱统计频数# bins=20: 分成20个区间(柱子数量),bins 越大直方图越精细# density=False: y 轴显示频数(计数);density=True 则显示概率密度(总面积=1)
# 4. 箱线图ax.boxplot(data, labels=['A','B','C'], showfliers=True, notch=False)# data: 列表的列表,每个子列表是一组数据# showfliers=True: 显示离群点(用小圆圈标记超出1.5倍IQR的点)# notch=True: 中位数处做凹槽,凹槽不重叠表示中位数差异显著
# 5. 散点图ax.scatter(x, y, c=colors, s=sizes, alpha=0.6, cmap='viridis')# c: 每个点的颜色值(可以是单色字符串或数值数组,数值时配合 cmap 映射颜色)# s: 每个点的大小(标量或数组),单位是点的平方# alpha=0.6: 透明度,点重叠时能看出密度分布# 【cmap】cmap='viridis':将 c 的数值映射到 viridis 色带(蓝→绿→黄),是默认推荐的色盲友好 colormap
# 6. 气泡图(散点的s参数映射第三维)ax.scatter(x, y, s=z*100, c=z, cmap='coolwarm', alpha=0.7)# s=z*100: 用第三维 z 控制气泡大小,*100 是为了让点可见(z 值可能太小)# c=z: 同一维 z 也映射为颜色,实现"大小+颜色"双通道编码第三维# 【cmap】cmap='coolwarm':双向色带(蓝→白→红),适合有正负对比的数据(如相关系数)
# 7. 折线图ax.plot(x, y, color='red', linestyle='--', linewidth=2, marker='o', label='系列A')# linestyle='--': 虚线('-'实线, '-.'点划线, ':'点线)# marker='o': 数据点处画实心圆标记('s'方块, '^'三角, 'd'菱形, 'x'叉号)# label='系列A': 图例中显示的系列名称,需配合 ax.legend() 才能显示
# 8. 面积图ax.fill_between(x, y1, y2, alpha=0.3, color='skyblue')# y1 和 y2 之间的区域被填充颜色(y1 可以是全0数组,实现从x轴向上的填充)# alpha=0.3: 填充区域半透明,避免遮挡背后的线条或网格
# 9. 主题河流图ax.stackplot(x, y1, y2, y3, labels=['A','B','C'], baseline='wiggle')# stackplot: 堆叠面积图,y1,y2,y3 依次堆叠在彼此的顶部# baseline='wiggle': 关键参数!使整体围绕中心轴波动,形成"河流"的视觉效果# baseline='zero' 是从 y=0 向上堆叠(默认),baseline='sym' 是对称堆叠
# 10. 玫瑰图 / 雷达图(极坐标)fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})# subplot_kw={'projection': 'polar'}:在创建子图时传入,将坐标系从直角坐标转为极坐标# 极坐标下 x 轴变成角度(弧度),y 轴变成半径
ax.bar(theta, r, width=2*np.pi/N)# 玫瑰图:在极坐标上画柱状图,theta 是角度(弧度制),r 是半径长度# width 控制每个扇形的弧宽,N 是扇形数量,2π/N 让扇形刚好铺满360°
ax.fill(angles, values, alpha=0.25)# 雷达图填充:angles 是各轴的角度,values 是各轴的数据值# fill 将多边形内部填充颜色,alpha 控制透明度以显示网格五、颜色与 colormap
cmap 怎么使用的:colormap(颜色映射表,简称 cmap)是一种将数值映射到颜色的查找表。
c=数值数组+cmap='映射表名',matplotlib 自动完成映射:最小值→cmap最左颜色,最大值→cmap最右颜色。
- 顺序数据(温度、人口):
viridis(默认,色盲友好)、plasma、hot- 双向数据(相关系数、增长率,有正有负):
coolwarm、RdBu(红蓝)、bwr- 分类数据(离散类别):
Set1、tab10、Pastel1- 加
_r后缀反转颜色顺序(如viridis_r:黄→绿→蓝)cmap 配合
plt.colorbar()添加颜色条(图例),让读者知道颜色和数值的对应关系。
# ===== 三种颜色写法 =====color='red' # 命名颜色:matplotlib 内置140+种命名颜色('red','blue','green','orange'等)color='#FF5733' # 十六进制 HEX:6位,每2位分别表示 R G B (00-FF),与 CSS/Web 通用color=(0.2, 0.4, 0.6) # RGB 元组:每个通道 0.0~1.0 的浮点数(0.2=20%红, 0.4=40%绿, 0.6=60%蓝)
# ===== 常用 cmap 及选择场景 =====cmap='viridis' # 默认推荐,从蓝渐变到绿到黄,色盲友好(红绿色盲也能分辨),适合连续数值的默认选择cmap='coolwarm' # 双向色带:冷色(蓝)→ 中性(白/灰)→ 暖色(红),适合有正负值的数据如相关系数矩阵cmap='RdYlBu_r' # 红→黄→蓝 的反转(_r = reversed),适合降水量、风险等级等"红=高"直觉的数据cmap='hot' # 黑→红→黄→白,模仿热辐射,适合热力图、密度图cmap='plasma' # 深紫→粉→黄,替代 jet 的现代选择,感知均匀(perceptually uniform)cmap='Set1' # 9种鲜明颜色的离散色板,适合分类数据(柱状图多类别着色)cmap='tab10' # Tableau 的10色离散色板,比默认色更美观,适合折线图多系列区分
# ===== 添加 colorbar(颜色条/色阶图例) =====sc = ax.scatter(x, y, c=z, cmap='viridis', s=50)# 【cmap】scatter 返回一个 PathCollection 对象,它是 colorbar 的"数据源"# c=z:将数值数组 z 映射到 viridis 色带;z 的最小值→紫色,最大值→黄色
cbar = plt.colorbar(sc, ax=ax, label='强度', shrink=0.8)# 【cmap】colorbar 必须传入一个 mappable 对象(如 scatter/contourf/imshow 的返回值)# ax=ax:指定 colorbar 与哪个 Axes 对齐(宽度自动匹配该 Axes)# label='强度':colorbar 旁边显示的标签文字# shrink=0.8:缩小 colorbar 高度到80%,避免顶部超出图表
cbar.ax.tick_params(labelsize=8) # 【cmap】调整 colorbar 刻度标签字体大小cbar.set_ticks([0, 0.5, 1.0]) # 【cmap】手动设置 colorbar 的刻度位置cbar.set_ticklabels(['低', '中', '高']) # 【cmap】用中文覆盖 colorbar 刻度标签
# ===== 热力图的 cmap(imshow / pcolormesh) =====im = ax.imshow(matrix, cmap='coolwarm', aspect='auto', origin='upper')# 【cmap】imshow 显示二维矩阵作为图片,每个像素的颜色由 matrix[i,j] 经 cmap 映射# aspect='auto':自动拉伸填充 Axes;origin='upper':矩阵 [0,0] 在左上角plt.colorbar(im, ax=ax, label='值')
# ===== 等高线图的 cmap =====contour = ax.contourf(X, Y, Z, levels=20, cmap='viridis')# 【cmap】contourf 绘制填充等高线图,levels=20 表示20个层级,每层颜色由 cmap 映射 Z 值# 每个等高线区间对应 cmap 中的一段颜色,颜色过渡越平滑越好
# ===== 颜色归一化(控制 cmap 映射范围) =====from matplotlib.colors import Normalize, LogNormnorm = Normalize(vmin=0, vmax=100) # 线性映射:将 [0,100] 映射到 cmap 的 [起点, 终点]sc = ax.scatter(x, y, c=z, cmap='viridis', norm=norm)# vmin/vmax 直接在 scatter 中传也行:ax.scatter(..., vmin=0, vmax=100)
log_norm = LogNorm(vmin=1, vmax=1000) # 对数映射:适合数据跨度几个数量级的情况# 使用场景:数据从 1 到 1000,用线性映射时小值全挤到 cmap 一端,对数映射让低值区间也有颜色区分六、Seaborn
6.1 定义、加载与优势
Seaborn 是什么:Seaborn 是基于 matplotlib 构建的 Python 高级统计可视化库,由 Michael Waskom 开发。 它封装了 matplotlib 的底层绘图 API,提供面向 DataFrame 的高级接口,让统计图表能用一行代码完成。
import seaborn as sns # 加载 Seaborn,约定别名 sns(取自电视剧《白宫风云》角色 Samuel Norman Seaborn)Seaborn 相比裸 matplotlib 的优势:
| 优势 | 说明 | 对比 matplotlib |
|---|---|---|
| ① DataFrame 原生支持 | 直接传列名字符串(x='col1'),无需手动提取数组 | plt.plot(df['col1'], df['col2']) 需手动取列 |
| ② 自动分组着色 | hue='类别列' 一个参数完成分组+配色+图例 | 需手动循环分组、选色、拼图例 |
| ③ 统计功能内置 | 箱线图/小提琴图自动计算分位数;回归图自动拟合+置信区间 | 需手动调用 numpy/scipy 计算 |
| ④ 默认美观 | 自带精心设计的调色板和主题样式,开箱即用 | 默认配色朴素,需大量手动美化 |
| ⑤ Figure-level API | pairplot、clustermap、catplot 等自动创建多子图布局 | 需手动 subplots + 循环填充每个子图 |
6.2 风格特点
Seaborn 提供五大内置主题,通过
sns.set_theme()一键切换整体视觉风格。 主题控制的是背景色、网格线、刻度线等"画布层面"的样式,不改变数据本身的绘制逻辑。
import seaborn as sns
# ===== 主题风格(style) =====sns.set_theme(style='whitegrid') # 白底+灰色网格线(最常用,商务图表首选)sns.set_theme(style='darkgrid') # 灰底+白色网格线(适合深色背景的演示文稿)sns.set_theme(style='white') # 纯白底无网格(极简风格,适合海报/信息图)sns.set_theme(style='dark') # 纯灰底无网格(与 darkgrid 类似但无网格)sns.set_theme(style='ticks') # 白底+刻度短线(Edward Tufte 推荐风格,最小化非数据元素)
# ===== 调色板(palette) =====sns.set_theme(palette='deep') # 默认10色,饱和度高,适合分类数据sns.set_theme(palette='muted') # 低饱和度版本,视觉更柔和(推荐用于论文)sns.set_theme(palette='pastel') # 粉彩色系,适合浅色背景sns.set_theme(palette='bright') # 高亮度色系,适合深色背景sns.set_theme(palette='dark') # 深色系sns.set_theme(palette='colorblind')# 色盲友好色板(约8%男性色盲,学术/公开报告推荐)
# ===== 上下文缩放(context) =====sns.set_context('paper') # 论文用:小字体,适合嵌入 LaTeX/Wordsns.set_context('notebook') # 默认:适中字体,适合 Jupyter Notebooksns.set_context('talk') # 演讲用:大字体,适合投影演示sns.set_context('poster') # 海报用:超大字体,适合打印展板# context 会自动缩放所有文字元素(标签、刻度、图例)的字体大小
# ===== 一行设置全部 =====sns.set_theme(style='whitegrid', palette='muted', context='notebook', font='SimHei')# 同时设定:主题样式 + 调色板 + 上下文缩放 + 中文字体6.3 与 Matplotlib 的关系
关系:Seaborn 是 matplotlib 的高级封装,不是替代品。 Seaborn 内部所有绘图最终都调用 matplotlib 的底层 API,返回的仍然是 matplotlib 的
Axes对象。 这意味着:你可以用 Seaborn 快速出图,再用 matplotlib 的 API 做精细微调。
Seaborn(高层:DataFrame 直传、自动统计、默认美观) ↓ 调用Matplotlib(底层:像素级控制、坐标轴定制、图表保存)三种典型的协作模式:
import matplotlib.pyplot as pltimport seaborn as snsimport pandas as pd
df = pd.DataFrame({'x': range(5), 'y': [3,7,2,5,8]})
# ===== 模式1:Seaborn 画图 + Matplotlib 美化 =====fig, ax = plt.subplots(figsize=(8, 5)) # matplotlib 创建画布sns.barplot(x='x', y='y', data=df, ax=ax) # Seaborn 在指定 ax 上绘图ax.set_title('Seaborn画+Matplotlib美', fontsize=14) # matplotlib 微调标题ax.spines['top'].set_visible(False) # matplotlib 去边框ax.tick_params(labelsize=10) # matplotlib 调刻度plt.tight_layout() # matplotlib 调布局
# ===== 模式2:纯 Seaborn Figure-level 函数(自动管理画布) =====g = sns.pairplot(df) # pairplot 返回 PairGrid 对象g.fig.suptitle('散点矩阵', y=1.02) # 通过 .fig 访问底层 Figure 加总标题# PairGrid / FacetGrid 是 Seaborn 独有的高级对象,.fig 是底层的 matplotlib Figure# .axes 是底层的 matplotlib Axes 数组,可逐个微调
# ===== 模式3:Matplotlib 全局设置影响 Seaborn =====plt.rcParams['font.sans-serif'] = ['SimHei'] # matplotlib 的 rcParams 对 Seaborn 同样生效plt.rcParams['axes.unicode_minus'] = Falsesns.set_theme(style='whitegrid') # Seaborn 主题覆盖部分 rcParams# Seaborn 的 set_theme() 内部也是修改 rcParams,两者可混用但注意调用顺序:# 先设 rcParams(字体等),再设 set_theme(主题),后者会覆盖前者冲突的键API 层级对比:
| 层级 | matplotlib | Seaborn | 何时用 |
|---|---|---|---|
| Figure-level | plt.subplots() 手动创建 | sns.pairplot(), sns.catplot(), sns.displot(), sns.jointplot(), sns.clustermap() | 需要自动多子图布局时 |
| Axes-level | ax.plot(), ax.bar() | sns.barplot(), sns.boxplot(), sns.heatmap(), sns.lineplot(), sns.scatterplot(), sns.histplot() | 画到指定 ax,嵌入自定义布局时 |
| 样式层 | plt.rcParams | sns.set_theme(), sns.set_context() | 全局外观设置 |
6.4 相关图表 API
import seaborn as snsimport pandas as pd
# 假设 df 是一个包含多列的 DataFramedf = pd.DataFrame({ 'cat': ['A','A','B','B','C','C'], 'val': [23, 45, 56, 78, 32, 47], 'group': ['G1','G2','G1','G2','G1','G2'], 'gender': ['M','F','M','F','M','F'], 'x': [1,2,3,4,5,6], 'y': [3,7,2,5,8,4], 'z': [10,30,20,50,40,60], 'time': pd.date_range('2024-01-01', periods=6, freq='D'),})
# 1. 柱状图(自动计算均值+误差棒)sns.barplot(x='cat', y='val', data=df, ax=ax, palette='deep')# 自动对每个 cat 类别的 val 求均值,误差棒默认显示95%置信区间
# 2. 箱线图(自动计算四分位数)sns.boxplot(x='group', y='val', data=df, hue='gender', palette='Set2')# hue='gender':按性别分组,同一 x 位置并排显示两个箱线图
# 3. 小提琴图(箱线图 + 密度曲线)sns.violinplot(x='group', y='val', data=df, hue='gender', split=True)# split=True:两半各一组,便于直接对比分布形状
# 4. 直方图 + KDE 密度曲线sns.histplot(data=df, x='val', bins=20, kde=True, hue='gender', alpha=0.5)# kde=True:叠加核密度估计平滑曲线;hue 分组后透明叠加便于对比
# 5. 散点图(支持 size/hue/style 三维编码)sns.scatterplot(x='x', y='y', size='z', hue='cat', style='gender', data=df, sizes=(20, 200))# size='z':z 列控制点大小;hue='cat':类别列着色;style='gender':不同标记形状
# 6. 热力图corr = df.select_dtypes('number').corr() # 计算数值列的相关系数矩阵sns.heatmap(corr, annot=True, fmt='.2f', cmap='coolwarm', vmin=-1, vmax=1, square=True, linewidths=0.5, cbar_kws={'shrink': 0.8, 'label': '相关系数'})# 【cmap】cmap='coolwarm':蓝(-1)→白(0)→红(+1),相关系数黄金配色
# 7. 折线图(自动按 hue 分组,带置信区间)sns.lineplot(x='time', y='val', hue='cat', data=df, style='cat', markers=True)# 默认显示各时间点的均值±95%置信区间半透明带
# 8. 散点矩阵图(Figure-level,自动创建 N×N 子图网格)sns.pairplot(df, hue='cat', diag_kind='kde', corner=True, palette='deep')# 【子图】自动为每对数值列创建散点图矩阵;对角线显示 KDE 密度曲线# corner=True:只显示左下三角,去重+省空间七、Pyecharts
7.1 定义、加载与特点
Pyecharts 是什么:Pyecharts 是百度开源 JavaScript 图表库 Apache ECharts 的 Python 封装, 由陈键冬等人开发维护。它将 ECharts 的配置项映射为 Python 的链式 API,让你用纯 Python 代码生成交互式网页图表。 与 matplotlib/Seaborn 生成静态图片不同,Pyecharts 输出的是 HTML 文件,内嵌 JavaScript,支持缩放、悬停提示、数据筛选等交互。
pip install pyecharts # 安装 Pyecharts(库,包含常用图表)# ===== 加载方式:按需导入,不导入整个库 =====from pyecharts.charts import Bar # 柱状图from pyecharts.charts import Line # 折线图from pyecharts.charts import Pie # 饼图from pyecharts.charts import Scatter # 散点图from pyecharts.charts import HeatMap # 热力图from pyecharts.charts import Map # 地图from pyecharts.charts import Geo # 地理坐标系图from pyecharts.charts import Graph # 关系图/网络图from pyecharts.charts import Sankey # 桑基图from pyecharts.charts import Radar # 雷达图from pyecharts.charts import Funnel # 漏斗图from pyecharts.charts import Gauge # 仪表盘from pyecharts.charts import Liquid # 水球图from pyecharts.charts import WordCloud # 词云图from pyecharts.charts import TreeMap # 矩形树图from pyecharts.charts import Sunburst # 旭日图from pyecharts.charts import Timeline # 时间线轮播(容器)
from pyecharts import options as opts # 全局配置项模块(标题、图例、工具箱、提示框等)# opts 是最关键的模块,几乎所有图表属性都通过 opts.XXXOpts 类来配置Pyecharts 的特点:
| 特点 | 说明 | 对比 matplotlib/Seaborn |
|---|---|---|
| ① 交互式 | 输出 HTML,支持鼠标悬停查看数值、缩放、拖拽、图例点击过滤 | matplotlib 生成静态图片,无交互 |
| ② 图表类型丰富 | 覆盖 30+ 种图表类型,包括地图、桑基图、仪表盘、3D 图等 | matplotlib 偏基础统计图,地图/桑基图需额外库 |
| ③ 链式调用 | .add_xaxis().add_yaxis().set_global_opts() 流式 API 链式配置 | matplotlib 是函数调用式,每个属性单独设置 |
| ④ 配置项体系 | 全局配置 set_global_opts() + 系列配置 set_series_opts(),分层清晰 | matplotlib 所有属性都在 ax 上调,层级扁平 |
| ⑤ 网页嵌入 | 生成的 HTML 可在浏览器中独立运行,也可嵌入 Flask/Django/Jupyter | matplotlib 图片需嵌入 <img> 标签 |
7.2 生成图表的主要步骤
Pyecharts 的图表创建遵循五步流程:初始化图表 → 添加数据 → 设置全局配置 → 设置系列配置 → 渲染输出。 所有图表类型共享相同的配置模式,学会一个就等于学会全部。
from pyecharts.charts import Barfrom pyecharts import options as opts
# ===== 第1步:初始化图表对象 =====bar = Bar() # 创建柱状图实例(无参数,或传入 init_opts 设定宽高/主题)
# 也可在初始化时指定画布大小和主题:bar = Bar(init_opts=opts.InitOpts( width='900px', # 画布宽度(支持 px / %) height='500px', # 画布高度 theme='light', # 内置主题:'light','dark','white','chalk','essos'等 # 更多主题需安装:pip install pyecharts-snapshot))
# ===== 第2步:添加数据 =====bar.add_xaxis(['苹果', '香蕉', '橙子', '葡萄', '西瓜'])# 设置 x 轴类别标签(所有系列共享同一 x 轴)
bar.add_yaxis('2024年销量', [120, 200, 150, 80, 70])# 添加一个系列:series_name='2024年销量',数据列表与 x 轴一一对应# 可多次调用 add_yaxis 添加多个系列,实现分组对比
bar.add_yaxis('2025年销量', [150, 220, 180, 90, 85])# 第二个系列:自动并排显示
# ===== 第3步:设置全局配置(标题、图例、工具箱、提示框、坐标轴等) =====bar.set_global_opts( title_opts=opts.TitleOpts( title='水果销量对比', # 主标题文字 subtitle='2024 vs 2025', # 副标题 pos_left='center', # 标题水平位置:'center' / 'left' / 'right' / 百分比 ), legend_opts=opts.LegendOpts( pos_top='8%', # 图例距顶部的距离 orient='horizontal', # 横向排列('vertical' 纵向) ), toolbox_opts=opts.ToolboxOpts( is_show=True, # 显示右上角工具箱(下载图片、数据视图、缩放等) feature={ 'saveAsImage': {}, # 启用"保存为图片"按钮 'dataView': {'readOnly': False}, # 启用"数据视图"按钮(可编辑) 'restore': {}, # 启用"还原"按钮 'dataZoom': {}, # 启用"区域缩放"按钮 }, ), tooltip_opts=opts.TooltipOpts( trigger='axis', # 触发方式:'axis'(坐标轴触发,显示所有系列) / 'item'(数据项触发) axis_pointer_type='shadow', # 指示器类型:'line','shadow','cross' ), xaxis_opts=opts.AxisOpts( name='水果种类', # x 轴名称 axislabel_opts=opts.LabelOpts(rotate=30),# x 轴标签旋转30° ), yaxis_opts=opts.AxisOpts( name='销量(吨)', # y 轴名称 splitline_opts=opts.SplitLineOpts(is_show=True), # 显示水平分割线 ),)
# ===== 第4步:设置系列配置(标签、标记点、颜色等——作用于数据系列层面) =====bar.set_series_opts( label_opts=opts.LabelOpts( is_show=True, # 在柱顶显示数值标签 position='top', # 标签位置:'top','inside','bottom' font_size=12, ), markpoint_opts=opts.MarkPointOpts( data=[ opts.MarkPointItem(type_='max', name='最大值'), # 标记最大值点 opts.MarkPointItem(type_='min', name='最小值'), # 标记最小值点 ], ), markline_opts=opts.MarkLineOpts( data=[ opts.MarkLineItem(type_='average', name='平均值'), # 标记平均值线 ], ),)
# ===== 第5步:渲染输出 =====bar.render('bar_chart.html') # 生成独立 HTML 文件,浏览器打开即可查看交互图表# 在 Jupyter Notebook 中可直接用 bar.render_notebook() 内嵌显示# bar 对象本身在 Jupyter 中也会自动渲染五步流程总结:
① Bar() / Line() / Pie() ... ↓② .add_xaxis() + .add_yaxis() ↓③ .set_global_opts() ← 标题/图例/工具箱/提示框/坐标轴(跨系列共用) ↓④ .set_series_opts() ← 标签/标记点/标记线(按系列设置) ↓⑤ .render('output.html')7.3 可生成的图表类型
Pyecharts 继承 ECharts 的全部图表能力,分为以下几大类。
一类:基础统计图(与 matplotlib/Seaborn 重叠,但交互式)
from pyecharts.charts import Bar, Line, Pie, Scatter, Boxplot, HeatMap
# ===== 柱状图 =====bar = Bar()bar.add_xaxis(['Q1', 'Q2', 'Q3', 'Q4'])bar.add_yaxis('营收', [120, 200, 150, 180])bar.set_global_opts(title_opts=opts.TitleOpts(title='柱状图'))# 支持:普通柱状图、堆叠柱状图(stack_='stack1')、条形图(.reversal_axis())
# ===== 折线图(自带平滑和面积填充) =====line = Line()line.add_xaxis(['1月', '2月', '3月', '4月', '5月'])line.add_yaxis('温度', [5, 12, 18, 25, 30], is_smooth=True, areastyle_opts=opts.AreaStyleOpts(opacity=0.3))# is_smooth=True:贝塞尔曲线平滑;areastyle_opts 填充折线下方区域成为面积图
# ===== 饼图/环形图 =====pie = Pie()pie.add('品类占比', [list(z) for z in zip(['A','B','C','D'], [335, 310, 234, 135])], radius=['40%', '75%']) # 内半径40%、外半径75% → 环形图;radius='70%' → 普通饼图# 数据格式必须是 [(key1, val1), (key2, val2), ...]
# ===== 散点图(支持视觉映射 visualmap) =====scatter = Scatter()scatter.add_xaxis([10, 20, 30, 40, 50])scatter.add_yaxis('系列A', [5, 20, 36, 10, 75], symbol_size=20)scatter.set_global_opts( visualmap_opts=opts.VisualMapOpts(max_=100), # 视觉映射组件,类似 matplotlib 的 colorbar)
# ===== 箱线图 =====box = Boxplot()box.add_xaxis(['对照组', '实验组'])box.add_yaxis('', box.prepare_data([[10,20,30,40,50,60], [15,25,35,45,55,65]]))# 注意:数据需用 .prepare_data() 预处理
# ===== 热力图 =====heat = HeatMap()heat.add_xaxis(['周一','周二','周三','周四','周五'])heat.add_yaxis('上午', [0,1,2,3,4], [[i, j, np.random.randint(0, 100)] for i in range(5) for j in range(5)])# 数据格式必须是 [[x_idx, y_idx, value], ...]二类:地理可视化(Pyecharts 最大亮点之一)
from pyecharts.charts import Map, Geo
# ===== 中国地图(内置中国省级/市级地理数据) =====map_chart = Map()map_chart.add('确诊人数', [['广东', 1200], ['浙江', 1100], ['河南', 980], ['四川', 850], ['北京', 600]], maptype='china') # maptype='china' 中国地图;也可填省份名如 '广东'map_chart.set_global_opts( title_opts=opts.TitleOpts(title='中国疫情地图'), visualmap_opts=opts.VisualMapOpts( max_=1500, # 颜色映射的最大值 is_piecewise=True, # 分段型视觉映射(离散色块) pieces=[ {'min': 1000, 'label': '>1000', 'color': '#B40404'}, {'min': 500, 'max': 999, 'label': '500-999', 'color': '#E41A1C'}, {'min': 100, 'max': 499, 'label': '100-499', 'color': '#FC8D59'}, {'min': 0, 'max': 99, 'label': '<100', 'color': '#FEE5D9'}, ], ),)
# ===== Geo 地理坐标系(散点+涟漪效果在地图上) =====geo = Geo()geo.add_schema(maptype='china') # 加载中国地图底图geo.add('城市分布', [('北京', 120), ('上海', 110), ('广州', 90), ('深圳', 85), ('杭州', 70)], type_='effectScatter', # 涟漪散点效果 symbol_size=15,)geo.set_global_opts( title_opts=opts.TitleOpts(title='Geo 地理散点'), visualmap_opts=opts.VisualMapOpts(is_show=False), # 不显示视觉映射条)# type_ 可选:'scatter'(散点), 'effectScatter'(涟漪), 'heatmap'(热力)三类:关系与流程类
from pyecharts.charts import Graph, Sankey, TreeMap, Sunburst
# ===== 关系图/网络图 =====graph = Graph()graph.add('人物关系', nodes=[{'name': 'A', 'symbolSize': 50}, {'name': 'B', 'symbolSize': 30}, {'name': 'C', 'symbolSize': 40}], links=[{'source': 'A', 'target': 'B'}, {'source': 'A', 'target': 'C'}, {'source': 'B', 'target': 'C'}], layout='force', # 力引导布局('circular' 环形布局) repulsion=8000, # 节点斥力(力布局参数))
# ===== 桑基图(流向图) =====sankey = Sankey()sankey.add('能量流向', nodes=[{'name': '煤炭'}, {'name': '发电'}, {'name': '居民用电'}, {'name': '工业用电'}, {'name': '损耗'}], links=[{'source': '煤炭', 'target': '发电', 'value': 100}, {'source': '发电', 'target': '居民用电', 'value': 40}, {'source': '发电', 'target': '工业用电', 'value': 50}, {'source': '发电', 'target': '损耗', 'value': 10}], linestyle_opt=opts.LineStyleOpts(opacity=0.5, curve=0.5),)
# ===== 矩形树图(层级占比) =====treemap = TreeMap()treemap.add('市场份额', [{'value': 40, 'name': '品牌A'}, {'value': 30, 'name': '品牌B'}, {'value': 20, 'name': '品牌C'}, {'value': 10, 'name': '其他'}],)
# ===== 旭日图(多层级的饼图) =====sunburst = Sunburst()sunburst.add('部门结构', [{'name': 'CEO', 'children': [ {'name': '技术部', 'children': [ {'name': '前端', 'value': 20}, {'name': '后端', 'value': 30}]}, {'name': '市场部', 'value': 50}]}],)四类:特殊图表
from pyecharts.charts import Gauge, Liquid, WordCloud, Radar, Timeline
# ===== 仪表盘 =====gauge = Gauge()gauge.add('完成率', [('完成率', 78.5)], min_=0, max_=100, detail_label_opts=opts.LabelOpts(formatter='{value}%'))# 适合 KPI 看板
# ===== 水球图(进度球) =====liquid = Liquid()liquid.add('完成度', [0.68, 0.55], # 多圈叠加(可从外到内显示多圈) shape='circle') # 'circle','diamond','rect','roundRect','triangle'# 适合展示百分比指标
# ===== 词云图 =====wordcloud = WordCloud()wordcloud.add('关键词', [('数据可视化', 100), ('Python', 80), ('matplotlib', 60), ('Seaborn', 50), ('Pyecharts', 70)], shape='circle', # 词云形状:'circle','cardioid','diamond','triangle' word_size_range=[20, 100], # 字号范围)
# ===== 雷达图 =====radar = Radar()radar.add_schema(schema=[ opts.RadarIndicatorItem(name='速度', max_=100), opts.RadarIndicatorItem(name='力量', max_=100), opts.RadarIndicatorItem(name='技巧', max_=100), opts.RadarIndicatorItem(name='防守', max_=100), opts.RadarIndicatorItem(name='体能', max_=100),])radar.add('球员A', [[85, 70, 90, 60, 80]]) # 数据需两层列表radar.add('球员B', [[70, 90, 65, 85, 75]])
# ===== 时间线轮播(将多个图表串联成动画) =====timeline = Timeline()for year in ['2020', '2021', '2022', '2023']: bar = Bar() bar.add_xaxis(['A', 'B', 'C']) bar.add_yaxis(year, [np.random.randint(100) for _ in range(3)]) timeline.add(bar, year) # 每年一个图表帧timeline.add_schema(is_auto_play=True, play_interval=1500) # 自动播放,每帧1.5秒# Timeline 是一个容器,可以将任何图表作为帧放入7.4 Pyecharts 图表类型速查总表
| 分类 | 图表 | 导入路径 | 典型场景 |
|---|---|---|---|
| 基础 | 柱状图 | from pyecharts.charts import Bar | 类别对比 |
| 基础 | 折线图 | from pyecharts.charts import Line | 趋势变化 |
| 基础 | 饼图/环形图 | from pyecharts.charts import Pie | 占比分布 |
| 基础 | 散点图 | from pyecharts.charts import Scatter | 相关性 |
| 基础 | 箱线图 | from pyecharts.charts import Boxplot | 分布对比 |
| 基础 | 热力图 | from pyecharts.charts import HeatMap | 矩阵数据 |
| 地理 | 地图 | from pyecharts.charts import Map | 区域数据 |
| 地理 | 地理坐标系 | from pyecharts.charts import Geo | 地理散点 |
| 关系 | 关系图 | from pyecharts.charts import Graph | 网络拓扑 |
| 关系 | 桑基图 | from pyecharts.charts import Sankey | 流量流向 |
| 关系 | 漏斗图 | from pyecharts.charts import Funnel | 转化率 |
| 层级 | 矩形树图 | from pyecharts.charts import TreeMap | 层级占比 |
| 层级 | 旭日图 | from pyecharts.charts import Sunburst | 多层饼图 |
| 特殊 | 仪表盘 | from pyecharts.charts import Gauge | KPI 指标 |
| 特殊 | 水球图 | from pyecharts.charts import Liquid | 进度百分比 |
| 特殊 | 词云图 | from pyecharts.charts import WordCloud | 文本权重 |
| 特殊 | 雷达图 | from pyecharts.charts import Radar | 多维对比 |
| 容器 | 时间线轮播 | from pyecharts.charts import Timeline | 动态播放 |
7.5 Pyecharts vs Matplotlib/Seaborn 对比
| 维度 | Pyecharts | Matplotlib/Seaborn |
|---|---|---|
| 输出格式 | HTML(交互式) | PNG/SVG/PDF(静态) |
| 交互性 | 悬停、缩放、拖拽、图例筛选 | 无(除非用 %matplotlib widget) |
| 学习曲线 | 中等(需理解 opts 配置体系) | 中等(需理解 figure/axes 对象) |
| 图表类型 | 30+ 种,含地图/桑基/仪表等 | 基础统计图为主 |
| 中文支持 | 原生完美支持 | 需额外配置字体 |
| 网页嵌入 | 天然适配,一个 HTML 搞定 | 需先保存图片再嵌入 |
| 论文/出版 | 不支持(无法导出矢量图) | 完美支持(PDF/SVG 矢量输出) |
| 适用场景 | Web 看板、数据分析报告、交互仪表盘 | 学术论文、出版物、静态报告 |
八、收尾区
plt.tight_layout(pad=1.5, h_pad=1.0, w_pad=1.0)# 【子图】自动调整子图间距和边距;pad 控制 Figure 边缘与子图间的距离(英寸)# h_pad/w_pad 控制子图之间的垂直/水平间距
plt.savefig('out.png', dpi=300, bbox_inches='tight', facecolor='white', transparent=False)# dpi=300:输出分辨率300 DPI(出版级),图片像素 = figsize × dpi# bbox_inches='tight':自动裁剪多余的空白边缘,让图片内容紧凑(最常用)# bbox_inches=None 则保留完整画布(含白边);也可手动指定 bbox_inches=(-0.5,-0.5,8.5,5.5)# facecolor='white':图片背景色设为白色;transparent=True 则背景透明(适合叠加到海报/网页)
plt.show()# 显示所有已创建的 Figure;在 Jupyter 中通常自动显示,脚本中必须显式调用九、模板
import numpy as npimport matplotlib.pyplot as pltimport seaborn as sns
# ===== 1. 全局配置 =====plt.rcParams['font.sans-serif'] = ['SimHei'] # 【字体配置】全局中文字体plt.rcParams['axes.unicode_minus'] = False # 【字体配置】负号正常显示plt.rcParams['figure.dpi'] = 100 # 全局分辨率
# ===== 2. 数据 =====np.random.seed(42)x = np.arange(10)y1 = np.random.rand(10) * 100y2 = np.random.rand(10) * 100z_color = np.random.rand(10) # 用于 cmap 映射的第三维数据z_size = np.random.rand(10) * 500 # 用于气泡大小的第三维数据
# ===== 3. 画布与子图 =====fig, axes = plt.subplots(nrows=2, ncols=2, figsize=(12, 10))# 【子图】创建2行2列共4个子图,axes[0,0]~axes[1,1]
# ===== 4. 子图1:折线图(演示刻度配置) =====ax1 = axes[0, 0]ax1.plot(x, y1, color='#3A86FF', marker='o', linewidth=2, label='系列A')ax1.set_xlabel('时间', fontsize=12) # 【刻度配置】x轴标签ax1.set_ylabel('数值', fontsize=12) # 【刻度配置】y轴标签ax1.set_title('折线图示例', fontsize=14, fontweight='bold')ax1.set_xticks(x) # 【刻度配置】每个数据点都有刻度ax1.set_xticklabels([f'第{i}天' for i in range(10)], rotation=45)# 【刻度配置】自定义中文刻度标签+旋转45°防止重叠ax1.tick_params(axis='both', labelsize=9) # 【刻度配置】统一调整刻度字号ax1.spines['top'].set_visible(False) # 去顶边框ax1.spines['right'].set_visible(False) # 去右边框ax1.legend(loc='best', frameon=False)ax1.grid(True, linestyle='--', alpha=0.3)
# ===== 5. 子图2:柱状图(演示字体配置) =====ax2 = axes[0, 1]bars = ax2.bar(x, y2, color='#FF6B6B', width=0.6, edgecolor='white')ax2.set_title('柱状图 - 中文字体测试', fontsize=14) # 【字体配置】标题用 SimHei 中文显示ax2.set_xlabel('类别', fontsize=12) # 【字体配置】轴标签中文显示ax2.set_ylabel('销售额(万元)', fontsize=12) # 【字体配置】y轴标签含中文+特殊符号ax2.spines['top'].set_visible(False)ax2.spines['right'].set_visible(False)# 在柱顶加数值标签for bar in bars: height = bar.get_height() ax2.text(bar.get_x() + bar.get_width()/2., height + 1, f'{height:.1f}', ha='center', va='bottom', fontsize=9) # 【字体配置】ax.text 的 fontsize 控制标注文字大小
# ===== 6. 子图3:气泡图(演示 cmap 使用) =====ax3 = axes[1, 0]sc = ax3.scatter(x, y1, c=z_color, s=z_size, cmap='viridis', alpha=0.7, edgecolors='gray', linewidth=0.5)# 【cmap】c=z_color:z_color 数组的每个值通过 cmap='viridis' 映射为颜色# 【cmap】s=z_size:z_size 控制每个气泡的大小,实现"大小+颜色"双通道第三维编码ax3.set_title('气泡图 - cmap 演示', fontsize=14)ax3.set_xlabel('X 轴')ax3.set_ylabel('Y 轴')cbar = plt.colorbar(sc, ax=ax3, label='颜色强度', shrink=0.85)# 【cmap】colorbar 将数值→颜色的映射关系可视化,shrink 缩小高度避免超出cbar.ax.tick_params(labelsize=8)
# ===== 7. 子图4:热力图(演示 cmap 双向映射) =====ax4 = axes[1, 1]corr = np.corrcoef(np.random.randn(5, 100)) # 5×5 相关系数矩阵(-1到1)sns.heatmap(corr, annot=True, fmt='.2f', cmap='coolwarm', vmin=-1, vmax=1, square=True, linewidths=0.5, cbar_kws={'shrink': 0.8, 'label': '相关系数'}, ax=ax4)# 【cmap】cmap='coolwarm':蓝(-1)→白(0)→红(+1),正向暖色负向冷色,相关系数可视化的黄金选择# 【cmap】vmin=-1, vmax=1:锁定 cmap 映射范围,确保0始终映射到白色ax4.set_title('相关系数热力图', fontsize=14)
# ===== 8. 收尾 =====plt.tight_layout(pad=2.0) # 【子图】统一调整所有子图间距plt.savefig('demo.png', dpi=300, bbox_inches='tight')plt.show()分享到社交平台
将本文分享给你的朋友们