欢迎光临
我们一直在努力

Day 06 · 图表美化:配色、标注、子图布局、中文显示

「AI Python 系列」第 02 栏 · AI+数据可视化 全栏 15 篇 · 零成本跟完 🍃 作者:梅雅达编程笔记 首发:CSDN


摘要: 基础图表会画了,但默认样式总是差点意思——颜色刺眼、中文变方块、布局挤成一团。本篇解决 matplotlib 最让人头疼的四个问题:中文乱码的根本原因和永久解决方案;6 套拿来就用的配色方案;数据标注、箭头注释等让图表"会说话"的技巧;以及子图布局的间距控制。学会这些,图表质量直接上一个台阶。


上一篇画了四种基础图。能跑,但说实话,默认样式有点丑。

颜色不够协调、中文可能显示成方块、多个子图挤在一起……这些都不是你的代码有问题,是 matplotlib 的默认审美确实不太行。

这篇专门解决这些问题。


一、中文显示:永远的痛

问题根源

matplotlib 默认字体不支持中文。如果你看到图表里的中文全变成了 □□□,就是这个问题。

临时解决(每篇都要写)

import matplotlib
import matplotlib.pyplot as plt
from matplotlib.font_manager import _load_fontmanager, fontManager
import os

# 删除旧字体缓存,强制重建(首次运行或换字体后必加)
cache_dir = matplotlib.get_cachedir()
for f in os.listdir(cache_dir):
if f.startswith('fontlist'):
os.remove(os.path.join(cache_dir, f))

_load_fontmanager(try_read_cache=False)

# 稳健的中文字体配置
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'FangSong']
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题

# 验证中文字体是否可用
available = {f.name for f in fontManager.ttflist}
for font in plt.rcParams['font.sans-serif']:
if font in available:
print(f'✅ 字体可用: {font}')
break
else:
print(f'❌ 字体不可用: {font}')

  • Microsoft YaHei:微软雅黑,Windows 上最清晰的中文字体
  • SimHei:黑体,兼容性最好
  • FangSong:仿宋,备选
  • 写三个是为了跨平台兼容,第一个不可用自动降级

永久解决(推荐)

每次都要加那一行确实烦。可以写成全局配置:

import matplotlib
import os

# 找到 matplotlib 的配置文件位置
print(matplotlib.matplotlib_fname())

打开这个文件(matplotlibrc),找到 font.sans-serif 那行,改成:

font.sans-serif: Microsoft YaHei, SimHei, FangSong, DejaVu Sans

找到 font.family 那行,改成:

font.family: sans-serif

保存后重启 Python,以后就不用每次手动设置了。

⚠️ 重要:plt.style.use() 会覆盖字体!

如果你用了 seaborn 或其他内置样式,必须在 style.use() 之后重新设置中文字体:

plt.style.use('seaborn-v0_8-whitegrid')
# style.use() 会重置所有 rcParams,包括字体!
# 所以必须重新设置:
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'FangSong']
plt.rcParams['axes.unicode_minus'] = False

这是很多人中文字体"莫名失效"的原因。

查看系统可用字体

from matplotlib.font_manager import fontManager

# 查看所有支持的中文字体
chinese_fonts = [f.name for f in fontManager.ttflist
if any(kw in f.name for kw in ['Hei', 'Song', 'Kai', 'Fang', 'Yuan', 'Ming'])]
print(sorted(set(chinese_fonts)))


二、配色方案:别再红绿蓝了

matplotlib 默认的颜色……怎么说呢,很有"2005 年 PPT"的感觉。

方案 1:直接使用十六进制色

最可控的方式,直接指定颜色:

# 我常用的几个配色组合
COLORFUL_SCHEME = ['#5B8FF9', '#5AD8A6', '#F6BD16', '#E8684A', '#6DC8EC'] # 多彩配色(推荐)
WARM_SCHEME = ['#FF5722', '#FF9800', '#FFC107', '#FFEB3B', '#FFF9C4']
NATURE_SCHEME = ['#4CAF50', '#8BC34A', '#CDDC39', '#FFC107', '#FF9800']
MUTED_SCHEME = ['#546E7A', '#78909C', '#90A4AE', '#B0BEC5', '#CFD8DC']

import matplotlib.pyplot as plt

products = ['商品A', '商品B', '商品C', '商品D', '商品E']
sales = [320, 280, 195, 205, 160]
colors = ['#5B8FF9', '#5AD8A6', '#F6BD16', '#E8684A', '#6DC8EC']

