使用Matplotlib合并CALL与PUT的CDF图并添加百分位竖线遇问题
问题解决:合并CALL/PUT的CDF柱状图并正确添加百分位竖线
问题背景
已成功单独绘制CALL和PUT未平仓量的CDF柱状图,且每张图都能正确添加40%、80%百分位的竖线,但合并两张图时,生成的图表混乱,竖线位置完全错误。
核心问题分析
- 竖线位置匹配错误:单图绘制时用原DataFrame的索引作为x轴位置,但合并后用
sns.barplot时,x轴是按Strike的唯一值重新排序后的位置,原索引和新x轴位置不对应,导致竖线画错地方。 - 数据合并后的结构问题:直接
concat两张DataFrame,未确保Strike对齐,可能导致柱状图错位或重复。
修正方案与代码
步骤1:确保数据对齐
先让CALL和PUT的DataFrame按Strike列对齐,保证每个Strike同时包含CALL和PUT的CDF数据:
# 按Strike合并CALL和PUT数据,保留双方的CDF和Percentile merged_df = pd.merge(CALL[['STRIKE', 'CDF', 'Percentile']], PUT[['STRIKE', 'CDF', 'Percentile']], on='STRIKE', suffixes=('_call', '_put')) # 转换为适合seaborn长格式的数据 long_df = merged_df.melt(id_vars='STRIKE', value_vars=['CDF_call', 'CDF_put'], var_name='Type', value_name='CDF') long_df['Type'] = long_df['Type'].map({'CDF_call': 'CALL', 'CDF_put': 'PUT'})
步骤2:正确计算竖线位置
绘图时,先获取sns.barplot生成的x轴刻度对应的Strike值,再找到目标Strike在x轴上的位置:
import matplotlib.pyplot as plt import seaborn as sns plt.figure(figsize=(16, 6)) # 绘制双柱状图,每个Strike对应CALL和PUT两个柱子 ax = sns.barplot(x='STRIKE', y='CDF', hue='Type', data=long_df, palette={'CALL': 'lightgreen', 'PUT': 'orange'}) # ---------------------- 处理CALL的百分位竖线 ---------------------- # 找到CALL的40%、80%最近的Strike call_40_strike = CALL.loc[(CALL['Percentile'] - 0.4).abs().idxmin(), 'STRIKE'] call_80_strike = CALL.loc[(CALL['Percentile'] - 0.8).abs().idxmin(), 'STRIKE'] # 获取x轴刻度对应的Strike列表 x_ticks = [tick.get_text() for tick in ax.get_xticklabels()] # 找到目标Strike在x轴上的位置(注意转换为相同类型,比如字符串) call_40_pos = x_ticks.index(str(call_40_strike)) call_80_pos = x_ticks.index(str(call_80_strike)) # 添加竖线,双柱状图每个Strike占两个位置,调整位置让线落在对应柱子中心 ax.axvline(x=call_40_pos + 0.25, color='red', linestyle='--', label=f'CALL 40% ({call_40_strike})') ax.axvline(x=call_80_pos + 0.25, color='darkred', linestyle='--', label=f'CALL 80% ({call_80_strike})') # ---------------------- 处理PUT的百分位竖线 ---------------------- put_40_strike = PUT.loc[(PUT['Percentile'] - 0.4).abs().idxmin(), 'STRIKE'] put_80_strike = PUT.loc[(PUT['Percentile'] - 0.8).abs().idxmin(), 'STRIKE'] put_40_pos = x_ticks.index(str(put_40_strike)) put_80_pos = x_ticks.index(str(put_80_strike)) ax.axvline(x=put_40_pos - 0.25, color='blue', linestyle='--', label=f'PUT 40% ({put_40_strike})') ax.axvline(x=put_80_pos - 0.25, color='darkblue', linestyle='--', label=f'PUT 80% ({put_80_strike})') # 设置图表样式 plt.xlabel('STRIKE') plt.ylabel('Cumulative Distribution Function (CDF)') plt.title('CDF for CALL and PUT Open Interest') plt.xticks(rotation=60) plt.legend() plt.grid(axis='y') plt.tight_layout() plt.show()
关键修正点
- 改用
pd.merge对齐Strike,确保每个Strike都有CALL和PUT的数据,避免柱状图错位。 - 转换为长格式数据适配seaborn的hue参数,生成双柱状图更清晰。
- 通过
ax.get_xticklabels()获取实际x轴的Strike标签,计算竖线的正确位置,解决原索引不匹配的问题。 - 双柱状图中,每个Strike对应两个柱子,通过±0.25调整竖线位置,让线落在对应类型柱子的中心区域。
内容的提问来源于stack exchange,提问作者Ricter
相关产品推荐
相关产品推荐

