Python绘图中文乱码怎么解决?Matplotlib显示中文标题实例
详细解析Python使用Matplotlib绘图时中文标题和坐标轴出现方块乱码的原因,并提供rcParams全局配置、FontProperties指定字体及跨平台(Windows/macOS/Linux)适配的完整代码实例。
UserWarning: Glyph missing 警告。这一现象的核心原因在于 Matplotlib 默认采用的无衬线字体不包含中文字库。要彻底解决该问题,最直接且通用的方法是在脚本开头为 plt.rcParams['font.sans-serif'] 指定系统自带的中文字体(如黑体或微软雅黑),并配合 plt.rcParams['axes.unicode_minus'] = False 修复负号显示。本文将通过成因解析、三种实用解决方案及完整可运行的实例代码,带你快速排查并根治中文显示问题。
一、为什么 Matplotlib 默认无法显示中文?
初学者在调用 plt.title("销售统计图") 时,界面上往往无法正常渲染出汉字,而是出现一排方形白框。在终端或控制台中,通常还会输出如下警告提示:
UserWarning: Glyph 38144 (\N{CJK UNIFIED IDEOGRAPH-9500}) missing from font(s) DejaVu Sans.
fig.canvas.print_figure(bytes_io, **kw)
这并不是 Python 的编码错误(如 UTF-8 异常),而是图形排版层面的字体匹配问题。Matplotlib 的渲染管线分为“文本传参”、“字体配置解析”、“字形库查找”以及“底图栅格化”四个阶段。默认情况下,Matplotlib 的配置字典中无衬线字体族 font.sans-serif 的首选字体是 DejaVu Sans。该字体属于西方字符集,内部字形索引表(Glyph Table)完全没有收录 CJK(中日韩)汉字的 Unicode 编码段。
当底层排版引擎通过 FreeType 试图提取对应汉字的矢量轮廓时,检索结果返回为空。渲染引擎无法定位笔画数据,只能退化为显示代表缺失字符的默认符号——也就是空心方块(行业内常称之为“Tofu / 豆腐块”)。
二、方法一:通过 rcParams 全局配置(最常用、最推荐)
对于常规的数据分析脚本、Jupyter Notebook 以及日常科研绘图,最优雅高效的做法是通过修改运行时的全局参数表 plt.rcParams。这种方式只需在导入模块后追加两行配置代码,当前脚本中所有图表的标题、刻度标签、图例均会自动继承中文字体支持。
1. 关键参数设置说明
我们需要同时配置两项参数,缺一不可:
plt.rcParams['font.sans-serif']:指定无衬线字体的优先级列表。建议传入一个列表,依次设置中文字体名称和备选西文字体,当首选字体在部分系统中缺失时可自动降级。plt.rcParams['axes.unicode_minus'] = False:Matplotlib 默认将负号渲染为 Unicode 减号字符U+2212。很多常见中文字体中同样缺少该特殊符号,导致负数刻度(如-10)变成□10。将其设为False即可指示引擎使用标准 ASCII 连字符-正常绘制减号。
2. 各操作系统常用中文字体对照
不同操作系统的预装中文字体库名称并不相同。如果在 macOS 上配置 Windows 独有的 SimHei,系统仍会因找不到字体而回退失败。请根据运行环境选择匹配的字体名称:
| 操作系统 | 常用无衬线中文字体名称 | 系统内置说明 |
|---|---|---|
| Windows | 'SimHei'(中易黑体)、'Microsoft YaHei'(微软雅黑) |
系统自带,无需安装,开箱即用 |
| macOS | 'PingFang SC'(苹方)、'Songti SC'(宋体)、'Arial Unicode MS' |
系统内置,高分辨率 Retina 屏幕渲染效果优秀 |
| Linux / 容器 | 'WenQuanYi Micro Hei'、'Noto Sans CJK SC' |
精简系统通常无预装,需通过包管理器安装 |
3. 完整代码实例:绘制带中文标题与双轴标签的图表
以下是包含了中文主标题、副标题、X/Y轴中文标签、图例以及负数刻度数据的完整可执行代码:
import matplotlib.pyplot as plt
# 1. 跨平台字体回退列表:按系统优先匹配 Windows / macOS 常见中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'PingFang SC', 'DejaVu Sans']
# 2. 正常显示负号(避免坐标轴负数变方块)
plt.rcParams['axes.unicode_minus'] = False
# 3. 准备绘图数据(包含正负波动数值)
months = ['1月', '2月', '3月', '4月', '5月', '6月']
sales_target = [120, 150, 180, 200, 240, 280]
profit_growth = [-15, 25, 40, -5, 30, 55]
# 4. 创建画布与图表
fig, ax1 = plt.subplots(figsize=(8, 4.8), dpi=100)
# 绘制销售目标柱状图
bars = ax1.bar(months, sales_target, color='#6B88A6', width=0.45, label='销售额 (万元)')
ax1.set_xlabel('统计月份', fontsize=12)
ax1.set_ylabel('销售额 (万元)', color='#2C3E50', fontsize=12)
ax1.tick_params(axis='y', labelcolor='#2C3E50')
# 创建双 Y 轴绘制利润波动折线图(涉及负数)
ax2 = ax1.twinx()
lines = ax2.plot(months, profit_growth, color='#71A397', marker='o', linewidth=2.2, label='利润环比增速 (%)')
ax2.set_ylabel('环比增速 (%)', color='#3D7569', fontsize=12)
ax2.tick_params(axis='y', labelcolor='#3D7569')
# 5. 设置中文主标题
plt.title('2026年上半年经营指标与利润波动统计图', fontsize=14, fontweight='bold', pad=15)
# 合并双轴图例并展示
handles1, labels1 = ax1.get_legend_handles_labels()
handles2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(handles1 + handles2, labels1 + labels2, loc='upper left')
plt.tight_layout()
plt.show()
三、方法二:使用 FontProperties 精准指定字体文件(跨平台团队首选)
如果你正在开发一个需要分发给其他团队成员的代码仓库,或者需要将 Python 脚本部署到 Docker 容器和远程 Linux 服务器上,通过 rcParams 依赖系统全局字体的做法往往存在隐患——因为你无法预知目标机器上究竟安装了什么字体。
此时更稳健的工程实践是:在项目内部附带一个通用的开源中文字体文件(例如思源黑体 SourceHanSansSC-Regular.otf 或文泉驿微米黑 wqy-microhei.ttc),然后使用 matplotlib.font_manager.FontProperties 直接加载文件路径。
1. 完整代码实例:基于字体路径加载
import os
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
# 1. 指定项目内置的字体文件绝对或相对路径
font_path = os.path.join(os.path.dirname(__file__), 'fonts', 'SourceHanSansSC-Regular.otf')
# 2. 如果项目内未打包字体,也可以直接指向系统已知字体路径:
# Windows 示例: 'C:/Windows/Fonts/simhei.ttf'
# macOS 示例: '/System/Library/Fonts/PingFang.ttc'
if not os.path.exists(font_path):
# 若本地无项目字体,降级到系统路径(以 Windows 字体为例)
font_path = 'C:/Windows/Fonts/simhei.ttf'
custom_font = FontProperties(fname=font_path)
# 3. 准备数据
categories = ['技术研发', '市场拓展', '用户运营', '行政支持']
budget = [45, 30, 15, 10]
# 4. 绘图并在每个文本 API 处显式传递 fontproperties 参数
fig, ax = plt.subplots(figsize=(7, 4.5))
ax.pie(budget, labels=categories, autopct='%1.1f%%', startangle=90,
colors=['#6B88A6', '#71A397', '#8EA4BA', '#C4D1DE'],
textprops={'fontproperties': custom_font, 'fontsize': 11})
# 标题传递自定义字体对象
ax.set_title('部门预算占比分布图', fontproperties=custom_font, fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
FontProperties(fname=...) 不会修改全局的 rcParams。对于需要多处渲染中文的复杂仪表盘,若不想在每个 xlabel、ylabel 和 text 处都重复传参,也可以将已加载的字体名称动态注册回 font_manager 并加入全局列表。
四、方法三:修改 matplotlibrc 配置文件(个人开发机一次性配置)
如果你只在本地固定的台式机或笔记本上进行开发,并且不想每次新建 Python 脚本或打开新 Jupyter Notebook 时都手动输入两行配置代码,可以直接修改 Matplotlib 的底层配置文件 matplotlibrc。
-
查找配置文件位置:在 Python 交互环境中运行以下指令获取路径:
通常输出类似import matplotlib print(matplotlib.matplotlib_fname()).../site-packages/matplotlib/mpl-data/matplotlibrc的绝对路径。 -
修改配置文件内容:使用文本编辑器打开该文件,搜索并定位到如下两行,去掉前面的注释符
#并填入中文字体:font.sans-serif: SimHei, Microsoft YaHei, DejaVu Sans axes.unicode_minus: False -
清理字体缓存文件:修改配置后,由于 Matplotlib 在初次启动时已在用户家目录下建立了字体索引缓存,必须删除该缓存才会重新加载配置:
在终端进入该目录(如# 查看缓存所在目录 import matplotlib print(matplotlib.get_cachedir())~/.cache/matplotlib),删除名为fontlist-vXXX.json的文件,随后重启 Python 即可永久生效。
五、常见问题排查清单
在实际操作中,读者有时虽然按照上述步骤添加了代码,但依然提示乱码或报错。以下是三个最常见的卡点及其对应解决办法:
1. 已经配置了 plt.rcParams['font.sans-serif'] = ['SimHei'],但依旧显示方块?
原因:当前操作系统中根本没有安装该字体,或者 Matplotlib 字体缓存已损坏。例如在 macOS 或 Linux 纯净镜像中并不存在 SimHei 字体文件。
排查:运行以下 Python 语句打印出系统内所有被 Matplotlib 识别的中文字体列表:
from matplotlib.font_manager import fontManager
# 筛选包含常见中文关键词的可用字体名
available_fonts = [f.name for f in fontManager.ttflist if any(k in f.name for k in ['Hei', 'Song', 'Kai', 'Ming', 'Yuan', 'SC', 'TC'])]
print("当前系统可用中文字体名称列表:")
for name in sorted(set(available_fonts)):
print(f" - {name}")
从中挑出一个真实存在的字体名称,填入 font.sans-serif 中即可。
2. Linux / Docker 容器中完全没有中文字体怎么办?
在极简的 Linux 发行版(如 Ubuntu Server、Alpine 或基础 Python Docker 镜像)中,系统未自带任何东亚语言字体包。若要在容器内生成图表,需先在终端执行系统包安装指令:
# Ubuntu / Debian 环境
apt-get update && apt-get install -y fonts-wqy-microhei
# 并在 Python 中指定
plt.rcParams['font.sans-serif'] = ['WenQuanYi Micro Hei']
3. 汉字显示正常了,但负数坐标轴变成了方块?
请务必检查是否遗漏了 plt.rcParams['axes.unicode_minus'] = False。汉字和负号分别由不同的字符段决定,单纯配置字体族往往只能解决汉字部分,只有关闭 unicode_minus 才能让负号恢复为标准减号显示。
六、总结
解决 Matplotlib 中文乱码的本质,是打破默认西方字体 DejaVu Sans 的单一依赖,将包含中文字形的系统字体或外部字体注入到渲染管线中。对于个人日常数据分析,首选使用 rcParams 配合系统字体(Windows 用 SimHei / Microsoft YaHei,macOS 用 PingFang SC);对于团队跨平台协作与线上部署,首选将字体文件放入项目并使用 FontProperties 进行加载。牢记同步配置 axes.unicode_minus = False 并适时清理字体缓存,即可让图表在所有平台中保持稳定、清晰的中文呈现。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