plt.figure(figsize=(8, 5))
plt.bar(products, sales, color=colors)
plt.title('各商品销量', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()

在这里插入图片描述

方案 2:使用 matplotlib 内置主题

# 查看所有可用样式
print(plt.style.available)

# 用内置样式
plt.style.use('seaborn-v0_8-whitegrid') # 推荐,背景干净有网格
plt.figure(figsize=(8, 5))
plt.bar(products, sales, color=['#5B8FF9', '#5AD8A6', '#F6BD16', '#E8684A', '#6DC8EC'])
plt.title('使用内置样式的柱图')
plt.tight_layout()
plt.show()

在这里插入图片描述

推荐几个好看的内置样式:

  • seaborn-v0_8-whitegrid:白底+灰网格,干净专业
  • fivethirtyeight:FiveThirtyEight 风格,偏媒体感
  • ggplot:R 语言 ggplot2 风格
  • bmh:贝叶斯风格,简洁

方案 3:渐变色

import numpy as np

n = 8
# 多彩渐变(tab10 色表,颜色丰富协调)
colors_gradient = plt.cm.tab10(np.linspace(0, 0.8, n))

plt.figure(figsize=(10, 5))
plt.bar(range(n), [x*10 for x in range(1, n+1)], color=colors_gradient)
plt.title('渐变色柱状图')
plt.tight_layout()
plt.show()

在这里插入图片描述

plt.cm.tab10 是 matplotlib 内置的多彩色表,每种颜色都不同。类似的还有 Set2、Paired、viridis 等,都比单色渐变好看。


三、标注:让图表"会说话"

一个没有标注的图表,别人可能看不懂重点在哪。

3.1 数据标签

months = ['1月', '2月', '3月', '4月', '5月', '6月']
sales = [12.3, 10.1, 15.8, 14.2, 18.6, 22.1]

plt.figure(figsize=(10, 5))
bars = plt.bar(months, sales, color=['#5B8FF9', '#5AD8A6', '#F6BD16', '#E8684A', '#6DC8EC', '#9270CA'])

# 在每个柱子顶部显示数值
for bar, val in zip(bars, sales):
plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.3,
f'{val}万', ha='center', fontsize=11, color='#333')

plt.title('上半年月度销售额', fontsize=14, fontweight='bold')
plt.ylabel('销售额(万元)')
plt.tight_layout()
plt.show()

在这里插入图片描述

3.2 箭头注释

当你要特别指出某个数据点时:

plt.figure(figsize=(10, 5))
plt.plot(months, sales, marker='o', color='#9270CA', linewidth=2)

# 用箭头标注最高点
max_idx = sales.index(max(sales))
plt.annotate(f'最高点: {sales[max_idx]}万',
xy=(max_idx, sales[max_idx]), # 箭头指向
xytext=(max_idx 1.5, sales[max_idx] + 2), # 文字位置
fontsize=12, color='#D32F2F',
arrowprops=dict(arrowstyle='->', color='#D32F2F', lw=1.5))

plt.title('销售额趋势(6月突破历史新高)', fontsize=14, fontweight='bold')
plt.ylabel('销售额(万元)')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

在这里插入图片描述

3.3 参考线和区域高亮

plt.figure(figsize=(10, 5))
plt.plot(months, sales, marker='o', color='#9270CA', linewidth=2)

# 平均值参考线
avg = sum(sales) / len(sales)
plt.axhline(y=avg, color='#FF5722', linestyle='–', linewidth=1.5,
label=f'平均值: {avg:.1f}万')

# 高亮某个区域
plt.axvspan(3.5, 5.5, alpha=0.1, color='#4CAF50', label='促销活动期')

