为什么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画图时中文显示为方块或乱码的问题,核心在于指定一个支持中文字符的字体。你有以下几种主要的选择:

  1. 运行时设置rcParams 最快捷方便,适用于当前脚本或会话。通过mpl.rcParams['font.family'] = '中文字体名'mpl.rcParams['axes.unicode_minus'] = False进行设置。
  2. 修改matplotlibrc配置文件: 实现持久化设置,影响该Matplotlib环境下的所有图表。找到配置文件,修改font.familyaxes.unicode_minus两项。
  3. 使用FontProperties 为图表中特定的文本元素单独设置字体。通过fm.FontProperties()创建字体对象,并在绘图函数的fontproperties参数中传入。

无论选择哪种方法,都需要确保你指定的字体在你的操作系统中是存在的。可以使用fm.findSystemFonts()或遍历fm.FontManager().ttflist来查找可用的字体名称或路径。如果修改设置后不生效,可以尝试清除Matplotlib的字体缓存。掌握这些方法,你就能轻松应对Python绘图中的中文显示问题了。

python画图显示中文