打开一份精心调试好的 pandas 图表,中文标题却变成了一排排小方块,这大概是每个用中文做数据分析的人都躲不过的经历。问题的根源说起来挺简单,pandas 底层默认调用的是 matplotlib,而 matplotlib 出身国外,它内置的默认字体压根没收录汉字字形,自然也就无从渲染。搞清楚这一点之后,剩下的事情就是找到一套既美观又不惹版权麻烦的字体方案,顺便让代码在换电脑、换系统、甚至换到服务器上跑的时候都不会掉链子。
🧭 问题的本质:字体渲染链路
要把这事讲透,得先弄明白 matplotlib 显示文字时到底发生了什么。它并不是简单地把字符扔给操作系统去画,而是自己维护一套字体查找机制,通过 font.family 和 font.sans-serif 这两个 rcParams 参数,按优先级顺序去系统里搜可用字体 。如果列表里排在前面的字体在当前系统上根本不存在,matplotlib 就会悄悄跳过,最终落到默认的 DejaVu Sans 上,而这个字体只认拉丁字符,中文自然全灭。

这也解释了一个很常见的坑,很多人在 Windows 上用 SimHei 用得好好的,代码搬到 Mac 或者 Linux 服务器上就直接报错找不到字体,因为 SimHei 压根是 Windows 系统自带的黑体,其他平台根本没有这个文件 。
🔍 常见的三种修复思路,从糙到雅
思路一,改配置文件(不推荐长期用)
最原始的做法是直接找到 matplotlib 的 matplotlibrc 配置文件,把 font.sans-serif 改成系统里确实存在的中文字体名字。这种方法能解决眼前的问题,但换一台机器就得重来一遍,团队协作时每个人的环境还不一样,维护成本高得让人抓狂。
思路二,代码里临时指定字体名(够用但不够稳)
python
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False # 负号显示问题也得顺手解决
这行代码在很多教程里能看到,胜在简单,但隐患也很明显,它依赖当前系统里恰好装了这个字体 。如果哪天代码要跑在 Docker 容器或者云端 Linux 服务器上,而那台机器压根没装中文字体,照样抓瞎 。另外 axes.unicode_minus 这行也别漏掉,中文字体渲染负号时经常会变成方块,把这个参数设为 False 能让 matplotlib 老老实实用 ASCII 减号。
思路三,直接把字体文件打包进项目(真正优雅的做法)
这才是能一劳永逸、跨平台通用的方案。核心思路是不依赖系统预装字体,而是把字体文件本身当作项目资源来管理 ,通过 font_manager.addfont() 动态注册,运行时指哪打哪。
ini
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import os, urllib.request
FONT_PATH = "fonts/SourceHanSansSC-Regular.otf"
# 如果本地没有字体文件,自动下载一份(也可以提前放进项目仓库)
if not os.path.exists(FONT_PATH):
os.makedirs("fonts", exist_ok=True)
url = ("https://github.com/adobe-fonts/source-han-sans/raw/release/"
"OTF/SimplifiedChinese/SourceHanSansSC-Regular.otf")
urllib.request.urlretrieve(url, FONT_PATH)
fm.fontManager.addfont(FONT_PATH)
font_name = fm.FontProperties(fname=FONT_PATH).get_name()
plt.rcParams['font.family'] = font_name
plt.rcParams['axes.unicode_minus'] = False
这种写法的好处在于,字体文件跟着项目代码一起走,Git 仓库里放一份,或者启动脚本里自动下载一份,团队里谁拉下代码谁能跑,服务器上部署也不用额外装字体。matplotlib 社区其实也一直在讨论能不能让 rcParams 直接支持文件路径而不是字体名,就是为了解决这种环境不一致的痛点 。
💡 商用字体避坑指南
这一部分才是真正容易被忽略的地方。很多人图省事直接用 SimHei 或者微软雅黑,殊不知这两款字体版权归属微软和方正,属于操作系统捆绑授权,严格来说并不允许在商业项目里自由分发和嵌入使用。如果只是自己写论文、做课堂作业当然没人较真,但一旦涉及给客户交付报告、公司内部产品对外发布,就存在授权风险。
好消息是,开源世界里有几款明确允许商用、可自由分发的高质量中文字体,完全可以替代那些版权敏感的选项。
| 字体名称 | 发布方 | 开源协议 | 商用是否可行 |
|---|---|---|---|
| 思源黑体 Source Han Sans | Adobe / Google | SIL Open Font License 1.1 | ✅ 完全免费商用 |
| Noto Sans CJK | SIL Open Font License 1.1 | ✅ 完全免费商用 | |
| 文泉驿微米黑 WenQuanYi | 开源社区 | GPL / Apache 双授权 | ✅ 可商用(注意 GPL 传染性条款) |
| 阿里巴巴普惠体 | 阿里巴巴 | 免费商用授权协议 | ✅ 商用需遵循官方声明 |
| HarmonyOS Sans | 华为 | 免费商用授权协议 | ✅ 商用需遵循官方声明 |
| 微软雅黑 / 黑体 SimHei | 微软 / 方正 | 系统捆绑,非开源 | ⚠️ 商业嵌入分发存在风险 |
从实操角度看,思源黑体和 Noto Sans CJK 其实是同一套字形数据的两个马甲版本,由 Adobe 和 Google 联合开发,走的是最宽松的 SIL 开源字体协议,无论个人还是企业都能放心用,甚至可以直接嵌入软件产品里分发,不需要额外申请授权,这也是为什么它成了大多数数据可视化教程里的首选替代方案。
🔧 完整的一揽子代码模板
把上面所有思路揉在一起,实际项目里推荐用下面这套写法,兼顾跨平台 、免版权风险 、自动兜底三个诉求。
python
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import os
import urllib.request
import pandas as pd
def setup_chinese_font():
"""一次性配置好中文绘图环境,跨平台、免商用授权风险"""
font_dir = "fonts"
font_path = os.path.join(font_dir, "SourceHanSansSC-Regular.otf")
if not os.path.exists(font_path):
os.makedirs(font_dir, exist_ok=True)
url = ("https://github.com/adobe-fonts/source-han-sans/raw/"
"release/OTF/SimplifiedChinese/SourceHanSansSC-Regular.otf")
print("正在下载思源黑体,首次运行需要一点耐心......")
urllib.request.urlretrieve(url, font_path)
fm.fontManager.addfont(font_path)
font_name = fm.FontProperties(fname=font_path).get_name()
plt.rcParams['font.family'] = font_name
plt.rcParams['axes.unicode_minus'] = False
print(f"字体配置完成,当前使用:{font_name}")
# 只需在脚本开头调用一次
setup_chinese_font()
# 后面正常用 pandas 画图,中文会自动正常显示
df = pd.DataFrame({'月份': ['一月', '二月', '三月'], '销售额': [120, 150, 90]})
df.plot(x='月份', y='销售额', kind='bar', title='季度销售趋势')
plt.show()
这套模板放进项目的初始化脚本里,团队每个人拉下代码就能直接跑,不用再互相排查为什么你能显示中文我不行这种玄学问题。
📌 写在最后
中文字体在 pandas 绘图里之所以年年被人吐槽,本质上是国外开源工具的默认设计没考虑非拉丁语系文字,而解决方案的关键就落在两件事上,一是让字体查找脱离对特定系统预装字体的依赖,二是选对开源协议清晰、能放心商用的字体本体。把思源黑体这类字体文件打包进项目、通过 addfont 动态注册,基本能一次配置、处处能用,团队协作、跨平台部署、商业交付这几件事同时就都不用再操心了。
参考资料
Matplotlib Official Documentation, Configure the font family , matplotlib.org/stable/gall...
Stack Overflow, How to mix Chinese and English with MatPlotLib , stackoverflow.com/questions/4...
Streamlit Community Discourse, How to use Chinese font in matplotlib visuals , discuss.streamlit.io/t/how-to-us...
Matplotlib GitHub Issues, ENH: Explicit file paths for font rcParams , github.com/matplotlib/...