如何基于Pandas按多列分组绘制可变数量的子图与曲线
如何基于Pandas按多列分组绘制可变数量的子图与曲线
我接触Python已经好几个月了,但偶尔还是会被一些小问题难住 😅 这次想请教一下怎么用Matplotlib从按两列分组的DataFrame里绘制子图,或者有没有相关的方法思路可以参考?
先给你看看我用到的DataFrame示例:
| name | N | coeff | X1 | Y1 | |
|---|---|---|---|---|---|
| 0 | F2 | F2_1 | 1 | 0 | 0.56451 |
| 1 | F2 | F2_1 | 5 | 0 | 0.45005 |
| 2 | F2 | F2_2 | 1 | 25 | 0.53641 |
| 3 | F2 | F2_2 | 5 | 25 | 0.53641 |
| 4 | F2 | F2_3 | 1 | 55 | 0.45067 |
| 5 | F2 | F2_3 | 5 | 55 | 0.38828 |
| 6 | F2 | F2_4 | 1 | 85 | 0.33279 |
| 7 | F2 | F2_4 | 5 | 85 | 0.35315 |
| 8 | F2 | F2_5 | 1 | 95 | 0.2756 |
| 9 | F2 | F2_5 | 5 | 95 | 0.32571 |
| 10 | F2 | F2_6 | 1 | 120 | 0.159 |
| 11 | F2 | F2_6 | 5 | 120 | 0.27583 |
| 12 | F2 | F2_7 | 1 | 150 | 0.05648 |
| 13 | F2 | F2_7 | 5 | 150 | 0.21128 |
| 14 | F3 | F3_1 | 1 | 0 | 0.42757 |
| 15 | F3 | F3_1 | 5 | 0 | 0.32409 |
| 16 | F3 | F3_2 | 1 | 25 | 0.36033 |
| 17 | F3 | F3_2 | 5 | 25 | 0.2701 |
| 18 | F3 | F3_3 | 1 | 55 | 0.2161 |
| 19 | F3 | F3_3 | 5 | 55 | 0.17014 |
| 20 | F3 | F3_4 | 1 | 85 | 0.08191 |
| 21 | F3 | F3_4 | 5 | 85 | 0.10512 |
| 22 | F3 | F3_5 | 1 | 95 | 0.04468 |
| 23 | F3 | F3_5 | 5 | 95 | 0.09686 |
| 24 | F3 | F3_6 | 1 | 120 | 0.07025 |
| 25 | F3 | F3_6 | 5 | 120 | 0.033 |
| 26 | F3 | F3_7 | 1 | 150 | 0.10689 |
| 27 | F3 | F3_7 | 5 | 150 | 0.01334 |
我现在是按name和coeff这两列分组,想要实现的效果是:每个name对应一个子图,每个子图里再绘制不同coeff对应的曲线(比如示例里F2和F3各占一个子图,每个子图里有coeff=1和coeff=5两条曲线)。之前按单列分组绘图没什么问题,但多列分组的逻辑我就有点绕不过来了,麻烦帮忙解答下~
别担心,我来一步步给你拆解这个需求,其实多列分组绘图的核心就是先按一级分组(比如name)来生成子图,再在每个子图里遍历二级分组(比如coeff)来画曲线,具体可以这么做:
步骤1:导入需要的库
首先得把Pandas和Matplotlib导入进来:
import pandas as pd import matplotlib.pyplot as plt
步骤2:准备你的DataFrame(这里用你给的示例数据)
先把示例数据转成DataFrame,当然你也可以直接用自己的数据源:
data = [ ["F2", "F2_1", 1, 0, 0.56451], ["F2", "F2_1", 5, 0, 0.45005], ["F2", "F2_2", 1, 25, 0.53641], ["F2", "F2_2", 5, 25, 0.53641], ["F2", "F2_3", 1, 55, 0.45067], ["F2", "F2_3", 5, 55, 0.38828], ["F2", "F2_4", 1, 85, 0.33279], ["F2", "F2_4", 5, 85, 0.35315], ["F2", "F2_5", 1, 95, 0.2756], ["F2", "F2_5", 5, 95, 0.32571], ["F2", "F2_6", 1, 120, 0.159], ["F2", "F2_6", 5, 120, 0.27583], ["F2", "F2_7", 1, 150, 0.05648], ["F2", "F2_7", 5, 150, 0.21128], ["F3", "F3_1", 1, 0, 0.42757], ["F3", "F3_1", 5, 0, 0.32409], ["F3", "F3_2", 1, 25, 0.36033], ["F3", "F3_2", 5, 25, 0.2701], ["F3", "F3_3", 1, 55, 0.2161], ["F3", "F3_3", 5, 55, 0.17014], ["F3", "F3_4", 1, 85, 0.08191], ["F3", "F3_4", 5, 85, 0.10512], ["F3", "F3_5", 1, 95, 0.04468], ["F3", "F3_5", 5, 95, 0.09686], ["F3", "F3_6", 1, 120, 0.07025], ["F3", "F3_6", 5, 120, 0.033], ["F3", "F3_7", 1, 150, 0.10689], ["F3", "F3_7", 5, 150, 0.01334], ] df = pd.DataFrame(data, columns=["name", "N", "coeff", "X1", "Y1"])
步骤3:按多列分组并绘制子图
这里的思路是:
- 先获取所有唯一的
name值,确定子图的数量 - 创建对应的子图布局
- 遍历每个
name,在对应的子图里,再按coeff分组,绘制每条曲线 - 加上图例、标题这些美化元素,让图更清晰
具体代码如下:
# 获取所有唯一的name,确定子图数量 unique_names = df["name"].unique() num_plots = len(unique_names) # 创建子图布局,这里用1行多列,你也可以根据数量调整成多行 fig, axes = plt.subplots(nrows=1, ncols=num_plots, figsize=(12, 5), sharey=True) # 如果只有一个子图,axes会是单个对象,需要转成列表统一处理 if num_plots == 1: axes = [axes] # 遍历每个name和对应的子图 for ax, name in zip(axes, unique_names): # 筛选当前name的数据 name_data = df[df["name"] == name] # 按coeff分组,绘制每条曲线 for coeff, group in name_data.groupby("coeff"): # 绘制X1 vs Y1的曲线,这里可以根据你的需求调整x和y列 ax.plot(group["X1"], group["Y1"], marker='o', label=f'coeff={coeff}') # 设置子图标题 ax.set_title(f'Name: {name}') # 设置坐标轴标签 ax.set_xlabel('X1') ax.set_ylabel('Y1') # 添加图例 ax.legend() # 调整子图间距,避免重叠 plt.tight_layout() # 显示图形 plt.show()
代码小解释
- 首先通过
df["name"].unique()拿到所有要画的子图主题,这样不管你有多少个name,子图数量都会自动适配,不用手动改代码 - 用
plt.subplots创建子图时,sharey=True可以让所有子图的Y轴刻度一致,方便不同name的数据对比 - 嵌套循环是核心:外层循环处理每个
name对应的子图,内层循环在子图里按coeff分组,把每个分组的数据绘制成一条带标记点的曲线 - 最后用
tight_layout()自动调整布局,避免标签和标题重叠,让图看起来更清爽
这样运行代码后,就能得到你想要的效果:每个name一个子图,每个子图里有不同coeff的曲线啦~
备注:内容来源于stack exchange,提问作者Nakira