plt.title('销售额趋势(虚线=平均值,绿色=促销期)', fontsize=14, fontweight='bold')
plt.ylabel('销售额(万元)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

在这里插入图片描述


四、子图布局:一图多用

4.1 基础子图

fig, axes = plt.subplots(2, 2, figsize=(12, 8))

# 左上:折线图
axes[0, 0].plot(months, sales, color='#5B8FF9', marker='o')
axes[0, 0].set_title('月度趋势', fontsize=12, fontweight='bold')
axes[0, 0].grid(True, alpha=0.3)

# 右上:柱状图
axes[0, 1].bar(months, sales, color='#E8684A')
axes[0, 1].set_title('月度对比', fontsize=12, fontweight='bold')

# 左下:饼图
axes[1, 0].pie([55, 30, 15], labels=['线上', '线下', '分销'],
autopct='%1.1f%%', colors=['#5B8FF9', '#5AD8A6', '#F6BD16'])
axes[1, 0].set_title('渠道占比', fontsize=12, fontweight='bold')

# 右下:散点图
prices = [59, 79, 99, 129, 159, 199]
volumes = [420, 350, 280, 180, 120, 85]
axes[1, 1].scatter(prices, volumes, color='#9270CA', s=80)
axes[1, 1].set_title('价格vs销量', fontsize=12, fontweight='bold')
axes[1, 1].set_xlabel('价格')
axes[1, 1].set_ylabel('销量')

plt.tight_layout()
plt.savefig('四图组合.png', dpi=150, bbox_inches='tight')
plt.show()

在这里插入图片描述

4.2 不规则布局

有时候你需要一个大图 + 两个小图:

fig = plt.figure(figsize=(12, 5))

# 左边大图
ax1 = fig.add_subplot(1, 2, 1)
ax1.plot(months, sales, color='#5B8FF9', marker='o', linewidth=2)
ax1.set_title('月度趋势(大图)', fontsize=12, fontweight='bold')
ax1.grid(True, alpha=0.3)

# 右边两个小图(上下排列)
ax2 = fig.add_subplot(2, 2, 2)
ax2.bar(['Q1', 'Q2'], [38.2, 54.9], color=['#FF5722', '#FF9800'])
ax2.set_title('季度汇总')

ax3 = fig.add_subplot(2, 2, 4)
ax3.pie([55, 30, 15], labels=['线上', '线下', '分销'],
autopct='%1.1f%%', colors=['#5B8FF9', '#5AD8A6', '#F6BD16'])
ax3.set_title('渠道占比')

plt.tight_layout()
plt.savefig('不规则布局.png', dpi=150, bbox_inches='tight')
plt.show()

在这里插入图片描述

4.3 间距控制

plt.tight_layout() 能解决大部分间距问题。但如果需要更精细的控制:

fig, axes = plt.subplots(2, 2, figsize=(10, 8))
# 手动调整间距
fig.subplots_adjust(hspace=0.4, wspace=0.3)
# hspace = 子图之间的垂直间距
# wspace = 子图之间的水平间距

在这里插入图片描述


五、图表样式速查表

元素常用参数示例
标题 fontsize, fontweight, color fontsize=14, fontweight='bold'
坐标轴标签 fontsize, color fontsize=12
刻度标签 fontsize, rotation rotation=45 旋转防重叠
网格线 alpha, linestyle alpha=0.3, linestyle='–'
图例 loc, fontsize, frameon loc='upper right'
保存图片 dpi, bbox_inches dpi=150, bbox_inches='tight'

六、小结

让图表好看的关键就三件事:

  • 中文正常显示:每次开头加上 plt.rcParams['font.sans-serif'] 或永久配置
  • 配色别用默认:用十六进制色值或内置主题,一张图的颜色不要超过 5 种
  • 加上标注:数据标签、参考线、箭头注释——告诉别人重点在哪
  • 记住一个原则:图表是给别人看的,不是给自己看的。你觉得"数据都在这了"不够,要让看的人三秒内就能 get 到你想说的。

    往期回顾

    Day 01 · 数据可视化到底在干啥?为什么 AI 让它变简单了

    Day 02:环境搭建 Python 数据分析零成本工具包

    Day 03 · Pandas 快速上手:读取、清洗、整理数据

    Day 04 · 数据清洗:缺失值、异常值、重复值一站式处理

    Day 05 · Matplotlib 基础:折线图、柱状图、饼图、散点图

    下一篇讲 Seaborn——基于 matplotlib 的高级封装,很多复杂的统计图表,用它一行代码就能画出来。


    下期预告: Day 07 Seaborn 进阶——用一行代码画出热力图、小提琴图、配对图等统计图表,比 matplotlib 更简单、更好看。


    专栏

    -「AI Python 系列」第 02 栏 ·AI+数据可视化(连载中) -「AI Python 系列」第 01 栏 · AI+自动化办公(连载中) -「AI Python 系列」第 03 栏 · Python 爬虫实战(连载中)

    资源领取

    • 关注作者获取本栏完整代码和数据集

    原创声明:本文为梅雅达编程笔记原创作品,未经允许不得转载。

    赞(0)
    未经允许不得转载:171主机测评 » Day 06 · 图表美化:配色、标注、子图布局、中文显示
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址