匹配两个DataFrame数据并在箱线图添加文本的实现及报错排查
问题解决:箱线图顶部添加匹配DataFrame的文本并修复ConversionError
错误原因分析
- 直接报错原因:原代码中
mean[xtick]返回的是(组名, 对应Series)的元组,matplotlib无法将该元组转换为y轴的数值单位,因此抛出ConversionError。 - 数据匹配逻辑缺陷:原代码通过
18*i+j的索引计算无法准确定位到df1中满足A == df2['num']且C == df2['S']的行,匹配逻辑不符合数据结构。
完整解决方案代码
import pandas as pd import numpy as np import seaborn as sns import matplotlib.pyplot as plt np.random.seed(12) # 修正数据生成笔误:col_a改为col_a1 col_a1 = [i for i in range(18)] * 15 col_b1 = np.random.randint(1, 100, len(col_a1)) configs = ['c1', 'c2', 'c3', 'c4'] col_c1 = [configs[i//90] + '_' + f'abctrct{i//18}' for i in range(len(col_a1))] df1 = pd.DataFrame({'A': col_a1, 'B': col_b1, 'C': col_c1}) # 生成第二个DataFrame col_d2 = [s + '-' +f'{np.random.randint(1,18)}' for s in list(set(col_c1))] df2 = pd.DataFrame({'D': col_d2, 'E': np.random.randint(100, 200, len(col_d2))}) # 拆分df1的C列,用于绘图分组 df1[['Hue', 'X']] = df1['C'].str.split('_', expand=True) # 绘制图形:添加dodge=True确保不同Hue的图形对齐 fig, ax = plt.subplots(figsize=(12,6)) sns.stripplot(y='B', x='X', data=df1, hue='Hue', dodge=True) sns.boxplot(y='B', x='X', data=df1, hue='Hue', dodge=True) plt.xticks(rotation=45, ha='right') # ---------------------- 数据匹配:获取需要显示的B和A值 ---------------------- # 拆分df2的D列,提取匹配标识和A值 df2[['S', 'num']] = df2['D'].str.split('-', expand=True) df2['num'] = df2['num'].astype(int) # 拆分S为Hue和X,与df1保持一致,方便关联 df2[['Hue', 'X']] = df2['S'].str.split('_', expand=True) # 合并df1和df2,精准匹配满足条件的行 merged = pd.merge(df1, df2, left_on=['C', 'A'], right_on=['S', 'num'], how='inner') display_data = merged[['X', 'Hue', 'B', 'A']].drop_duplicates() # ---------------------- 定位箱线位置,添加文本 ---------------------- # 建立X类别与x轴位置的映射 x_categories = ax.get_xticklabels() x_pos_dict = {label.get_text(): idx for idx, label in enumerate(x_categories)} # 计算不同Hue的偏移量(适配dodge=True的布局) box_width = 0.8 n_hues = len(df1['Hue'].unique()) dodge_width = box_width / n_hues hue_order = ['c1', 'c2', 'c3', 'c4'] hue_pos_dict = {hue: -box_width/2 + dodge_width/2 + i*dodge_width for i, hue in enumerate(hue_order)} # 遍历每个匹配项,添加带边框的文本 for _, row in display_data.iterrows(): x_base = x_pos_dict[row['X']] x_pos = x_base + hue_pos_dict[row['Hue']] # 获取对应箱线的顶部y坐标 box_top = None for patch in ax.artists: box_center_x = patch.get_x() + patch.get_width()/2 if abs(box_center_x - x_pos) < 0.1: box_top = patch.get_y() + patch.get_height() break if not box_top: box_top = df1['B'].max() * 1.1 # fallback到y轴最大值上方 # 添加B值文本 ax.text(x_pos, box_top + 2, f'B={row["B"]}', horizontalalignment='center', verticalalignment='bottom', fontsize=10, bbox={'facecolor': 'green', 'alpha': 0.5, 'pad': 3}) # 添加A值文本 ax.text(x_pos, box_top + 6, f'A={row["A"]}', horizontalalignment='center', verticalalignment='bottom', fontsize=10, bbox={'facecolor': 'yellow', 'alpha': 0.5, 'pad': 3}) plt.tight_layout() plt.show()
关键修复说明
- 修正数据生成笔误:将原代码中的
col_a改为col_a1,避免运行时索引长度不匹配的错误。 - 图形对齐优化:为
stripplot和boxplot添加dodge=True,确保不同Hue的图形横向对齐,方便后续文本定位。 - 精准数据匹配:使用
pd.merge替代原有的索引计算,通过C和A字段精准关联两个DataFrame,避免索引逻辑错误。 - 解决坐标错误:通过遍历matplotlib的艺术家对象(
ax.artists)获取每个箱线的实际顶部y坐标,替代原代码中错误的元组值,彻底解决ConversionError。 - 文本定位适配:计算每个Hue的横向偏移量,确保文本准确对应到所属的箱线位置。
内容的提问来源于stack exchange,提问作者A.E
相关产品推荐
相关产品推荐

