公司动态
Matplotlib数据可视化实战:从基础图表到高级定制化技巧
1. 项目概述为什么你需要一个“能看懂”的Matplotlib指南如果你刚开始用Python做数据分析或者科学计算十有八九会碰到Matplotlib这个库。官方文档很全但有时候看完了还是不知道怎么下手参数多到眼花画出来的图总觉得哪里不对劲离“好看”和“专业”差那么一口气。网上的教程要么太浅画个折线图就结束了要么直接上高级用法看得云里雾里。这个内容就是想解决这个问题——让你真の能看懂。我不是要复述官方手册而是把我自己从新手到熟练踩过的坑、总结的技巧用最直白的话讲清楚。Matplotlib的核心其实就两块画什么图表种类和怎么画得好看样式调整。很多人卡在第二块因为第一块没学透。所以我们会从最基础的图表种类出发把每一种图“为什么用”、“怎么用”、“有哪些坑”都掰开揉碎顺便把调整样式那些最实用的技巧穿插进去。等你跟着走一遍再去看那些复杂的组合图、自定义样式就会觉得“哦原来就是这么回事”。适合谁看就是正在学Python数据分析、需要做报告、写论文或者任何需要把数据变成直观图表的人。你不用是编程高手但最好对Python基础语法和列表、字典这些数据结构有点了解。我们的目标很简单看完之后你能 confidently自信地用Matplotlib画出工作中90%需要的图表并且知道怎么让它看起来更专业。2. 核心思路理解Matplotlib的“图层”与“对象”哲学在动手画图之前得先搞明白Matplotlib是怎么工作的。很多人一开始就用plt.plot()画图然后plt.title()加标题觉得挺简单。但一旦想调个细节比如把某个坐标轴的颜色改一下或者在同一个画布上并排放两个不一样的图就懵了。问题的根源在于没有理解Matplotlib的面向对象OO和状态机Stateful两套API的区别。简单来说状态机接口就是pyplot模块通常导入为plt它保持一个“当前图形”和“当前坐标轴”的状态。你调plt.plot()它就在当前坐标轴上画线你再调plt.xlabel()它就给当前坐标轴加标签。这套接口对于快速交互和简单的脚本非常方便代码写起来像在“发布命令”。而面向对象接口则是更推荐、也更强大的方式。它明确地创建和操作图形Figure和坐标轴Axes对象。你可以把它想象成画画Figure就是你的画布Axes就是画布上的一个子图可以包含坐标轴、刻度、数据区域。一个画布上可以有多个子图。为什么强烈建议从OO接口开始学因为它逻辑清晰可控性强。当你拿到一个ax对象后所有关于这个子图的设置比如ax.set_xlabel(),ax.set_title(),ax.plot()都是作用在这个明确的对象上不会和其他图搞混。这对于画子图、做精细调整至关重要。一个最基础的OO模式代码框架长这样import matplotlib.pyplot as plt import numpy as np # 1. 创建图形和坐标轴对象 fig, ax plt.subplots() # 创建一个画布和一个子图 # 2. 准备数据 x np.linspace(0, 10, 100) y np.sin(x) # 3. 在坐标轴ax上绘图 ax.plot(x, y, labelsin(x)) # 这里用的是ax.plot不是plt.plot # 4. 设置这个坐标轴的属性 ax.set_xlabel(X Axis) ax.set_ylabel(Y Axis) ax.set_title(A Simple Sine Wave) ax.legend() # 5. 显示图形 plt.show()注意plt.subplots()是创建图形和坐标轴最常用的方法。即使你只画一个图也养成用这个习惯。fig代表整个图形窗口ax代表那个唯一的子图。后续所有绘图和设置操作都基于ax进行。理解了这套“画布-子图”的对象体系我们再去看各种图表种类就会明白所谓“画散点图”、“画柱状图”本质上就是在Axes对象上调用不同的绘图方法如ax.scatter(),ax.bar()并传入对应的数据。每种方法有自己的专属参数来控制样式。接下来我们就进入实战逐一拆解。3. 基础图表种类详解与避坑指南这一部分我们聚焦最常用、也最容易出错的几种基础图表。我会给出最精简的代码模板并重点讲解那些容易忽略但至关重要的参数以及我踩过的坑。3.1 折线图不只是plot一下那么简单折线图大概是所有人第一个学会的图用于展示数据随时间或其他连续变量的变化趋势。基础画法fig, ax plt.subplots(figsize(8, 5)) # figsize控制图形宽高单位英寸 ax.plot(x, y, colorsteelblue, linewidth2, markero, markersize8, labelTrend) ax.set_xlabel(Time (s)) ax.set_ylabel(Value) ax.set_title(Data Trend Over Time) ax.legend() ax.grid(True, linestyle--, alpha0.7) # 添加网格线虚线半透明关键参数解析与避坑color/c: 颜色。别只用‘r‘, ‘g‘, ‘b‘。Matplotlib有丰富的 命名颜色 如‘steelblue‘, ‘coral‘, ‘slategray‘看起来更专业。也可以用十六进制如‘#2E86AB‘。linewidth/lw: 线宽。默认1.5有时在出版物上显得细可以调到2。marker: 数据点标记。‘o‘圆点‘s‘方块‘^‘上三角。常见坑当数据点很多时加上marker会让图变得杂乱不堪此时应该去掉marker或设置markersize0。label: 图例标签。务必记得在最后调用ax.legend()否则标签不显示。figsize: 这是一个Figure级别的参数。如果你发现图上的字挤在一起或者线太密首先应该调整figsize给图形更多空间。通常用(width, height)的元组指定。实操心得处理时间序列当X轴是时间日期时直接传入datetime对象Matplotlib可以自动处理刻度标签。但默认格式可能不好看。import pandas as pd # 假设df是一个DataFrame有‘date‘和‘price‘两列 df[‘date‘] pd.to_datetime(df[‘date‘]) fig, ax plt.subplots(figsize(10,6)) ax.plot(df[‘date‘], df[‘price‘]) # 优化日期刻度 import matplotlib.dates as mdates ax.xaxis.set_major_formatter(mdates.DateFormatter(‘%Y-%m‘)) # 格式化为年-月 ax.xaxis.set_major_locator(mdates.MonthLocator(interval3)) # 每3个月一个主刻度 fig.autofmt_xdate() # 自动旋转日期标签避免重叠这个技巧能让你的时间序列图立刻变得清晰专业。3.2 散点图揭示关系与分布散点图用于展示两个连续变量之间的关系或者观察数据的分布情况。这是探索性数据分析EDA的利器。基础画法fig, ax plt.subplots() # s: 点的大小 c: 点的颜色 alpha: 透明度 scatter ax.scatter(x_data, y_data, s50, c‘orange‘, edgecolors‘black‘, linewidth0.5, alpha0.7) ax.set_xlabel(‘Feature X‘) ax.set_ylabel(‘Feature Y‘) ax.set_title(‘Scatter Plot of X vs Y‘)进阶技巧用颜色和大小传递更多信息散点图的强大之处在于点的大小s和颜色c可以映射到第三个甚至第四个变量。s可以是一个标量所有点一样大也可以是一个和x、y长度相同的数组表示每个点的大小。c同理。# 假设我们有四个数组x, y, category类别用于颜色, value数值用于大小 categories np.random.randint(0, 3, 100) # 0,1,2 三个类别 values np.random.rand(100) * 200 # 大小值 fig, ax plt.subplots() # c参数传入类别数组cmap指定颜色映射norm将类别整数归一化到颜色映射范围 scatter ax.scatter(x, y, svalues, ccategories, cmap‘viridis‘, alpha0.8, edgecolors‘w‘, linewidth0.5) # 添加颜色条 plt.colorbar(scatter, axax, label‘Category‘) # 添加图例对于大小比较麻烦通常用标注代替 ax.set_xlabel(‘X‘) ax.set_ylabel(‘Y‘) ax.set_title(‘Bubble Chart: SizeValue, ColorCategory‘)注意当c是数值数组时默认使用‘viridis‘色彩映射。你可以通过cmap参数更改如‘plasma‘, ‘coolwarm‘, ‘RdYlBu‘。edgecolors给点加上边框如白色可以让点在深色背景下更清晰linewidth控制边框粗细。常见问题散点重叠Overplotting当数据点很多且密集时散点会大量重叠看不出分布密度。解决方法减小点大小和增加透明度s10, alpha0.3。使用蜂群图或抖动给数据添加少量随机噪声抖动避免完全重叠。可以用seaborn库的swarmplot或stripplot或者手动给xy加一点随机小数。使用二维直方图hexbin或密度图对于极大量数据ax.hexbin()或seaborn.kdeplot更能反映密度分布。3.3 柱状图比较类别间的差异柱状图用于比较不同类别之间的数值大小。分为普通柱状图ax.bar()和水平柱状图ax.barh()。基础画法垂直categories [‘A‘, ‘B‘, ‘C‘, ‘D‘] values [25, 40, 30, 35] fig, ax plt.subplots() bars ax.bar(categories, values, color[‘skyblue‘, ‘lightgreen‘, ‘salmon‘, ‘gold‘]) ax.set_ylabel(‘Score‘) ax.set_title(‘Performance by Category‘) # 在柱子上方添加数值标签 for bar in bars: height bar.get_height() ax.text(bar.get_x() bar.get_width()/2., height 0.5, f‘{height}‘, ha‘center‘, va‘bottom‘, fontsize10)关键技巧与避坑颜色可以传入一个颜色列表实现每个柱子不同颜色。使用和谐的颜色组合如tab10色彩映射比随机颜色更好。宽度width参数控制柱子宽度默认0.8。如果类别很多可以适当调小避免拥挤。标签如代码所示用循环和ax.text()添加数据标签是标准做法。ha‘center‘水平居中和va‘bottom‘垂直底部对齐是常用组合。0.5是为了让标签在柱子顶端上方一点。X轴标签如果类别名称很长它们可能会重叠。使用ax.set_xticklabels(categories, rotation45, ha‘right‘)来旋转标签并右对齐能有效改善可读性。分组柱状图这是最易出错的地方之一。分组柱状图的核心思想是控制每个组和每个柱子的X轴位置。import numpy as np labels [‘G1‘, ‘G2‘, ‘G3‘, ‘G4‘] men_means [20, 35, 30, 35] women_means [25, 32, 34, 20] x np.arange(len(labels)) # 标签位置: [0, 1, 2, 3] width 0.35 # 柱子的宽度 fig, ax plt.subplots() rects1 ax.bar(x - width/2, men_means, width, label‘Men‘, color‘royalblue‘) rects2 ax.bar(x width/2, women_means, width, label‘Women‘, color‘lightcoral‘) # 设置x轴刻度位置和标签 ax.set_xticks(x) ax.set_xticklabels(labels) ax.set_ylabel(‘Scores‘) ax.set_title(‘Scores by group and gender‘) ax.legend()原理np.arange(len(labels))生成了类别的位置索引0,1,2,3。第一组柱子Men的中心放在x - width/2第二组Women放在x width/2。这样两组柱子就并排在了每个类别标签的两侧。width控制每组柱子的宽度。理解了这个“手动定位”的逻辑你就能画出任意多组的并列柱状图。3.4 直方图与密度图查看数据分布直方图将连续数据分箱显示每个箱内数据点的频数是了解数据分布形状是否正态、偏斜等的基本工具。基础画法data np.random.randn(1000) # 生成1000个标准正态分布随机数 fig, ax plt.subplots() # bins: 箱子数量 edgecolor: 箱子边框颜色 alpha: 透明度 n, bins, patches ax.hist(data, bins30, color‘lightblue‘, edgecolor‘black‘, alpha0.7) ax.set_xlabel(‘Value‘) ax.set_ylabel(‘Frequency‘) ax.set_title(‘Histogram of Normal Distribution‘) # 可以添加一条密度曲线KDE from scipy.stats import gaussian_kde kde gaussian_kde(data) x_range np.linspace(data.min(), data.max(), 200) ax2 ax.twinx() # 创建共享x轴的第二个y轴 ax2.plot(x_range, kde(x_range), color‘red‘, linewidth2, label‘KDE‘) ax2.set_ylabel(‘Density‘) ax2.legend(loc‘upper left‘)参数选择与避坑bins分箱数这是最关键也是最难选的参数。箱数太少会丢失细节太多则会产生很多空箱图形锯齿状严重。经验法则可以尝试‘auto‘,‘fd‘Freedman-Diaconis规则,‘sqrt‘平方根规则。‘auto‘通常是个不错的起点。手动尝试多试几个值比如10 30 50看看分布形状是否稳定。density如果设为True直方图将显示为概率密度面积和为1而不是频数。这在和密度图KDE对比时非常有用。edgecolor务必设置一个明显的边框色如‘black‘否则相邻的箱子会粘在一起难以分辨。密度图KDE密度图是直方图的平滑版本更适合观察分布形状。虽然Matplotlib没有直接的KDE函数但结合scipy.stats.gaussian_kde或使用seaborn.kdeplot()可以轻松实现。上面代码展示了如何叠加KDE曲线。3.5 箱线图与小提琴图展示统计摘要与分布箱线图用五个数字总结一组数据最小值、第一四分位数Q1、中位数Q2、第三四分位数Q3、最大值。还能显示离群点。非常适合比较多组数据的分布。基础画法# 准备多组数据 data [np.random.normal(0, std, 100) for std in range(1, 4)] fig, ax plt.subplots() # labels: 每组数据的标签 bp ax.boxplot(data, labels[‘Group1‘, ‘Group2‘, ‘Group3‘], patch_artistTrue) # 美化箱子颜色 colors [‘lightblue‘, ‘lightgreen‘, ‘pink‘] for patch, color in zip(bp[‘boxes‘], colors): patch.set_facecolor(color) ax.set_ylabel(‘Value‘) ax.set_title(‘Boxplot by Group‘) ax.grid(True, axis‘y‘, linestyle‘--‘, alpha0.7)解读与避坑箱子从Q1到Q3中间的线是中位数。须默认是1.5倍IQR四分位距范围内的最小和最大值。此范围外的点被视为离群点以独立点显示。patch_artistTrue这个参数必须设为True后续才能用set_facecolor给箱子上色。notchTrue可以画出缺口箱线图用于直观比较中位数的置信区间。如果两个箱子的缺口不重叠可能意味着中位数有显著差异。小提琴图小提琴图结合了箱线图和密度图能展示数据的实际分布形状尤其是多峰分布。在Matplotlib中绘制稍复杂通常直接用seaborn.violinplot()。它展示了数据的核密度估计宽度表示数据在该值处的密度。import seaborn as sns fig, ax plt.subplots() sns.violinplot(datadata, axax) # data可以是列表的列表或DataFrame ax.set_title(‘Violin Plot‘)小提琴图在比较多组数据分布形状时比箱线图提供的信息更丰富。4. 高级图表与组合应用掌握了基础图表后我们可以开始玩一些更复杂的组合以应对更高级的数据可视化需求。4.1 组合图在同一坐标轴上叠加不同类型比如我们想在同一张图上用柱状图显示月度销售额再用折线图显示累计销售额或目标线。months [‘Jan‘, ‘Feb‘, ‘Mar‘, ‘Apr‘] sales [120, 145, 130, 160] cumulative_sales np.cumsum(sales) # 累计销售额 target [150] * len(months) # 月度目标线 fig, ax1 plt.subplots(figsize(8,5)) # 柱状图 - 主Y轴 color_bar ‘tab:blue‘ ax1.set_xlabel(‘Month‘) ax1.set_ylabel(‘Monthly Sales‘, colorcolor_bar) bars ax1.bar(months, sales, colorcolor_bar, alpha0.7, label‘Monthly‘) ax1.tick_params(axis‘y‘, labelcolorcolor_bar) # 在柱子上添加数值 for bar in bars: height bar.get_height() ax1.annotate(f‘{height}‘, xy(bar.get_x() bar.get_width() / 2, height), xytext(0, 3), # 3 points vertical offset textcoords‘offset points‘, ha‘center‘, va‘bottom‘) # 创建共享X轴的第二个Y轴 ax2 ax1.twinx() color_line1 ‘tab:red‘ color_line2 ‘tab:green‘ ax2.set_ylabel(‘Cumulative Sales / Target‘, colorcolor_line1) # 折线1 - 累计销售额 line1, ax2.plot(months, cumulative_sales, colorcolor_line1, marker‘s‘, linewidth2, label‘Cumulative‘) # 折线2 - 目标线 line2, ax2.plot(months, target, colorcolor_line2, linestyle‘--‘, linewidth2, label‘Target‘) ax2.tick_params(axis‘y‘, labelcolorcolor_line1) # 合并图例技巧点 lines_labels [bars, line1, line2] labels [l.get_label() for l in lines_labels] ax1.legend(lines_labels, labels, loc‘upper left‘) plt.title(‘Monthly Sales with Cumulative and Target‘) fig.tight_layout() # 自动调整子图参数使之填充整个图像区域 plt.show()核心技巧解析双Y轴通过ax1.twinx()创建共享X轴但拥有独立Y轴的新坐标轴ax2。这允许两个量纲不同的数据系列在同一张图上展示。颜色与标签分离分别设置ax1和ax2的Y轴标签颜色labelcolor和刻度颜色tick_params使其与对应的数据系列颜色一致增强可读性。合并图例这是组合图的难点。我们需要手动收集所有需要出现在图例中的元素bars,line1,line2和它们的标签然后在一个ax通常是ax1上调用legend。注意bars是一个容器BarContainer而line1和line2是Line2D对象获取标签的方式略有不同上面代码展示了一种通用的处理方法。fig.tight_layout()这是一个非常重要的函数它能自动调整子图、标签、标题等之间的间距避免它们重叠。在图形元素复杂时调用它总能得到更好的布局。4.2 子图并排展示多个相关图表使用plt.subplots()可以轻松创建网格状排列的子图。fig, axes plt.subplots(nrows2, ncols2, figsize(10, 8)) # 2x2网格 # axes 是一个 2x2 的 numpy 数组每个元素是一个 Axes 对象 axes axes.flatten() # 展平为一维数组方便循环 # 为每个子图绘制不同类型 plot_types [‘Line‘, ‘Scatter‘, ‘Bar‘, ‘Hist‘] for i, ax in enumerate(axes): ax.set_title(f‘Subplot {i1}: {plot_types[i]} Plot‘) # 这里可以根据i的不同调用不同的绘图函数如ax.plot, ax.scatter等 # 示例画点简单的数据 x np.linspace(0, 10, 50) if i 0: ax.plot(x, np.sin(x)) elif i 1: ax.scatter(x, np.cos(x) np.random.randn(50)*0.1) elif i 2: ax.bar([‘A‘, ‘B‘, ‘C‘], [3, 7, 2]) else: ax.hist(np.random.randn(100), bins20) ax.grid(True, alpha0.3) fig.suptitle(‘Multiple Subplots Demo‘, fontsize16) # 为整个图形添加总标题 fig.tight_layout(rect[0, 0, 1, 0.96]) # 调整布局为总标题留出空间关键点axes是一个二维数组通过axes[0,0],axes[0,1]等方式访问或者用flatten()展平后循环。fig.suptitle()设置整个图形的总标题。tight_layout(rect[left, bottom, right, top])中的rect参数可以指定子图区域在图形中的相对位置0到1之间。这里top0.96是为了给总标题留出4%的空间。4.3 定制化进阶让图表“会说话”一张专业的图表细节决定成败。下面是一些提升图表表现力的实用技巧。1. 色彩映射Colormap的妙用色彩映射不仅用于散点图在热图、等高线图中更是核心。选择合适的色彩映射至关重要。顺序数据如温度、高度使用‘viridis‘, ‘plasma‘, ‘summer‘, ‘wistia‘等。这些色彩映射在亮度上平滑变化适合表示数值大小。发散数据如相对零点的正负偏差使用‘RdBu‘, ‘coolwarm‘, ‘bwr‘。这些色彩映射中间亮如白色两端是两种对比色。分类数据使用‘tab10‘, ‘Set3‘, ‘tab20c‘。这些色彩映射由一系列区分明显的颜色组成。# 在散点图中使用发散色图 z x * y # 第三个变量 sc ax.scatter(x, y, cz, cmap‘coolwarm‘, edgecolor‘k‘) plt.colorbar(sc, axax, label‘Z Value‘)2. 文本与标注除了基本的set_xlabel,set_titleax.text()和ax.annotate()可以让你在任意位置添加文本或箭头标注。ax.text(0.5, 0.9, ‘Important Note‘, transformax.transAxes, # 使用坐标轴相对坐标 (0,0)左下角 (1,1)右上角 fontsize12, ha‘center‘, bboxdict(boxstyle‘round‘, facecolor‘wheat‘, alpha0.8)) # 箭头标注 ax.annotate(‘Peak Value‘, xy(peak_x, peak_y), xytext(peak_x1, peak_y10), arrowpropsdict(facecolor‘black‘, shrink0.05, width1, headwidth8), fontsize10, ha‘center‘)3. 刻度与脊柱的精细化调整刻度ax.xaxis.set_major_locator()和ax.xaxis.set_minor_locator()控制主/次刻度的位置。ax.tick_params()可以调整刻度方向、长度、颜色、标签大小等。脊柱ax.spines[‘top‘].set_visible(False)可以隐藏顶部脊柱这是学术图表常见的风格。# 设置科学计数法 from matplotlib.ticker import ScalarFormatter ax.yaxis.set_major_formatter(ScalarFormatter(useMathTextTrue)) # 使用LaTeX风格的科学计数法 # 隐藏右边和上边的脊柱 ax.spines[‘right‘].set_visible(False) ax.spines[‘top‘].set_visible(False) # 只保留左下脊柱 ax.xaxis.set_ticks_position(‘bottom‘) ax.yaxis.set_ticks_position(‘left‘)5. 实战问题排查与性能优化即使知道了所有方法在实际操作中还是会遇到各种奇怪的问题。这里记录一些高频问题和解决方案。5.1 中文显示乱码问题这是Matplotlib在中文环境下最经典的问题。解决方法是在绘图前设置中文字体。import matplotlib.pyplot as plt plt.rcParams[‘font.sans-serif‘] [‘SimHei‘, ‘Microsoft YaHei‘, ‘DejaVu Sans‘] # 指定默认字体 plt.rcParams[‘axes.unicode_minus‘] False # 解决负号‘-‘显示为方块的问题如果系统没有这些字体可能需要手动下载字体文件如SimHei.ttf并添加到Matplotlib的字体目录或者指定绝对路径。5.2 图形保存为高清图片用plt.show()展示后右键保存的图片分辨率往往不够。应该使用fig.savefig()。fig.savefig(‘my_plot.png‘, dpi300, bbox_inches‘tight‘, facecolor‘white‘, edgecolor‘none‘)dpi: 分辨率出版物通常要求300或600。bbox_inches‘tight‘: 自动裁剪图形周围的空白区域非常有用。facecolor/edgecolor: 设置图形背景和边框颜色。5.3 绘图速度慢卡顿当数据点超过10万个时Matplotlib的默认渲染可能会变慢。优化策略1降低精度。对于极大数据集绘图前可以先下采样。优化策略2使用更快的后端。在Jupyter中可以使用%matplotlib widget或%matplotlib ipympl获得交互性更好的体验但初始加载可能慢。对于脚本TkAgg或Qt5Agg是常用后端。优化策略3换用专门库。对于超大规模数据可视化考虑Datashader先栅格化再渲染或PlotlyWebGL加速。5.4 常见错误速查表问题现象可能原因解决方案图例不显示1. 绘图时未设置label参数。2. 设置了label但忘记调用ax.legend()。1. 在plot,scatter等函数中添加label‘xxx‘。2. 在绘图后调用ax.legend()。子图标签或标题重叠子图间距太小。调用fig.tight_layout()或手动调整fig.subplots_adjust()。保存的图片有空白边缘默认保存包含坐标轴外部的区域。在savefig中使用bbox_inches‘tight‘。柱状图柱子挤在一起或太宽类别太多或width参数不合适。调整figsize增加画布宽度或减小width参数值。散点图所有点一个颜色c参数传入了一个单一数值或字符串而不是一个数组。检查c的数据结构确保它是一个长度与数据点相同的序列。无法显示图形脚本中非交互式环境下未使用plt.show()。在脚本末尾添加plt.show()。在Jupyter中使用%matplotlib inline。5.5 个人踩坑心得先规划后编码在动手写代码前先在纸上或脑子里想好要表达什么信息用什么图表几个子图大概的布局是怎样的这能节省大量后期调整的时间。善用面向对象接口从一开始就强迫自己使用fig, ax plt.subplots()和ax.plot()的模式。当图形复杂时你会感谢这个决定。颜色不是越多越好坚持使用色盲友好的配色方案如viridis,plasma,Set2,tab20c。避免使用红绿对比大约8%的男性是红绿色盲。标签要清晰X/Y轴标签、单位、图例、数据标签一个都不能少。假设你的读者对你数据一无所知。迭代优化很少有图能一次画成理想的样子。通常是画出一个草图然后不断微调字体大小、刻度密度、图例位置、颜色、边距……fig.tight_layout()是你的好朋友。了解你的输出媒介在屏幕上看的图和打印在纸上的图设计考量不同。打印需要更高的对比度和更清晰的线条屏幕显示则可以更灵活地使用颜色。画图本质上是一种沟通技能。Matplotlib给了你一套强大的工具但如何用这些工具讲好一个数据故事需要不断的练习和思考。从模仿优秀的图表开始理解其设计原则然后应用到自己的工作中你会逐渐形成自己的风格。记住最好的图表是那些能让观众在最短时间内理解最多信息的图表。