如何用Python(Pandas/Matplotlib)复现带SEM误差棒的Excel分组柱状图?
解决方案:带SEM误差棒的分组柱状图实现
核心思路
先按Plant、Concentration、Stress分组计算均值和标准误(SEM),再用Matplotlib手动控制柱状图布局,解决Pandas默认绘图分组混乱、样式不佳的问题。
步骤1:导入依赖库
import pandas as pd import matplotlib.pyplot as plt import numpy as np
步骤2:数据预处理(可替换为你的真实数据)
# 模拟符合需求的数据集(替换成你的真实数据即可) np.random.seed(42) # 固定随机种子保证结果可复现 plants = ['Plant A', 'Plant B', 'Plant C'] concentrations = [0, 10, 50, 100] stresses = ['No Stress', 'Stress'] data = pd.DataFrame({ 'Plant': np.random.choice(plants, 240), 'Concentration': np.random.choice(concentrations, 240), 'Stress': np.random.choice(stresses, 240), 'Measurement': np.random.normal(loc=50, scale=10, size=240) + np.where(np.random.choice(stresses, 240) == 'Stress', -10, 0) + np.where(np.random.choice(concentrations, 240) > 0, 5*np.random.choice(concentrations, 240)/100, 0) })
步骤3:计算均值与SEM
SEM公式:SEM = 标准差 / sqrt(样本量)
# 分组计算关键统计量 grouped_stats = data.groupby(['Plant', 'Concentration', 'Stress']).agg( mean=('Measurement', 'mean'), std=('Measurement', 'std'), n=('Measurement', 'count') ).reset_index() # 计算标准误 grouped_stats['sem'] = grouped_stats['std'] / np.sqrt(grouped_stats['n'])
步骤4:绘制美观的分组柱状图
提供两种布局方案,按需选择:
方案1:按植物分面展示(清晰易读)
每个植物单独子图,避免元素拥挤:
plt.figure(figsize=(12, 8)) bar_width = 0.35 unique_plants = grouped_stats['Plant'].unique() unique_concs = grouped_stats['Concentration'].unique() n_concs = len(unique_concs) for idx, plant in enumerate(unique_plants): ax = plt.subplot(1, len(unique_plants), idx+1) plant_data = grouped_stats[grouped_stats['Plant'] == plant] # 拆分胁迫状态数据 no_stress = plant_data[plant_data['Stress'] == 'No Stress'] stress = plant_data[plant_data['Stress'] == 'Stress'] # 设置x轴基准位置 x = np.arange(n_concs) # 绘制无胁迫柱子+误差棒 ax.bar(x - bar_width/2, no_stress['mean'], width=bar_width, label='No Stress', color='#1f77b4') ax.errorbar(x - bar_width/2, no_stress['mean'], yerr=no_stress['sem'], fmt='none', c='black', capsize=5) # 绘制有胁迫柱子+误差棒 ax.bar(x + bar_width/2, stress['mean'], width=bar_width, label='Stress', color='#ff7f0e') ax.errorbar(x + bar_width/2, stress['mean'], yerr=stress['sem'], fmt='none', c='black', capsize=5) # 美化子图 ax.set_xticks(x) ax.set_xticklabels(unique_concs) ax.set_xlabel('Compound Concentration') ax.set_ylabel('Measurement Value') ax.set_title(f'{plant}') ax.legend() plt.tight_layout() plt.show()
方案2:所有植物同图展示(对比直观)
用颜色区分植物,同一浓度下展示两种胁迫状态:
plt.figure(figsize=(14, 8)) bar_width = 0.2 n_plants = len(unique_plants) x = np.arange(n_concs) # 每个浓度的基准位置 for idx, plant in enumerate(unique_plants): plant_data = grouped_stats[grouped_stats['Plant'] == plant] no_stress = plant_data[plant_data['Stress'] == 'No Stress'] stress = plant_data[plant_data['Stress'] == 'Stress'] # 计算柱子偏移量,避免重叠 offset = idx * bar_width * 2 - (n_plants-1)*bar_width # 绘制无胁迫柱子+误差棒 plt.bar(x + offset, no_stress['mean'], width=bar_width, label=f'{plant} - No Stress') plt.errorbar(x + offset, no_stress['mean'], yerr=no_stress['sem'], fmt='none', c='black', capsize=3) # 绘制有胁迫柱子+误差棒 plt.bar(x + offset + bar_width, stress['mean'], width=bar_width, label=f'{plant} - Stress') plt.errorbar(x + offset + bar_width, stress['mean'], yerr=stress['sem'], fmt='none', c='black', capsize=3) # 美化全局样式 plt.xticks(x + bar_width, unique_concs) plt.xlabel('Compound Concentration') plt.ylabel('Measurement Value') plt.title('Plant Responses by Concentration & Stress Treatment') plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left') # 图例放图外避免遮挡 plt.tight_layout() plt.show()
旧方法效果差的原因
data.groupby(...).unstack().plot.bar()是Pandas快捷绘图方式,但会自动把所有分组层级直接映射到x轴,导致标签拥挤、分组逻辑混乱,且误差棒样式和位置难以自定义。手动用Matplotlib绘制可完全控制布局、颜色和误差棒样式,更符合科研展示的美观要求。
内容的提问来源于stack exchange,提问作者rbiem8
相关产品推荐
相关产品推荐

