You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

咨询:基于pH分组的聚合DataFrame适用可视化图表及实现方法

问题与解决方案

问题背景

我是数据分析新手,通过以下代码用groupby和agg生成了包含多指标(温度、浊度等)min/max/mean统计值的多层索引DataFrame:

df_ph = df2.groupby('pH').agg(['min','max','mean'])

该DataFrame的结构如下:

{('Temperature (°C)', 'min'): {6.83: 22.5,
  6.89: 23.6,
  6.9: 22.5,
  6.92: 21.4,
  6.94: 23.2},
 ('Temperature (°C)', 'max'): {6.83: 22.5,
  6.89: 23.6,
  6.9: 22.5,
  6.92: 21.7,
  6.94: 23.2},
 ('Temperature (°C)', 'mean'): {6.83: 22.5,
  6.89: 23.6,
  6.9: 22.5,
  6.92: 21.549999999999997,
  6.94: 23.2},
 ('Turbidity (NTU)', 'min'): {6.83: 3.3,
  6.89: 4.6,
  6.9: 4.2,
  6.92: 4.7,
  6.94: 4.0},
 ('Turbidity (NTU)', 'max'): {6.83: 3.3,
  6.89: 4.6,
  6.9: 4.2,
  6.92: 4.9,
  6.94: 4.0},
 ('Turbidity (NTU)', 'mean'): {6.83: 3.3,
  6.89: 4.6,
  6.9: 4.2,
  6.92: 4.800000000000001,
  6.94: 4.0},
 ('Dissolved Oxygen (mg/L)', 'min'): {6.83: 6.1,
  6.89: 7.2,
  6.9: 6.0,
  6.92: 6.3,
  6.94: 6.6},
 ('Dissolved Oxygen (mg/L)', 'max'): {6.83: 6.1,
  6.89: 7.2,
  6.9: 6.0,
  6.92: 6.8,
  6.94: 6.6},
 ('Dissolved Oxygen (mg/L)', 'mean'): {6.83: 6.1,
  6.89: 7.2,
  6.9: 6.0,
  6.92: 6.55,
  6.94: 6.6},
 ('Conductivity (µS/cm)', 'min'): {6.83: 348,
  6.89: 320,
  6.9: 357,
  6.92: 362,
  6.94: 348},
 ('Conductivity (µS/cm)', 'max'): {6.83: 348,
  6.89: 320,
  6.9: 357,
  6.92: 363,
  6.94: 348},
 ('Conductivity (µS/cm)', 'mean'): {6.83: 348.0,
  6.89: 320.0,
  6.9: 357.0,
  6.92: 362.5,
  6.94: 348.0}}

尝试用以下代码绘图未得到合理结果:

df.plot(xticks=df2['pH'], ylabel='pH')

需要以pH为核心的嵌套式或更优的可视化方案(基于matplotlib/seaborn)。


核心思路:先规整数据格式

多层索引的DataFrame不直接适合绘图,第一步要转成长格式(melt),让每个行对应一个(pH, 指标, 统计量, 值)的组合:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# 转成长格式
df_melt = df_ph.reset_index().melt(
    id_vars='pH',
    var_name=['Metric', 'Stat'],
    value_name='Value'
)

方案1:子图矩阵(每个指标单独展示统计值)

适合对比不同pH下,单个指标的min/max/mean变化,逻辑清晰,可读性强。

Matplotlib实现

metrics = df_melt['Metric'].unique()
n_metrics = len(metrics)

# 创建子图布局
fig, axes = plt.subplots(nrows=n_metrics, ncols=1, figsize=(8, 4*n_metrics), sharex=True)

for ax, metric in zip(axes, metrics):
    # 筛选当前指标数据
    subset = df_melt[df_melt['Metric'] == metric]
    # 绘制每个统计量的折线
    for stat in ['min', 'max', 'mean']:
        stat_data = subset[subset['Stat'] == stat]
        ax.plot(stat_data['pH'], stat_data['Value'], marker='o', label=stat)
    # 设置子图样式
    ax.set_title(f'{metric} 随pH的变化')
    ax.set_ylabel(metric.split('(')[1].strip(')'))
    ax.legend()

plt.xlabel('pH')
plt.tight_layout()
plt.show()

Seaborn简化实现

用FacetGrid自动生成子图,代码更简洁:

g = sns.FacetGrid(df_melt, col='Metric', col_wrap=2, height=4, sharey=False)
g.map(sns.lineplot, 'pH', 'Value', 'Stat', marker='o')
g.add_legend(title='统计量')
g.set_xlabels('pH')
g.set_titles('{col_name}')
plt.tight_layout()
plt.show()

方案2:分组柱状图(同图展示所有指标的统计值)

适合在一张图里对比同一pH下,不同指标的min/max/mean差异,视觉冲击力强。

# 先将统计量转为列,方便分组
df_pivot = df_melt.pivot(index=['pH', 'Metric'], columns='Stat', values='Value').reset_index()

fig, ax = plt.subplots(figsize=(12, 6))
bar_width = 0.25
x_base = range(len(df_pivot['pH'].unique()))
metrics = df_pivot['Metric'].unique()

# 遍历每个统计量绘制柱状图
for i, stat in enumerate(['min', 'max', 'mean']):
    # 整理每个pH下所有指标的统计值
    values = []
    for ph in df_pivot['pH'].unique():
        values.extend(df_pivot[df_pivot['pH'] == ph][stat].values)
    # 计算柱子的x位置
    x_pos = [pos + i*bar_width for pos in x_base for _ in metrics]
    ax.bar(x_pos, values, width=bar_width, label=stat)

# 设置x轴标签
ax.set_xticks([pos + bar_width for pos in x_base])
ax.set_xticklabels([f'pH={ph}' for ph in df_pivot['pH'].unique()])

# 添加指标分组的图例
from matplotlib.patches import Patch
metric_legend = [Patch(facecolor=f'C{j}', label=metric) for j, metric in enumerate(metrics)]
stat_legend = ax.get_legend_handles_labels()
ax.legend(handles=metric_legend + stat_legend[0], loc='upper right', bbox_to_anchor=(1.2, 1))

ax.set_ylabel('数值')
ax.set_title('不同pH下各指标的统计值对比')
plt.tight_layout()
plt.show()

方案3:误差区间图(突出波动范围)

如果更关注指标在每个pH下的波动(min-max范围),可以用填充区间或误差棒展示:

# 以温度指标为例
temp_subset = df_melt[df_melt['Metric'] == 'Temperature (°C)']
ph_values = temp_subset['pH'].unique()

# 提取各统计量的值
mean_vals = temp_subset[temp_subset['Stat'] == 'mean']['Value'].values
min_vals = temp_subset[temp_subset['Stat'] == 'min']['Value'].values
max_vals = temp_subset[temp_subset['Stat'] == 'max']['Value'].values

fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(ph_values, mean_vals, marker='o', label='平均值')
ax.fill_between(ph_values, min_vals, max_vals, alpha=0.2, label='波动范围(Min-Max)')

ax.set_title('温度随pH的变化及波动范围')
ax.set_xlabel('pH')
ax.set_ylabel('温度 (°C)')
ax.legend()
plt.show()

为什么之前的代码失效?

  • 多层索引的DataFrame直接调用plot()会导致列名混乱,绘图逻辑不明确;
  • xticks=df2['pH']用了原始数据中的重复pH值,导致x轴刻度重叠、错乱。转成长格式后可以彻底避免这类问题。

内容的提问来源于stack exchange,提问作者jugunnu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 18:34:59