数据分析通常不会停留在“算出结果”,最后往往还要把结果展示出来。Matplotlib 就是 Python 生态中最经典、最基础的可视化库。
很多更高级的可视化库,本质上也和 Matplotlib 有密切关系,所以它很值得作为第一站学习。
一、为什么需要可视化
同一组数据,直接打印数字和画成图,理解成本通常完全不同。
例如你要看:
- 销量是上涨还是下跌
- 不同城市之间差距有多大
- 某些值是否明显异常
- 数据分布是集中还是分散
这些问题,用图往往比看纯文本更直观。
二、Matplotlib 的基本使用流程
最常见的写法是:
import matplotlib.pyplot as plt
这里的 pyplot 可以理解为“一组便捷画图接口”。
把基础绘图流程记成四步:
- 准备横轴和纵轴数据
- 选择图表类型,例如
plot()画折线 - 添加标题和坐标轴说明
- 调用
show()展示
一个最常见的入门示例:
import matplotlib.pyplot as plt
months = ["Jan", "Feb", "Mar", "Apr"]
sales = [120, 150, 130, 180]
plt.plot(months, sales) # 根据横轴和纵轴数据画出折线
plt.title("Monthly Sales") # 添加图表标题
plt.xlabel("Month") # 设置横轴名称
plt.ylabel("Sales") # 设置纵轴名称
plt.show() # 把图表窗口显示出来

这段代码已经包含了 Matplotlib 入门最常用的一整套操作:导包、画折线、补充标题和坐标轴标签、最后显示图表。
💡 提示:中文显示问题 如果你在标题或标签中使用了中文(如
plt.title("月度销量")),默认情况下可能会显示为方块(乱码)。 解决方法是在代码开头加上两行配置:#按顺序尝试可用字体 plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'Arial Unicode MS'] plt.rcParams['axes.unicode_minus'] = False # 确保负号正常显示这组
rcParams配置是 Matplotlib 中很常见的中文显示修复方式,适合初学者先掌握。
💡 不弹窗、图形窗口打不开问题 如果你遇到
plt.show()不弹窗、图形窗口打不开,那通常是后端问题,不属于中文乱码本身。 此时才可能需要显式指定后端,而且必须写在导入pyplot之前,例如:import matplotlib matplotlib.use('TkAgg') import matplotlib.pyplot as plt但
TkAgg更适合本地桌面脚本环境,在 Jupyter、远程服务器或无图形界面的环境中并不一定适用。
三、折线图:看趋势
折线图最适合看“随时间变化”的趋势,比如月销量、日温度、网站访问量等连续变化的数据。
上面的基础示例本身就是折线图。使用 plt.plot(x, y) 时,Matplotlib 会按照数据顺序把各个点连接起来,从而表现整体走势。
折线图里最常见的几个参数有:
marker="o":给每个数据点加上标记,便于观察具体位置color="blue":设置线条颜色linestyle="--":设置线条样式,例如虚线label="Sales":给当前线条命名,后续可配合plt.legend()显示图例
如果数据本质上是在比较离散类别,而不是观察连续趋势,那么通常优先考虑柱状图会更合适。
四、柱状图:看类别对比
如果你要比较不同类别的数值,柱状图通常更合适。
import matplotlib.pyplot as plt
cities = ["Beijing", "Shanghai", "Shenzhen"]
orders = [320, 410, 360]
plt.bar(cities, orders)
plt.title("Orders by City")
plt.xlabel("City")
plt.ylabel("Orders")
plt.show()
运行效果示意:

柱状图很适合展示:
- 各城市销量对比
- 各产品销售额对比
- 各班级平均分对比
五、饼图:看占比
如果你更关心“整体里各部分各占多少”,饼图会比柱状图更直观。
import matplotlib.pyplot as plt
labels = ["Food", "Rent", "Transport", "Other"]
values = [35, 40, 15, 10]
plt.pie(
values,
labels=labels,
autopct="%1.1f%%",
startangle=90,
)
plt.title("Monthly Expense Ratio")
plt.axis("equal") # 保证饼图是正圆,而不是椭圆
plt.show()

这里常见的几个参数含义是:
labels:每一块对应的类别名称autopct:在图上显示百分比格式startangle:控制起始绘制角度,让图形更便于阅读axis("equal"):让横纵坐标比例一致,保证图形不变形
饼图更适合展示“组成占比”,例如:
- 月度支出结构
- 各渠道流量占比
- 各产品销售额占比
如果类别太多,或者各项数值差异很小,饼图就不如柱状图清楚,这时优先考虑 bar() 会更稳妥。
六、散点图:看相关性
当你想观察两个变量之间有没有某种关系时,散点图很有用。
import matplotlib.pyplot as plt
study_hours = [1, 2, 3, 4, 5, 6]
scores = [55, 60, 68, 72, 80, 88]
plt.scatter(study_hours, scores)
plt.title("Study Hours vs Score")
plt.xlabel("Study Hours")
plt.ylabel("Score")
plt.show()
运行效果示意:

