为什么Python画图时中文会变成方块或乱码?
当你使用Python的Matplotlib、Seaborn等库绘制图表时,如果在标题、轴标签、图例或文本注释中使用了中文,经常会遇到中文无法正常显示,而是变成一堆方块(□□□□)或者问号(????)的情况。
出现这个问题的主要原因在于:
- 默认字体问题: Matplotlib等绘图库在不同的操作系统上会有一套默认的字体设置。这些默认字体通常是英文字体(如DejaVu Sans、Arial等),它们只包含了西文字符的字形(glyphs),而不包含中文字符的字形。当遇到需要显示中文字符时,由于字体中没有对应的字形信息,就无法正确渲染,只能显示为占位符,也就是方块或问号。
- 编码问题(较少见但可能): 在极少数情况下,如果你的脚本文件编码、系统环境编码与Matplotlib期望的编码不一致,也可能导致中文解析错误。但这在现代Python 3环境中较少发生,绝大多数问题根源是字体。
因此,解决这个问题的核心在于告诉绘图库使用一个支持中文字符的字体。
这个问题会出现在图的哪些位置?
几乎所有需要显示文本的地方都可能受到影响,包括但不限于:
- 图表主标题 (
plt.title()) - X轴和Y轴的标签 (
plt.xlabel(),plt.ylabel()) - 轴刻度上的标签 (
plt.xticks(),plt.yticks()) - 图例 (
plt.legend()) - 文本注释 (
plt.text(),plt.annotate()) - 图形内的文本框 (
plt.figtext(),plt.suptitle())
如果这些位置使用了中文字符串,且没有进行相应的字体配置,就会出现显示问题。
如何解决Python画图中文显示问题?
有几种常见且有效的方法来解决中文显示问题,可以根据你的需求选择最适合的方法:
方法一:在脚本中运行时设置(推荐快速解决)
这是最常用、最快捷的方法,直接在你的Python脚本开头设置Matplotlib的全局参数。这种方法只对当前运行的脚本或当前的Python会话有效。
设置字体
通过修改matplotlib.rcParams字典来设置默认字体。你需要指定一个你的操作系统中已经安装且支持中文的字体名称。
import matplotlib.pyplot as plt
import matplotlib as mpl
# --- 设置中文显示字体 ---
# 选择一个系统中存在的支持中文的字体
# 例如: 'SimHei' (黑体), 'KaiTi' (楷体), 'FangSong' (仿宋), 'Microsoft YaHei' (微软雅黑) 等
# 请根据你的操作系统安装的字体选择
mpl.rcParams['font.family'] = 'SimHei'
# 或者尝试列表形式,Matplotlib会按顺序查找
# mpl.rcParams['font.family'] = ['SimHei', 'Arial Unicode MS']
# --- 解决负号'-'显示为方块的问题 ---
# 有些字体设置后,负号可能显示不正常,需要额外设置
mpl.rcParams['axes.unicode_minus'] = False
# --- 示例绘图 ---
plt.plot([1, 2, 3], [4, 5, 6])
plt.xlabel('时间 (单位: 小时)')
plt.ylabel('温度 (单位: ℃)')
plt.title('温度随时间变化图')
plt.show()
解释:
mpl.rcParams['font.family'] = 'SimHei':将全局字体族设置为’SimHei’(黑体)。这是一个在Windows系统上常见的、支持中文的字体。在其他系统上,你可能需要使用不同的字体名,比如在macOS上可能是’PingFang SC’,在Linux上可能是’WenQuanYi Zen Hei’或’Source Han Sans CN’等。如果不知道有哪些字体可用,请参考后文“如何找到系统中可用的中文字体?”部分。mpl.rcParams['axes.unicode_minus'] = False:当你设置了中文字体后,Matplotlib可能会使用Unicode编码来显示负号(-),但在某些字体下,这个Unicode字符可能显示不正常,仍然是方块。将axes.unicode_minus设置为False,强制Matplotlib使用ASCII字符集的负号,通常可以解决这个问题。
这种方法简单直接,非常适合于单个脚本或临时测试。
方法二:修改Matplotlib配置文件(更持久的设置)
如果你希望你的Matplotlib绘图在所有脚本中都能默认显示中文,而不需要每次都写上面的配置代码,可以修改Matplotlib的配置文件matplotlibrc。
查找配置文件位置
你可以通过Python代码找到你的matplotlibrc文件所在的目录:
import matplotlib as mpl import os print(mpl.matplotlib_fname()) # 通常会输出一个文件路径,例如: # /path/to/your/python/site-packages/matplotlib/mpl-data/matplotlibrc # 或在用户目录下的某个缓存文件夹中
找到这个文件路径后,使用文本编辑器打开它。
修改配置文件
在打开的matplotlibrc文件中,找到以下两行(可能被注释掉,即前面有#号):
#font.family : sans-serif #axes.unicode_minus: True
取消这两行的注释(去掉前面的#号),并修改其值:
font.family : SimHei # 或者你选择的其他中文字体名 axes.unicode_minus: False
保存并关闭文件。重启你的Python环境(如Jupyter Notebook、IDE等),新的配置就会生效。以后你在Matplotlib或Seaborn中绘制的图表就应该默认显示中文了。
注意: 如果修改用户目录下的配置文件不生效,可能需要找到并修改Matplotlib安装目录下的那个文件,但这通常不推荐,因为它会影响所有使用该Python环境的用户,且在更新Matplotlib时可能会被覆盖。推荐优先查找用户目录下的配置文件。
方法三:为特定文本元素单独设置字体
如果你不想修改全局或配置文件的设置,只想为图中的某个特定文本元素(如某个标题或某个注释)设置中文字体,可以使用matplotlib.font_manager.FontProperties。
导入FontProperties
首先导入需要的模块:
import matplotlib.pyplot as plt import matplotlib.font_manager as fm
创建FontProperties对象并使用
你需要创建一个FontProperties对象,并在相应的绘图函数中通过fontproperties参数传入。你可以通过字体名称或字体文件路径来指定字体。
通过字体名称:
# 创建一个FontProperties对象,指定中文字体名
# 同样请确保系统中存在此字体
my_font = fm.FontProperties(fname='/path/to/your/font.ttf', size=14) # 或者使用font_manager查找的路径
# 或者只指定名称(不指定fname时,Matplotlib会尝试查找)
# my_font = fm.FontProperties(family='SimHei', size=14)
# 示例绘图
plt.plot([1, 2, 3], [4, 5, 6])
# 在需要显示中文的地方使用 fontproperties 参数
plt.xlabel('时间 (单位: 小时)', fontproperties=my_font)
plt.ylabel('温度 (单位: ℃)', fontproperties=my_font)
plt.title('温度随时间变化图', fontproperties=my_font)
# 解决负号问题(如果使用FontProperties单独设置字体,负号问题需要另外处理或在rcParams中全局设置)
# 如果rcParams['axes.unicode_minus'] = False 已经设置,这里通常不需要额外操作
plt.show()
通过字体文件路径:
如果你知道某个字体文件的确切路径(例如,你下载了一个字体文件),可以直接指定该文件的路径:
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
# 指定字体文件路径
font_path = '/path/to/your/SimHei.ttf' # 替换为你系统中实际的字体文件路径
# 创建FontProperties对象
my_font = fm.FontProperties(fname=font_path, size=14)
# 示例绘图,并在需要中文的地方使用 fontproperties
plt.plot([1, 2, 3], [4, 5, 6])
plt.xlabel('时间 (单位: 小时)', fontproperties=my_font)
plt.ylabel('温度 (单位: ℃)', fontproperties=my_font)
plt.title('温度随时间变化图', fontproperties=my_font)
plt.show()
这种方法的好处是可以为图表中不同的文本元素设置不同的字体(比如标题用黑体,轴标签用楷体),或者使用一个不安装在系统默认字体目录下的字体文件。缺点是代码会比较冗长。
如何找到系统中可用的中文字体?
要解决中文显示问题,关键在于知道系统中有哪些支持中文的字体以及它们的名称或路径。
常见系统字体路径
不同的操作系统,字体文件通常存放在不同的位置:
- Windows:
C:\Windows\Fonts\ - macOS:
/Library/Fonts/或/System/Library/Fonts/或~/Library/Fonts/(用户字体) - Linux:
/usr/share/fonts/或/usr/local/share/fonts/或~/.local/share/fonts/(用户字体)
你可以在这些目录下查找以.ttf, .ttc, .otf等后缀结尾的文件,通常字体名会和文件名或文件夹名相关。常见的中文字体文件及对应名称(用于rcParams或FontProperties):
- Windows:
simsun.ttc(宋体, SimSun),simhei.ttf(黑体, SimHei),msyh.ttc(微软雅黑, Microsoft YaHei),msjhl.ttc(Microsoft JhengHei Light, 微软正黑体 Light) - macOS:
PingFang.ttc(苹方, PingFang SC/TC/HK),Heiti.ttc(黑体-简/繁, Heiti SC/TC),STHeiti.ttf(华文黑体) - Linux:
wqy-zenhei.ttc(文泉驿点阵正黑, WenQuanYi Zen Hei),SourceHanSansCN-Regular.otf(思源黑体 CN, Source Han Sans CN),NotoSansCJK-Regular.ttc(Noto Sans CJK)
使用Matplotlib查找可用字体
Matplotlib提供了一些工具来帮助你查找系统中的字体。
可以使用matplotlib.font_manager.findSystemFonts()找到所有系统字体文件的路径:
import matplotlib.font_manager as fm
# 查找所有字体文件路径
font_files = fm.findSystemFonts(fontpaths=None, fontext='ttf')
# fontext='ttf' 可以指定只查找ttf文件,或其他后缀
print(f"找到 {len(font_files)} 个 .ttf 字体文件。")
# 打印部分文件路径示例
for i, font_file in enumerate(font_files[:10]):
print(font_file)
if i == 9:
print("...")
找到字体文件路径后,你可以通过FontProperties(fname='字体文件路径').get_name()来获取该字体的内部名称,这个名称可以在rcParams['font.family']中使用。
更直接的方法是列出Matplotlib已经缓存的字体列表:
import matplotlib.font_manager as fm
# 获取Matplotlib的字体管理器
font_manager = fm.FontManager()
# 遍历字体列表,查找包含'Hei'或'YaHei'等字样的字体名称
print("尝试查找包含'Hei'或'YaHei'的字体名称:")
for font in font_manager.ttflist:
# font.name 是字体的内部名称
# font.fname 是字体文件的路径
if 'Hei' in font.name or 'YaHei' in font.name or 'KaiTi' in font.name or 'FangSong' in font.name or 'Song' in font.name or 'PingFang' in font.name:
print(f"Name: {font.name}, Path: {font.fname}")
# 你也可以直接打印所有字体的名称来查看
# print("\n所有可用字体名称:")
# for font in font_manager.ttflist:
# print(font.name)
通过这些方法,你可以找到系统中可用且支持中文的字体名称(如’SimHei’, ‘Microsoft YaHei’, ‘PingFang SC’等),然后将这个名称填入rcParams['font.family']或FontProperties(family='...')中。
清除Matplotlib字体缓存
有时候,即使你安装了新的字体或者修改了配置文件,Matplotlib可能因为使用了旧的字体缓存而没有立即识别。这时,你需要清除Matplotlib的字体缓存。
Matplotlib的字体缓存通常位于用户目录下的一个隐藏文件夹中,比如~/.cache/matplotlib(Linux/macOS)或C:\Users\YourUser\.matplotlib(Windows)。在这个文件夹里找到以fontlist-开头的文件并删除它。
或者,你可以在Python脚本中执行以下代码来重建字体缓存(这会花费一些时间,因为它会重新扫描系统中的所有字体):
import matplotlib.font_manager as fm
print("正在重建Matplotlib字体缓存,请稍候...")
fm._rebuild()
print("字体缓存重建完成。")
执行完重建后,重启你的Python环境,再尝试绘图,通常就可以识别新字体或应用配置更改了。
这些方法对Seaborn等库有效吗?
是的,这些方法对Seaborn是完全有效的。Seaborn是一个基于Matplotlib的高级数据可视化库,它的大部分绘图函数底层都调用了Matplotlib的功能。因此,通过修改matplotlib.rcParams来设置全局字体,或者使用FontProperties为特定文本元素设置字体,同样会影响到Seaborn绘制的图表中的中文显示。
当你设置了mpl.rcParams['font.family'] = 'SimHei'和mpl.rcParams['axes.unicode_minus'] = False后,使用Seaborn绘制图表(例如seaborn.countplot(), seaborn.heatmap()等),其中的中文标签、标题等通常都能正常显示。
其他一些基于Matplotlib的绘图库,如Pandas自带的绘图功能(DataFrame.plot()等),也会受到这些设置的影响。
总结
解决Python画图时中文显示为方块或乱码的问题,核心在于指定一个支持中文字符的字体。你有以下几种主要的选择:
- 运行时设置
rcParams: 最快捷方便,适用于当前脚本或会话。通过mpl.rcParams['font.family'] = '中文字体名'和mpl.rcParams['axes.unicode_minus'] = False进行设置。 - 修改
matplotlibrc配置文件: 实现持久化设置,影响该Matplotlib环境下的所有图表。找到配置文件,修改font.family和axes.unicode_minus两项。 - 使用
FontProperties: 为图表中特定的文本元素单独设置字体。通过fm.FontProperties()创建字体对象,并在绘图函数的fontproperties参数中传入。
无论选择哪种方法,都需要确保你指定的字体在你的操作系统中是存在的。可以使用fm.findSystemFonts()或遍历fm.FontManager().ttflist来查找可用的字体名称或路径。如果修改设置后不生效,可以尝试清除Matplotlib的字体缓存。掌握这些方法,你就能轻松应对Python绘图中的中文显示问题了。