4
Matplotlib:把数据画出来
Matplotlib & Seaborn
"一图胜千言"。数据表格看三行就眼花,画成折线图趋势一目了然。Matplotlib 是 Python 画图的祖师爷,简单直接;Seaborn 在它之上封装了更漂亮的统计图表。
基本图表:折线、散点、柱状、饼图
import matplotlib.pyplot as plt
# 折线图
x = [1, 2, 3, 4, 5]
y = [2, 4, 1, 5, 3]
plt.plot(x, y, label="趋势", color="#2E4B3D", marker="o")
plt.title("我的折线图")
plt.xlabel("X 轴")
plt.ylabel("Y 轴")
plt.legend()
plt.savefig("line.png", dpi=150)
plt.show()
# 散点图
plt.scatter(x, y, c="red", s=50)
# 柱状图
names = ["张三", "李四", "王五"]
scores = [90, 85, 78]
plt.bar(names, scores, color="#B98A2F")
# 饼图
plt.pie(scores, labels=names, autopct="%1.1f%%")
# 直方图:看分布
data = [1,2,2,3,3,3,4,5]
plt.hist(data, bins=5)
子图 subplots
# 2 行 2 列,画 4 张图
fig, axes = plt.subplots(2, 2, figsize=(10, 8))
axes[0][0].plot(x, y)
axes[0][0].set_title("折线")
axes[0][1].scatter(x, y)
axes[1][0].bar(names, scores)
axes[1][1].hist(data, bins=5)
plt.tight_layout()
plt.show()
Matplotlib 中文乱码
默认字体不支持中文,标题写中文会变成方块。在最开头加这几行:
import matplotlib.pyplot as plt
plt.rcParams["font.sans-serif"] = ["PingFang SC", "SimHei", "Arial Unicode MS"]
plt.rcParams["axes.unicode_minus"] = False # 负号也正常显示
Seaborn:更美观的统计图表
import seaborn as sns
# 箱线图:看一组数据的分布、离群点
sns.boxplot(data=df, x="部门", y="工资")
# 热力图:看相关性矩阵
corr = df.corr(numeric_only=True)
sns.heatmap(corr, annot=True, cmap="YlGnBu")
更多常用图:箱线图、小提琴图
# 箱线图:看分布、中位数、离群点(须外的点就是异常值)
sns.boxplot(data=df, x="部门", y="工资")
# 小提琴图:箱线图 + 密度估计,更好看
sns.violinplot(data=df, x="部门", y="工资")
# 散点图矩阵:两两看关系(小数据集)
sns.pairplot(df[["成绩", "年龄", "出勤率"]])
# 保存图片:savefig 要在 show() 之前
plt.savefig("result.png", dpi=200, bbox_inches="tight")
ROC 曲线:画出来看模型好坏
from sklearn.metrics import roc_curve, auc
# y_score 是模型对正例的"信心分数",不是 0/1 预测
fpr, tpr, thresholds = roc_curve(y_test, y_score)
roc_auc = auc(fpr, tpr)
plt.plot(fpr, tpr, label=f"ROC (AUC={roc_auc:.2f})")
plt.plot([0, 1], [0, 1], "--", color="gray") # 对角线=瞎猜
plt.xlabel("假正例率")
plt.ylabel("真正例率")
plt.legend()
plt.show()
子图布局进阶:subplots / GridSpec
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
# 1. subplots 简单布局:2 行 2 列
fig, axes = plt.subplots(2, 2, figsize=(10, 7))
axes[0,0].plot(x, np.sin(x)); axes[0,0].set_title("sin")
axes[0,1].plot(x, np.cos(x)); axes[0,1].set_title("cos")
axes[1,0].scatter(x, np.random.rand(100))
axes[1,1].hist(np.random.randn(1000), bins=30)
fig.tight_layout() # 自动调整子图间距
# 2. GridSpec:不规则布局(如第一行跨两列)
from matplotlib.gridspec import GridSpec
fig = plt.figure(figsize=(10, 8))
gs = GridSpec(3, 3, figure=fig)
ax1 = fig.add_subplot(gs[0, :]) # 第0行跨全部3列
ax2 = fig.add_subplot(gs[1, :2]) # 第1行前2列
ax3 = fig.add_subplot(gs[1, 2]) # 第1行第3列
ax4 = fig.add_subplot(gs[2, :]) # 第2行跨全部3列
ax1.plot(x, np.sin(x))
ax2.hist(np.random.randn(1000))
ax3.scatter(x, np.cos(x))
ax4.plot(x, np.tan(x))
fig.tight_layout()
plt.show()
双 Y 轴:同一图画两个不同量纲
# 比如同时画"销售额(元)"和"订单数(单)"
fig, ax1 = plt.subplots(figsize=(10,5))
ax1.plot(x, sales, color="#2E4B3D", label="销售额")
ax1.set_xlabel("月份")
ax1.set_ylabel("销售额(万元)", color="#2E4B3D")
ax2 = ax1.twinx() # 共享 x 轴,新建一个 y 轴
ax2.plot(x, orders, color="#B98A2F", label="订单数")
ax2.set_ylabel("订单数(单)", color="#B98A2F")
plt.title("销售额与订单数")
plt.show()
3D 图与动画
from mpl_toolkits.mplot3d import Axes3D
# 3D 散点图:看三维数据分布
fig = plt.figure(figsize=(8,6))
ax = fig.add_subplot(111, projection="3d")
xs = np.random.rand(100)
ys = np.random.rand(100)
zs = np.random.rand(100)
ax.scatter(xs, ys, zs, c="#B98A2F")
ax.set_xlabel("X"); ax.set_ylabel("Y"); ax.set_zlabel("Z")
plt.show()
# 动画:sin 波传播(需要保存为 mp4/gif)
from matplotlib.animation import FuncAnimation
fig, ax = plt.subplots()
line, = ax.plot([], [], lw=2)
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1.5, 1.5)
def update(frame):
y = np.sin(x + frame/10)
line.set_data(x, y)
return line,
ani = FuncAnimation(fig, update, frames=100, interval=50)
ani.save("wave.gif", writer="pillow", fps=20)
plt.show()
Seaborn 高级统计图
import seaborn as sns
sns.set_theme(style="whitegrid", font="PingFang SC")
# 1. 分面图(FacetGrid):按类别拆成小图
# 比如按"性别"分两列,看不同组的年龄分布
g = sns.FacetGrid(df, col="性别", hue="是否在职")
g.map(sns.histplot, "年龄", bins=20)
# 2. 联合分布图:散点 + 边缘直方图
sns.jointplot(data=df, x="工资", y="工龄", kind="reg")
# 3. 成对关系图:所有数值列两两画散点/分布
sns.pairplot(df[["工资", "年龄", "工龄"]], hue="性别")
# 4. 线性回归拟合 + 置信区间
sns.lmplot(data=df, x="广告投入", y="销售额")
# 5. 热力图:相关性矩阵(EDA 必备)
corr = df.corr(numeric_only=True)
sns.heatmap(corr, annot=True, cmap="YlGnBu", center=0)
# 6. 聚类热图:行列都按相似性排序
sns.clustermap(corr, cmap="RdBu_r", center=0)
Matplotlib 章节面试题
面试 · Matplotlib
Q1. plt.show() 和 plt.savefig() 的顺序?
查看答案
savefig 必须在 show() 之前。show() 会清空画布,先 show 再 savefig 保存出来是空白图。
Q2. 怎么解决中文乱码?
查看答案
开头加 plt.rcParams["font.sans-serif"] = ["PingFang SC", "SimHei", "Arial Unicode MS"] 和 plt.rcParams["axes.unicode_minus"] = False(修负号显示)。
Q3. plt.subplots 和 plt.add_subplot 区别?
查看答案
plt.subplots(n,m) 一次性创建 n×m 个子图,返回 (fig, axes);fig.add_subplot(gs[...]) 用于 GridSpec 不规则布局,一次加一个。
Q4. 双 Y 轴怎么画?
查看答案
ax2 = ax1.twinx(),然后 ax1 画左边量纲、ax2 画右边量纲。共享 x 轴。
Q5. Seaborn 和 Matplotlib 的关系?
查看答案
Seaborn 是 Matplotlib 的高级封装,底层还是 Matplotlib 对象。Seaborn 提供更漂亮的默认样式和现成的统计图表(箱线图、热力图、pairplot);需要精细控制时还是要回到 Matplotlib API。