如果点大致沿着某个方向分布,你就能直观看到变量之间的趋势关系。
七、直方图:看分布
当你想看一组数据大致集中在哪些区间、是否分布均匀时,直方图会很有帮助。
import matplotlib.pyplot as plt
scores = [55, 60, 62, 68, 70, 72, 75, 80, 82, 88, 90, 95]
plt.hist(scores, bins=5, edgecolor="black")
plt.title("Score Distribution")
plt.xlabel("Score")
plt.ylabel("Count")
plt.show()
运行效果示意:

这里:
bins=5表示把数据大致分成 5 个区间edgecolor="black"让每个柱子的边界更清楚
八、设置样式与图例
import matplotlib.pyplot as plt
months = ["Jan", "Feb", "Mar", "Apr"]
product_a = [120, 150, 140, 180]
product_b = [100, 130, 160, 170]
plt.plot(months, product_a, label="Product A", color="blue", linestyle="-")
plt.plot(months, product_b, label="Product B", color="green", linestyle="--")
plt.title("Sales Comparison")
plt.xlabel("Month")
plt.ylabel("Sales")
plt.legend()
plt.show()
常见可调项包括:
color颜色linestyle线型marker点样式label图例名称
九、figure 和 axes
后面你会逐渐接触:
figure:整张画布axes:画布中的具体子图区域(图里真正画线、画柱子的区域)
例如:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [2, 4, 3])
ax.set_title("Demo")
plt.show()
这种写法在多子图场景下更常见,也更适合后续进阶。
十、图表大小与子图布局
很多时候一张图还不够,你可能想把多个图放在同一行里对比,同时控制图片大小。
import matplotlib.pyplot as plt
months = ["Jan", "Feb", "Mar", "Apr"]
sales = [120, 150, 130, 180]
profit = [30, 45, 35, 60]
fig, axes = plt.subplots(1, 2, figsize=(10, 4))
axes[0].plot(months, sales, marker="o")
axes[0].set_title("Sales")
axes[1].bar(months, profit)
axes[1].set_title("Profit")
plt.tight_layout()
plt.show()
运行效果示意:

