求助:基于Pandas列调整Matplotlib箱线图样式(颜色、图例、X轴)
问题需求
我需要将当前的Matplotlib箱线图修改为指定样式,具体要求如下:
- 按
Oxidation列的类别设置箱线颜色:oxide为红色,transition为绿色,fresh为蓝色; - 添加对应类别的图例;
- 简化X轴,仅显示元素名称。
以下是数据片段、当前使用的Matplotlib脚本,以及我尝试的Seaborn脚本(但无法自定义分位数和轴样式),恳请协助解决:
数据片段
Oxidation Elements GAI 1.OXIDE Ag_ppm 1 2.TRANS Ag_ppm 1 2.TRANS Ag_ppm 0 2.TRANS Ag_ppm 2 2.TRANS Ag_ppm 2 2.TRANS Ag_ppm 1 3.FRESH Ag_ppm 2 3.FRESH Ag_ppm 0 3.FRESH Ag_ppm 0 3.FRESH Ag_ppm 1 3.FRESH Ag_ppm 0 3.FRESH Ag_ppm 0 1.OXIDE Ag_ppm 0 1.OXIDE Ag_ppm 1 1.OXIDE Ag_ppm 0 1.OXIDE Ag_ppm 0 1.OXIDE Ag_ppm 0 1.OXIDE Ag_ppm 0 1.OXIDE Cu_ppm 1 2.TRANS Cu_ppm 1 2.TRANS Cu_ppm 1 2.TRANS Cu_ppm 1 2.TRANS Cu_ppm 2 2.TRANS Cu_ppm 1 3.FRESH Cu_ppm 2 3.FRESH Cu_ppm 1 3.FRESH Cu_ppm 2 3.FRESH Cu_ppm 2 3.FRESH Cu_ppm 2 3.FRESH Cu_ppm 1 3.FRESH Cu_ppm 2 1.OXIDE Cu_ppm 3 1.OXIDE Cu_ppm 3 1.OXIDE Cu_ppm 3 1.OXIDE Cu_ppm 4 1.OXIDE Cu_ppm 2 1.OXIDE Mg_pct 1 1.OXIDE Mg_pct 1 1.OXIDE Mg_pct 3 1.OXIDE Mg_pct 2 1.OXIDE Mg_pct 1 1.OXIDE Mg_pct 1 1.OXIDE Mg_pct 2 1.OXIDE Mg_pct 2 2.TRANS Mg_pct 2 2.TRANS Mg_pct 2 2.TRANS Mg_pct 2 2.TRANS Mg_pct 2 2.TRANS Mg_pct 2 3.FRESH Mg_pct 2 3.FRESH Mg_pct 2 3.FRESH Mg_pct 2 3.FRESH Mg_pct 2 3.FRESH Mg_pct 2
当前Matplotlib脚本
# Plot boxplots fig, ax = plt.subplots(figsize = (20,8)) # Box plot properties mean = dict(marker = 'x', mec = 'Black', ms = 9) median=dict(color ="Black", lw=1.2) whisker=dict(color ="Black", lw=1.2) cap=dict(color ="Black", lw=1.2) flier=dict(markerfacecolor= 'green', ms=10) key_label = ['Ag','Cu','Mg'] key_ox = ['Oxide','Transition','Fresh','Oxide','Transition','Fresh','Oxide','Transition','Fresh'] col_ox = ['r','g','b','r','g','b','r','g','b'] gai_waste.boxplot(ax=ax, by = ['Elements', 'Oxidation'], column =["GAI"], color = '#ADD8E6', patch_artist=True, whis=[10, 90], #widths=0.2, whiskerprops = whisker, showmeans=True, meanprops = mean, medianprops=median, capprops = cap, showfliers=False, grid = True, rot = 90, fontsize = 'medium') plt.ylabel("GAI", fontsize = 'x-large', fontname='Calibri', weight='bold') plt.xlabel(None) plt.yticks(fontsize = 'large', fontname='Calibri') plt.xticks(np.arange(1, 10, 1),labels=key_ox,ha='center',fontsize = 'large', fontname='Calibri', rotation = 0) ax.yaxis.set_major_formatter(FormatStrFormatter('%.0f')) plt.grid(True, which='both', color='lightgrey',ls = '--') plt.suptitle('') plt.title('') x=2 for i in range(3): plt.text(x = x , y = -5.5, s = str(key_label[i]),horizontalalignment='center', fontsize = 'x-large', weight='bold') x+=3
尝试的Seaborn脚本
fig, ax = plt.subplots(figsize = (20,8)) sns.set_theme(style="ticks", palette="pastel") sns.boxplot(data=gai_waste, x='Elements', y='GAI', hue='Oxidation', palette=['r','g','b'], width=0.5, dodge=True, ax=ax, whis=[10,90], showfliers=False , whiskerprops = whisker, medianprops=median, showmeans=True,meanprops=mean) sns.despine(offset=10, trim=True) plt.xlabel(None) #add legend image img = plt.imread("legend2.jpg") ab = AnnotationBbox(OffsetImage(img, zoom=0.1), (5,-2.5), frameon=True) ax.add_artist(ab)
解决方案
方案1:改进Matplotlib原生脚本
直接基于现有代码修改,完全满足三个需求:
import matplotlib.pyplot as plt import numpy as np from matplotlib.ticker import FormatStrFormatter from matplotlib.patches import Patch # 定义颜色与标签映射 color_map = {"1.OXIDE": "red", "2.TRANS": "green", "3.FRESH": "blue"} label_map = {"1.OXIDE": "Oxide", "2.TRANS": "Transition", "3.FRESH": "Fresh"} fig, ax = plt.subplots(figsize=(20, 8)) # 箱线图样式参数 mean_props = dict(marker='x', mec='Black', ms=9) median_props = dict(color="Black", lw=1.2) whisker_props = dict(color="Black", lw=1.2) cap_props = dict(color="Black", lw=1.2) # 生成箱线图 box = gai_waste.boxplot( ax=ax, by=['Elements', 'Oxidation'], column=["GAI"], patch_artist=True, whis=[10, 90], whiskerprops=whisker_props, showmeans=True, meanprops=mean_props, medianprops=median_props, capprops=cap_props, showfliers=False, grid=True, fontsize='medium' ) # 按类别设置箱线颜色 groups = gai_waste.groupby(['Elements', 'Oxidation']).size().index for patch, (_, ox) in zip(box['boxes'], groups): patch.set_facecolor(color_map[ox]) # 添加自定义图例 legend_elements = [ Patch(facecolor=color_map["1.OXIDE"], label=label_map["1.OXIDE"]), Patch(facecolor=color_map["2.TRANS"], label=label_map["2.TRANS"]), Patch(facecolor=color_map["3.FRESH"], label=label_map["3.FRESH"]) ] ax.legend(handles=legend_elements, loc='upper right', fontsize='large') # 简化X轴:仅显示元素名称 ax.set_xticklabels([]) # 每个元素组的中心位置:2、5、8 element_positions = [2, 5, 8] element_names = ['Ag', 'Cu', 'Mg'] for elem, pos in zip(element_names, element_positions): ax.text(pos, -0.3, elem, horizontalalignment='center', fontsize='x-large', weight='bold', transform=ax.get_xaxis_transform()) # 其他样式调整 plt.ylabel("GAI", fontsize='x-large', fontname='Calibri', weight='bold') plt.xlabel(None) plt.yticks(fontsize='large', fontname='Calibri') ax.yaxis.set_major_formatter(FormatStrFormatter('%.0f')) plt.grid(True, which='both', color='lightgrey', ls='--') plt.suptitle('') plt.title('') plt.tight_layout() plt.show()
方案2:修正Seaborn脚本实现需求
Seaborn支持自定义分位数和轴样式,之前的问题是图例处理不当,修改后代码如下:
import matplotlib.pyplot as plt import seaborn as sns from matplotlib.ticker import FormatStrFormatter # 颜色与标签映射 color_palette = {"1.OXIDE": "red", "2.TRANS": "green", "3.FRESH": "blue"} label_map = {"1.OXIDE": "Oxide", "2.TRANS": "Transition", "3.FRESH": "Fresh"} fig, ax = plt.subplots(figsize=(20, 8)) # 设置Seaborn基础样式 sns.set_theme(style="ticks") # 绘制箱线图,指定分位数与样式 sns.boxplot( data=gai_waste, x='Elements', y='GAI', hue='Oxidation', palette=color_palette, width=0.5, dodge=True, ax=ax, whis=[10, 90], showfliers=False, whiskerprops=dict(color="Black", lw=1.2), medianprops=dict(color="Black", lw=1.2), showmeans=True, meanprops=dict(marker='x', mec='Black', ms=9) ) # 简化X轴标签为仅元素名称 ax.set_xticklabels([label.get_text().split('_')[0] for label in ax.get_xticklabels()], fontsize='x-large', weight='bold') # 修正图例标签 handles, labels = ax.get_legend_handles_labels() ax.legend(handles=handles, labels=[label_map[l] for l in labels], loc='upper right', fontsize='large') # 其他样式调整 plt.ylabel("GAI", fontsize='x-large', fontname='Calibri', weight='bold') plt.xlabel(None) plt.yticks(fontsize='large', fontname='Calibri') ax.yaxis.set_major_formatter(FormatStrFormatter('%.0f')) ax.grid(True, which='both', color='lightgrey', ls='--', axis='y') sns.despine(offset=10, trim=True) plt.tight_layout() plt.show()
内容的提问来源于stack exchange,提问作者BRat
相关产品推荐
相关产品推荐

