Python绘图中文显示乱码怎么解决?
详细讲解Python绘图(Matplotlib/Seaborn)中文显示乱码与负号方块的底层成因,提供基于rcParams全局配置、跨平台FontProperties文件挂载以及永久配置的三种实用解决方案与排查指南。
UserWarning: Glyph missing,核心原因是默认字体库(DejaVu Sans)中缺少中文字形数据。最直接有效的解决方式是在绘图代码开头通过全局参数配置系统中已有的中文字体,并同步关闭负号的 Unicode 转义:plt.rcParams['font.sans-serif'] = ['SimHei'] 与 plt.rcParams['axes.unicode_minus'] = False。对于多平台协作或容器部署项目,则推荐使用 FontProperties 显式挂载字体文件。
一、问题具体表现与乱码成因剖析
初次使用 Python 进行数据可视化的开发者,在为图表添加中文标题、坐标轴标签或图例时,通常会遇到两类异常表现:
- 汉字显示为白色空心方块:文字位置被替换为一个或多个矩形方框(俗称“豆腐块”),控制台伴随输出警告:
UserWarning: Glyph 20013 (中) missing from font(s) DejaVu Sans.。 - 坐标轴负号显示为方块或截断:当数据中包含负数(如 -10、-5)时,原本应显示为减号的符号变成了方块。
许多开发者常将此问题误判为“Python 编码错误”并尝试使用 .encode('utf-8') 转码,但这是无效的。Python 3 内部原生使用 Unicode 字符串,数据传递过程本身并无乱码;乱码的根本原因发生在底层矢量文字的光栅化渲染阶段。
如上图所示,Matplotlib 在初始化时会通过其内置的 font_manager 扫描系统字体并建立索引缓存。其全局配置字典 rcParams 中预设的无衬线字体序列为:
# Matplotlib 默认字体族配置(默认仅包含西文字符)
font.family : sans-serif
font.sans-serif : ['DejaVu Sans', 'Bitstream Vera Sans', 'Computer Modern Sans Serif', ...]
默认列表首位的 DejaVu Sans 是一套专为西文排版设计的开源字体,其字模库(Glyph Table)仅涵盖了 ASCII 字符和部分拉丁扩展符号,完全不包含 CJK(中日韩统一表意文字)字形。当渲染引擎尝试按照字形编码提取笔画轮廓时,未命中的字符就会被降级显示为“缺失占位符”(即方块)。
而负号方块则是由 axes.unicode_minus 参数引起的。Matplotlib 默认将其设为 True,意图使用排版效果更规范的 Unicode 减号(Unicode 编码 U+2212),但多数常见中文字体库未收录该特殊字符,直接导致负号渲染失败。
二、解决方案一:通过 rcParams 全局配置系统字体(快速通用)
在单人本地分析或 Jupyter Notebook 交互式环境中,最简捷的做法是直接在脚本头部通过 plt.rcParams 注入当前操作系统自带的中文字体,并修正负号渲染模式。
1. 常见操作系统的中文字体名称对照
不同操作系统预装的商用或开源中文字体名称不同。配置时需使用字体在系统中的英文字族名(Family Name):
| 操作系统 | 推荐字体英文字族名称 | 中文字体中文名称 | 系统预装状态 |
|---|---|---|---|
| Windows | SimHei / Microsoft YaHei |
黑体 / 微软雅黑 | 系统自带 |
| macOS | Arial Unicode MS / PingFang SC |
Arial Unicode / 苹方-简 | 系统自带 |
| Linux (Ubuntu/CentOS) | WenQuanYi Micro Hei |
文泉驿微米黑 | 需 apt/yum 安装 |
2. 完整代码示例
以下是一段包含正负区间坐标、中文标题、中文坐标轴与图例的完整绘图代码。在不同操作系统中运行前,只需确认字体名列表中包含本机已有的字体:
import matplotlib.pyplot as plt
import numpy as np
# 步骤一:设置中文字体候选列表(优先使用列表中排在首位的已装字体)
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'PingFang SC', 'sans-serif']
# 步骤二:关闭 Unicode 减号,恢复使用标准 ASCII 连字符渲染负号(关键!)
plt.rcParams['axes.unicode_minus'] = False
# 准备演示数据(含正负数值)
x = np.linspace(-10, 10, 100)
y = np.sin(x) * 20
# 绘图操作
plt.figure(figsize=(8, 4.5), dpi=100)
plt.plot(x, y, label='周期振幅曲线', color='#687D8D', linewidth=2)
# 添加中文元素
plt.title('传感器环境数据波动测试', fontsize=14, pad=12)
plt.xlabel('时间偏移量 (秒)', fontsize=11)
plt.ylabel('温差读数 (℃)', fontsize=11)
plt.grid(True, linestyle='--', alpha=0.5)
plt.legend(loc='upper right')
# 保存或展示
plt.tight_layout()
# plt.show()
plt.rcParams['axes.unicode_minus'] = False。不少人配置了中文字体后汉字显示正常,但坐标轴上的“-5”、“-10”等负数数字前端仍然出现小方框,正是因为忽略了该项设置。
三、解决方案二:使用 FontProperties 显式加载字体文件(跨平台团队推荐)
在实际生产环境中,代码往往需要在 Windows 开发机、macOS 笔记本以及无桌面界面的 Linux 服务器(或 Docker 容器)之间迁移。如果仅依赖 rcParams,一旦目标机器未预装同名字体,绘图程序便会悄然回退到默认英文字体并重新出现方块乱码。
此时更稳健的工程做法是:在项目代码仓中放置一份体积较小的开源中文字体文件(如思源黑体 SourceHanSansSC-Regular.otf 或 SimHei.ttf),在代码中通过 FontProperties 显式指定路径。
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
import os
# 指向工程内部存放的字体文件(支持相对路径与绝对路径)
font_path = os.path.join(os.path.dirname(__file__), 'fonts', 'SimHei.ttf')
my_font = FontProperties(fname=font_path, size=12)
title_font = FontProperties(fname=font_path, size=14, weight='bold')
# 仍然需要保持负号修复
plt.rcParams['axes.unicode_minus'] = False
plt.figure(figsize=(7, 4))
plt.plot([-2, 0, 2, 4], [10, -5, 15, 8], color='#879B88')
# 显式传入 fontproperties 参数
plt.title('跨平台指标监控', fontproperties=title_font)
plt.xlabel('采样节点', fontproperties=my_font)
plt.ylabel('偏离程度', fontproperties=my_font)
plt.show()
关键差异解析:为什么这个技术选型差异很重要?
为了让读者在实际项目中有明确的选型依据,我们从四个维度对比这两种主流方式:
- 侵入范围不同:
rcParams属于环境级全局生效,一次声明即可作用于所有调用;而FontProperties属于局部参数传递,需要在每个涉及文字渲染的方法(如title、xlabel、legend、text)中显式挂载对象。 - 对宿主环境的依赖度截然相反:这是导致线上报错的最关键因素。
rcParams强依赖宿主机已安装的字体,如果代码在 Windows 本地运行正常,部署到没有预装黑体的 Linux 服务器或 CI/CD 容器中就会直接失效;而FontProperties携带字体文件随代码打包分发,彻底解除了对操作系统环境的依赖。 - 维护成本与稳定性平衡:对于日常单机数据分析,采用全局
rcParams代码最清爽;但如果是对外交付的 Python 库、定期生成 PDF/图片报告的定时任务,采用FontProperties才能保证在任何运行环境下视觉输出绝对一致。
四、解决方案三:永久配置与字体缓存清理(一劳永逸法)
如果你希望在个人电脑上每次启动 Python 或 Jupyter 时都无需重复编写配置代码,可以修改 Matplotlib 的全局配置文件。
1. 定位配置文件路径
import matplotlib
print(matplotlib.matplotlib_fname())
# 示例输出:/Users/username/.matplotlib/matplotlibrc
2. 修改 matplotlibrc 关键项
使用文本编辑器打开上述路径输出的 matplotlibrc 文件,搜索并取消以下两行的注释(移除行首的 #),并填入系统字体名称:
font.sans-serif: SimHei, DejaVu Sans, Arial Unicode MS
axes.unicode_minus: False
3. 清理已生成的字体缓存
Matplotlib 会在用户目录缓存系统字体列表,如果不清除缓存,新增或变更的配置可能不会立即生效。运行以下代码可获取缓存目录并进行清理:
import matplotlib
import os
cache_dir = matplotlib.get_cachedir()
print("缓存目录:", cache_dir)
# 手动删除该目录下形如 fontlist-v*.json 的缓存文件
for fname in os.listdir(cache_dir):
if fname.startswith('fontlist') and fname.endswith('.json'):
os.remove(os.path.join(cache_dir, fname))
print(f"已清除旧缓存:{fname}")
Kernel -> Restart 重启内核,否则已加载到内存中的字体管理器不会重新载入配置。
五、排查要点与方案选型决策
总结而言,解决 Python 绘图中文乱码的核心脉络非常清晰:乱码不是数据编码出问题,而是渲染字体缺少字模。遇到方块报错时,可根据项目所处阶段与运行环境,对照下方决策树快速挑选最适合的解决方案。
在完成配置后,只需运行一段同时包含中文字符、负数坐标轴与西文字符的基准图表。当所有文字轮廓清晰完整,且控制台未抛出任何 Glyph missing 警告信息时,即代表绘图中文显示问题已彻底修复。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