这段代码里:
figsize=(10, 4)控制整张图的宽和高plt.subplots(1, 2)表示创建 1 行 2 列的子图tight_layout()用来自动调整间距,避免标题或标签挤在一起
十一、保存图片
很多时候我们不只是看图,还要把图保存到文件。
import matplotlib.pyplot as plt
x = [1, 2, 3]
y = [3, 5, 4]
plt.plot(x, y)
plt.title("Example")
plt.savefig("example.png", dpi=150)
常见注意点:
savefig()一般放在show()前面dpi越高,导出图片通常越清晰- 文件格式可以是
png、jpg、svg、pdf
十二、和 Pandas 联动的常见方式
在数据分析里,Matplotlib 往往不是直接手写全部数据,而是接收 Pandas 处理后的结果。
常见分析流程通常是:
- 用
Pandas读取和清洗数据 - 用
groupby或聚合得到结果 - 用
Matplotlib把结果画成图
案例:
# TODO 需求: 获取中美日三个国家 GDP 数据, 并绘制每年变化折线图
# 1. 导包
import matplotlib
matplotlib.use("TkAgg") # 解决个别本地环境 show() 不弹窗的问题,必须写在 pyplot 之前
import matplotlib.pyplot as plt
import pandas as pd
# 解决中文乱码和负号显示问题
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
# 2. 读取数据
# 注意:以后更常见的是通过读取文件生成 DataFrame 对象
df = pd.read_csv("data/1960-2019全球GDP数据.csv", encoding="gbk", sep=",")
# 3. 先了解数据
print(df.shape) # 形状 (9931, 3)
# print(df.ndim) # 维度
# print(df.head()) # 如果不传参数,默认显示前 5 行
# print(df.tail()) # 如果不传参数,默认显示后 5 行
# print(df.columns) # 获取所有列名
# print(df.count()) # 各列的非空数据个数
# df.info() # 查看基本信息
# 4. 数据预处理
df.dropna(inplace=True)
print(df.shape) # 形状 (9930, 3)
# 5. 数据分析
# TODO 需求 1:先查询中美日的 GDP 数据
#加copy()就不会有提醒,否则会提醒:SettingWithCopyWarning:
#A value is trying to be set on a copy of a slice from a DataFrame
df_cn = df[df["country"] == "中国"].copy()
df_us = df[df["country"] == "美国"].copy()
df_jp = df[df["country"] == "日本"].copy()
# TODO 需求 2:把年份作为索引列
df_cn.set_index("year", inplace=True)
df_us.set_index("year", inplace=True)
df_jp.set_index("year", inplace=True)
# TODO 需求 3:把 GDP 列名依次修改为中国、美国、日本
df_cn.rename(columns={"GDP": "中国"}, inplace=True)
df_us.rename(columns={"GDP": "美国"}, inplace=True)
df_jp.rename(columns={"GDP": "日本"}, inplace=True)
# TODO 需求 4:可视化展示中美日的每年变化折线
plt.title("1960-2019 年中美日 GDP 数据折线图")
plt.plot(df_cn.index, df_cn["中国"], label="中国", color="red")
plt.plot(df_us.index, df_us["美国"], label="美国", color="blue")
plt.plot(df_jp.index, df_jp["日本"], label="日本", color="green")
plt.legend() # 添加图例
plt.grid() # 添加网格
plt.show() # 手动展示
"""
样例数据
1960-2019全球GDP数据.csv
year,country,GDP
1960,美国,543300000000
1960,英国,73233967692
1960,法国,62225478000
1960,中国,59716467625
1960,日本,44307342950
1960,加拿大,40461721692
1960,意大利,40385288344
1960,印度,37029883875
1960,澳大利亚,18577668271
"""
# 1.导包
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
# 解决个别版本show()无法展示图的问题
import matplotlib
matplotlib.use('TkAgg')
# 解决中文乱码问题
plt.rcParams['font.sans-serif'] = ['SimHei']
# 额外安装: pip install openpyxl
# 2.加载数据
df = pd.read_excel("data/sales_test.xlsx", index_col='USERID')
# print(df)
# print(df.index)
print(df.shape) # (9, 3)
# 3.数据预处理
# 删除全部为空的行
sale_data = df.dropna(how='all')
print(sale_data.shape)
print(sale_data.columns)
# 4.数据分析
# todo 1.获取每个用户最近的购买时间,购买频率,购买金额
F_data = sale_data.groupby(sale_data.index)['ORDERID'].count()
M_data = sale_data.groupby(sale_data.index)['AMOUNTINFO'].sum()
R_data = sale_data.groupby(sale_data.index)['ORDERDATE'].max()
# print(R_data)
# print(F_data)
# print(M_data)
print('===========================================================')
# TODO 2.分别计算RFM的分数
F_score = pd.cut(F_data, 5, labels=[1, 2, 3, 4, 5])
M_score = pd.cut(M_data, 5, labels=[1, 2, 3, 4, 5])
# 设置一个基础时间,计算R分数
base_date = pd.to_datetime("2022-04-01")
R_days = (R_data - base_date).dt.days
R_score = pd.cut(R_days, 5, labels=[1, 2, 3, 4, 5])
# print(R_score)
# print(F_score)
# print(M_score)
print('===========================================================')
# TODO 3.合并分数
rfm_list = [R_score, F_score, M_score]
rfm_cols = ['r_score', 'f_score', 'm_score']
# 注意: .transpose()把3行6列转换为6行3列
rfm_df = pd.DataFrame(np.array(rfm_list).transpose(), dtype=np.int32, columns=rfm_cols, index=R_data.index)
print(rfm_df)
# TODO 4.加权得分
# 商家根据自己的需求:设置关注用户交易时间,次数,金额的权重
# 比如: 我设置交易时间:20%,交易总次数:20%,交易总金额:80%
rfm_df['rfm_w_score'] = rfm_df['r_score'] * 0.2 + rfm_df['f_score'] * 0.2 + rfm_df['m_score'] * 0.6
# TODO 5.1 分析结果可视化
# 自定义分层阈值,设置会员等级
bins = [0, 2, 3, 5]
labels = ['低', '中', '高']
# 打分
rfm_df['客户分类'] = pd.cut(rfm_df['rfm_w_score'], bins=bins, labels=labels)
print(rfm_df)
# 绘制柱状图
rfm_df['客户分类'].value_counts().plot(kind='bar',color=['red','green','pink'], rot=0)
plt.title('RFM客户分群')
plt.xlabel('会员等级')
plt.ylabel('会员数量')
plt.grid()
plt.show()
# TODO 5.2 分析结果保存到本地
rfm_df.to_csv('data/rfm_score.txt')
#样例数据:data/sales_test.xlsx
"""
ORDERDATE ORDERID AMOUNTINFO
USERID
1.0 2023-05-01 11.0 1000.0
2.0 2023-04-02 22.0 2000.0
3.0 2023-04-03 33.0 3000.0
4.0 2023-04-04 44.0 4000.0
5.0 2023-04-05 55.0 5000.0
6.0 2023-04-05 66.0 NaN
2.0 2022-04-02 77.0 300.0
NaN NaT NaN NaN
2.0 2022-04-23 88.0 800.0
6.0 2022-04-23 99.0 1500.0
NaN NaT NaN NaN
"""
小结
Matplotlib 解决的是“把分析结果直观展示出来”的问题。只要掌握基础绘图流程、常见图表类型和几个常用配置项,你就已经能完成大量日常分析展示任务。
它的学习门槛不算最低,但回报非常高,因为几乎所有数据分析工作最终都绕不开可视化表达。