如何用Python或R绘制多层分组的堆叠/并列条形图?
多层分组条形图实现方案(Python + R)
Python 实现(Matplotlib + Seaborn)
思路
先将宽格式数据转为长格式(合并Train/Test score列),通过计算条形位置实现三层分组:最外层按Segment length,中间层按Parameter,最内层按Parameter value,每个值对应并列的Train/Test score条形。
完整代码
import pandas as pd import matplotlib.pyplot as plt import seaborn as sns # 构建目标DataFrame data = pd.DataFrame({ 'Segment length': [16]*9 + [32]*9, 'Parameter': ['n_estimators']*3 + ['max_depth']*3 + ['max_features']*3 + ['n_estimators']*3 + ['max_depth']*3 + ['max_features']*3, 'Parameter value': [5.0,10.0,15.0,2.0,6.0,10.0,0.1,0.3,0.5, 5.0,10.0,15.0,2.0,6.0,10.0,0.1,0.3,0.5], 'Train score': [0.975414,0.982342,1.0,0.580884,1.0,1.0,1.0,1.0,1.0, 0.961905,0.988095,1.0,0.785714,1.0,1.0,1.0,1.0,1.0], 'Test score': [0.807823,0.756803,0.801020,0.284014,0.824830,0.824830,0.845238,0.845238,0.845238, 0.714286,0.857143,0.857143,0.571429,0.857143,0.857143,0.904762,0.904762,0.857143] }) # 转为长格式,适配分组绘图 melted_data = data.melt( id_vars=['Segment length', 'Parameter', 'Parameter value'], value_vars=['Train score', 'Test score'], var_name='Score type', value_name='Score' ) # 设置绘图风格与画布大小 sns.set_style("whitegrid") plt.figure(figsize=(14, 8)) # 定义分组参数与条形尺寸 segment_lengths = melted_data['Segment length'].unique() parameters = melted_data['Parameter'].unique() param_values_per_param = melted_data.groupby('Parameter')['Parameter value'].unique() bar_width = 0.35 segment_gap = len(parameters) * len(param_values_per_param.iloc[0]) * bar_width * 2 + 1 # 循环绘制多层分组条形 for seg_idx, seg_len in enumerate(segment_lengths): seg_data = melted_data[melted_data['Segment length'] == seg_len] for param_idx, param in enumerate(parameters): param_data = seg_data[seg_data['Parameter'] == param] param_values = param_values_per_param[param] for val_idx, val in enumerate(param_values): val_data = param_data[param_data['Parameter value'] == val] # 计算当前条形基准位置 base_pos = seg_idx * segment_gap + param_idx * len(param_values) * bar_width * 2 + val_idx * bar_width * 2 # 绘制Train score条形 plt.bar( base_pos, val_data[val_data['Score type'] == 'Train score']['Score'].values[0], width=bar_width, label='Train score' if seg_idx == 0 and param_idx ==0 and val_idx ==0 else "", color='#1f77b4' ) # 绘制Test score条形 plt.bar( base_pos + bar_width, val_data[val_data['Score type'] == 'Test score']['Score'].values[0], width=bar_width, label='Test score' if seg_idx ==0 and param_idx ==0 and val_idx ==0 else "", color='#ff7f0e' ) # 设置x轴标签与刻度 x_ticks = [] x_labels = [] for seg_idx, seg_len in enumerate(segment_lengths): seg_base = seg_idx * segment_gap for param_idx, param in enumerate(parameters): param_base = seg_base + param_idx * len(param_values_per_param.iloc[0]) * bar_width * 2 for val_idx, val in enumerate(param_values_per_param[param]): tick_pos = param_base + val_idx * bar_width * 2 + bar_width/2 x_ticks.append(tick_pos) x_labels.append(f"{seg_len}\n{param}\n{val}") plt.xticks(x_ticks, x_labels, rotation=45, ha='right') plt.ylabel('Score') plt.title('Multi-grouped Bar Plot: Segment Length → Parameter → Parameter Value') plt.legend() plt.tight_layout() plt.show()
扩展适配
后续扩展到6个Segment length、每个Parameter下10个值时,只需调大画布尺寸(如figsize=(20,8)),代码逻辑无需修改即可自动适配分组数量。
R 实现(ggplot2)
思路
利用ggplot2的分组与分面功能,结合长格式数据,通过facet_wrap处理外层Segment length分组,用position_dodge实现Train/Test条形的并列展示。
完整代码
library(ggplot2) library(tidyr) # 构建目标数据框 data <- data.frame( `Segment length` = rep(c(16,32), each=9), Parameter = rep(c('n_estimators','max_depth','max_features'), each=3, times=2), `Parameter value` = c(5.0,10.0,15.0,2.0,6.0,10.0,0.1,0.3,0.5, 5.0,10.0,15.0,2.0,6.0,10.0,0.1,0.3,0.5), `Train score` = c(0.975414,0.982342,1.0,0.580884,1.0,1.0,1.0,1.0,1.0, 0.961905,0.988095,1.0,0.785714,1.0,1.0,1.0,1.0,1.0), `Test score` = c(0.807823,0.756803,0.801020,0.284014,0.824830,0.824830,0.845238,0.845238,0.845238, 0.714286,0.857143,0.857143,0.571429,0.857143,0.857143,0.904762,0.904762,0.857143) ) # 转为长格式 melted_data <- pivot_longer( data, cols = c(`Train score`, `Test score`), names_to = "Score type", values_to = "Score" ) # 绘制多层分组条形图 ggplot(melted_data, aes(x = interaction(Parameter, `Parameter value`), y = Score, fill = `Score type`)) + geom_bar(stat = "identity", position = position_dodge(width=0.8), width=0.7) + facet_wrap(~`Segment length`, scales = "free_x", nrow=1) + labs(x = "Parameter - Parameter Value", y = "Score", title = "Multi-grouped Bar Plot") + theme( axis.text.x = element_text(angle=45, hjust=1), plot.title = element_text(hjust=0.5), panel.spacing = unit(1, "cm") ) + scale_fill_manual(values = c("#1f77b4", "#ff7f0e"))
扩展适配
扩展分组数量时,ggplot2会自动调整分面布局;若需更紧凑的排版,可修改facet_wrap的nrow参数,或调整theme中的axis.text.x字体大小。
内容的提问来源于stack exchange,提问作者Oskar
相关产品推荐
相关产品推荐

