问题描述
在使用 matplotlib 绘制图表时,如果图表标题、坐标轴标签或图例中包含中文字符,可能会出现以下问题:
- 中文显示为方框:
□□□或[][][] - 中文显示为乱码:显示为其他字符或符号
- 字体警告信息:控制台输出字体相关的警告信息
问题原因
matplotlib 默认使用的字体(如 DejaVu Sans)不支持中文字符,当遇到中文字符时无法正确渲染,导致显示为方框或乱码。
排查步骤
1. 检查系统是否安装中文字体
# 检查系统字体列表(Linux)
fc-list :lang=zh | head -20
# 检查常见中文字体目录
ls -la /usr/share/fonts/truetype/ | grep -i -E "(noto|cjk|han|wqy|wenquan)"
ls -la /usr/share/fonts/opentype/noto/
2. 检查 matplotlib 可用的字体
import matplotlib.font_manager as fm
# 获取所有可用字体
all_fonts = [f.name for f in fm.fontManager.ttflist]
# 查找中文字体
chinese_fonts = [f for f in all_fonts if any(kw in f.lower()
for kw in ['noto', 'cjk', 'han', 'simhei', 'simsun', 'yahei',
'wenquan', 'wqy', 'arphic', 'song', 'hei'])]
print("找到的中文字体:")
for font in sorted(set(chinese_fonts)):
print(f" - {font}")
3. 检查当前 matplotlib 字体配置
import matplotlib.pyplot as plt
# 查看当前字体配置
print("当前字体配置:")
print(f" font.sans-serif: {plt.rcParams['font.sans-serif']}")
print(f" axes.unicode_minus: {plt.rcParams['axes.unicode_minus']}")
4. 测试中文显示
import matplotlib.pyplot as plt
# 简单测试
fig, ax = plt.subplots(figsize=(6, 4))
ax.text(0.5, 0.5, '测试中文显示', ha='center', va='center', fontsize=16)
ax.set_title('中文标题测试')
plt.savefig('/tmp/test_chinese.png', dpi=100, bbox_inches='tight')
plt.close()
解决方案
方案一:自动检测并配置中文字体(推荐)
创建一个通用的字体配置函数,自动检测并设置可用的中文字体:
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import matplotlib
import os
def setup_chinese_font():
"""
配置 matplotlib 使用中文字体,彻底解决中文乱码问题
"""
# 清除字体缓存(如果需要)
cache_dir = matplotlib.get_cachedir()
cache_file = os.path.join(cache_dir, 'fontlist-v330.json')
if os.path.exists(cache_file):
try:
os.remove(cache_file)
except:
pass
# 按优先级尝试的中文字体列表(简体中文优先)
chinese_fonts = [
'Noto Sans CJK SC', # Google Noto 简体中文(推荐)
'Noto Serif CJK SC', # Google Noto 简体中文(衬线)
'WenQuanYi Micro Hei', # 文泉驿微米黑
'WenQuanYi Zen Hei', # 文泉驿正黑
'AR PL UKai CN', # 文鼎PL中楷
'SimHei', # 黑体(Windows)
'Microsoft YaHei', # 微软雅黑(Windows)
'SimSun', # 宋体(Windows)
'STSong', # 华文宋体
'STHeiti', # 华文黑体
'Source Han Sans SC', # 思源黑体
]
# 获取所有可用字体
available_fonts = {f.name: f for f in fm.fontManager.ttflist}
# 查找第一个可用的中文字体
selected_font = None
for font_name in chinese_fonts:
if font_name in available_fonts:
selected_font = font_name
break
# 如果通过名称找不到,尝试通过路径查找
if selected_font is None:
for font in fm.fontManager.ttflist:
font_path_lower = font.fname.lower()
# 检查字体路径中是否包含中文字体关键词
if any(keyword in font_path_lower for keyword in ['noto', 'cjk', 'wqy', 'wenquan', 'arphic']):
# 检查字体名称是否包含中文相关关键词
font_name_lower = font.name.lower()
if any(keyword in font_name_lower for keyword in ['cjk', 'sc', 'chinese', 'han', 'noto']):
selected_font = font.name
break
# 如果还是找不到,尝试直接从系统字体目录加载
if selected_font is None:
system_font_dirs = [
'/usr/share/fonts/opentype/noto',
'/usr/share/fonts/truetype/wqy',
'/usr/share/fonts/truetype/arphic',
'/usr/share/fonts/opentype',
'/usr/share/fonts/truetype',
]
# 优先查找 Noto Sans CJK SC
for font_dir in system_font_dirs:
if os.path.exists(font_dir):
for root, dirs, files in os.walk(font_dir):
for file in files:
if 'notosanscjk' in file.lower() and ('sc' in file.lower() or 'regular' in file.lower()):
font_path = os.path.join(root, file)
try:
# 使用字体文件路径直接设置
font_prop = fm.FontProperties(fname=font_path)
font_name = font_prop.get_name()
plt.rcParams['font.sans-serif'] = [font_name] + plt.rcParams['font.sans-serif']
print(f"✓ 已从系统路径加载中文字体: {font_name} (来自 {font_path})")
plt.rcParams['axes.unicode_minus'] = False
return font_name
except Exception as e:
continue
# 设置字体
if selected_font:
plt.rcParams['font.sans-serif'] = [selected_font] + plt.rcParams['font.sans-serif']
print(f"✓ 已设置中文字体: {selected_font}")
else:
# 如果找不到中文字体,使用默认配置并给出警告
print("⚠ 警告: 未找到中文字体,中文可能显示为方框")
print(" 建议安装: sudo apt-get install fonts-noto-cjk")
plt.rcParams['font.sans-serif'] = ['DejaVu Sans']
# 解决负号显示问题
plt.rcParams['axes.unicode_minus'] = False
return selected_font
# 执行字体配置
setup_chinese_font()
使用方法:
在 Jupyter Notebook 或 Python 脚本的开头调用此函数:
# 在导入 matplotlib 后立即调用
import matplotlib.pyplot as plt
setup_chinese_font()
# 之后正常使用 matplotlib
fig, ax = plt.subplots()
ax.set_title('中文标题')
ax.set_xlabel('横轴标签')
ax.set_ylabel('纵轴标签')
plt.show()
方案二:手动指定字体
如果知道系统中已安装的中文字体名称,可以直接指定:
import matplotlib.pyplot as plt
# 方法1:通过字体名称
plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC', 'WenQuanYi Micro Hei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
# 方法2:通过字体文件路径
from matplotlib.font_manager import FontProperties
font_prop = FontProperties(fname='/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc')
plt.rcParams['font.sans-serif'] = [font_prop.get_name()]
方案三:安装中文字体(如果系统没有)
Ubuntu/Debian 系统
# 安装文泉驿字体
sudo apt-get update
sudo apt-get install fonts-wqy-microhei fonts-wqy-zenhei
# 或安装 Noto 字体(推荐)
sudo apt-get install fonts-noto-cjk
# 安装后清除 matplotlib 字体缓存
rm -rf ~/.cache/matplotlib
CentOS/RHEL 系统
# 安装文泉驿字体
sudo yum install wqy-microhei-fonts wqy-zenhei-fonts
# 或安装 Noto 字体
sudo yum install google-noto-cjk-fonts
macOS 系统
macOS 系统通常自带中文字体,如果遇到问题:
# 清除 matplotlib 字体缓存
rm -rf ~/.matplotlib
Windows 系统
Windows 系统通常自带中文字体(如 SimHei、SimSun、Microsoft YaHei),如果遇到问题:
# 直接使用系统字体
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'SimSun']
plt.rcParams['axes.unicode_minus'] = False
方案四:清除字体缓存并重建
如果字体已安装但 matplotlib 仍无法识别:
import matplotlib
import matplotlib.font_manager as fm
import os
# 清除字体缓存
cache_dir = matplotlib.get_cachedir()
cache_file = os.path.join(cache_dir, 'fontlist-v330.json')
if os.path.exists(cache_file):
os.remove(cache_file)
# 重建字体缓存(需要重启 Python 内核或重新导入)
# 在 Jupyter 中需要重启内核
或者使用命令行:
# 清除 matplotlib 缓存
rm -rf ~/.cache/matplotlib
# 或
rm -rf ~/.matplotlib
完整示例
Jupyter Notebook 中的使用
# Cell 1: 导入库并配置字体
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import matplotlib
import os
def setup_chinese_font():
"""配置中文字体"""
chinese_fonts = [
'Noto Sans CJK SC',
'WenQuanYi Micro Hei',
'AR PL UKai CN',
]
available_fonts = {f.name: f for f in fm.fontManager.ttflist}
for font_name in chinese_fonts:
if font_name in available_fonts:
plt.rcParams['font.sans-serif'] = [font_name]
plt.rcParams['axes.unicode_minus'] = False
print(f"✓ 已设置中文字体: {font_name}")
return font_name
print("⚠ 未找到中文字体")
plt.rcParams['axes.unicode_minus'] = False
return None
setup_chinese_font()
# Cell 2: 绘制图表
import numpy as np
import pandas as pd
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
# 示例数据
scores = [1, 2, 3, 4, 3, 4, 2, 3, 4, 4]
# 1. 直方图
axes[0, 0].hist(scores, bins=20, edgecolor='black', alpha=0.7)
axes[0, 0].set_title('评分分布(直方图)', fontsize=12)
axes[0, 0].set_xlabel('分数')
axes[0, 0].set_ylabel('频数')
axes[0, 0].grid(True, alpha=0.3)
# 2. 箱线图
axes[0, 1].boxplot(scores, vert=True)
axes[0, 1].set_title('评分分布(箱线图)', fontsize=12)
axes[0, 1].set_ylabel('分数')
axes[0, 1].grid(True, alpha=0.3)
# 3. 条形图
score_counts = pd.Series(scores).value_counts().sort_index()
axes[1, 0].bar(score_counts.index, score_counts.values, alpha=0.7, edgecolor='black')
axes[1, 0].set_title('评分计数', fontsize=12)
axes[1, 0].set_xlabel('分数')
axes[1, 0].set_ylabel('数量')
axes[1, 0].grid(True, alpha=0.3, axis='y')
# 4. 累积分布
sorted_scores = np.sort(scores)
cumulative = np.arange(1, len(sorted_scores) + 1) / len(sorted_scores)
axes[1, 1].plot(sorted_scores, cumulative, marker='o', markersize=2)
axes[1, 1].set_title('评分累积分布', fontsize=12)
axes[1, 1].set_xlabel('分数')
axes[1, 1].set_ylabel('累积概率')
axes[1, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
常见问题
Q1: 字体已安装但 matplotlib 仍无法识别
解决方案:
-
清除 matplotlib 字体缓存:
rm -rf ~/.cache/matplotlib -
重启 Python 内核或重新导入 matplotlib
-
使用字体文件路径直接加载(见方案二)
Q2: 在 Jupyter Notebook 中设置后仍显示乱码
解决方案:
- 确保在第一个 Cell 中设置字体配置
- 重启 Jupyter 内核
- 检查是否在多个 Cell 中重复设置导致冲突
Q3: 负号显示为方框
解决方案:
plt.rcParams['axes.unicode_minus'] = False
Q4: 不同操作系统字体名称不同
解决方案:
使用自动检测函数(方案一),它会自动适配不同操作系统。
Q5: 使用 seaborn 时中文仍显示乱码
解决方案:
seaborn 基于 matplotlib,设置 matplotlib 的字体配置即可:
import matplotlib.pyplot as plt
import seaborn as sns
# 先设置 matplotlib 字体
setup_chinese_font()
# 然后使用 seaborn
sns.set_style("whitegrid")
# seaborn 会自动使用 matplotlib 的字体配置
最佳实践
- 统一配置:在项目开头统一配置字体,避免在每个脚本中重复设置
- 自动检测:使用自动检测函数,提高代码的可移植性
- 字体优先级:优先使用 Noto Sans CJK SC,它是 Google 开发的高质量中文字体
- 缓存管理:安装新字体后记得清除 matplotlib 缓存
- 错误处理:在字体配置函数中添加错误处理,避免因字体问题导致程序崩溃
总结
matplotlib 中文乱码问题的核心是字体配置。通过以下步骤可以彻底解决:
- 排查:检查系统字体和 matplotlib 字体列表
- 安装:如果缺少中文字体,安装合适的字体包
- 配置:使用自动检测函数或手动指定字体
- 验证:绘制测试图表确认中文正常显示
使用本文提供的自动检测函数(方案一)是最推荐的解决方案,它能够自动适配不同环境,提高代码的可移植性和健壮性。