You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Seaborn热力图上正确叠加适配对数轴的理论曲线

如何在Seaborn热力图上正确叠加适配对数轴的理论曲线

你遇到的问题本质是热力图的x轴是离散分类轴——虽然你的num_trials/vals是用对数生成的数值,但当你把它们作为热力图的列时,Seaborn会把这些数值当作分类标签渲染,x轴的每个刻度对应一个分类位置(从0、1、2…依次递增),而非原始的数值大小。而你直接用原始数值作为sns.lineplot的x坐标时,Matplotlib会把这些数值当作绝对坐标,和热力图的分类轴完全不匹配,所以曲线才会出现错位。

下面我从你简化的y=1/sqrt(x)例子入手,一步步解决这个问题,搞定这个案例后,你的Sharpe Ratio问题就能直接复用逻辑。

解决步骤

1. 核心问题拆解

热力图的x轴是分类变量:每个num_trials/vals值对应一个固定的分类位置(比如第1个列对应x=0,第2个对应x=1…),和数值本身的大小无关。而理论曲线的x用的是原始数值(比如10、100、1000…),这两个坐标系统完全不兼容,必须做一次映射转换。

2. 修正后的简化版代码(y=1/sqrt(x)案例)

我在代码里标注了关键的映射逻辑,替换你原来的曲线绘制部分即可:

import numpy as np
import pandas as pd
import scipy.stats as stats
import matplotlib.pyplot as plt
import seaborn as sns

vals = np.logspace(0.5, 3.5, 100).astype(int)

theoretical_values = pd.Series(1/np.sqrt(vals), index=vals)

num_runs = 10000
trials_per_run = 10

exprimental_values = np.zeros(shape=(num_runs * len(vals), 2))

for i, n in enumerate(vals):
    for j in range(num_runs):
        dist = stats.norm.rvs(size=(trials_per_run, n)).mean(axis=1)
        exprimental_values[num_runs * i + j, 0] = n
        exprimental_values[num_runs * i + j, 1] = np.std(dist, ddof=1)
            
exprimental_values = pd.DataFrame(exprimental_values, columns=['num', 'std'])
      
heatmap_df = exprimental_values.copy()
heatmap_df['count'] = 1
heatmap_df['std'] = heatmap_df['std'].round(3)
    
heatmap_df = heatmap_df.groupby(['num', 'std'])['count'].sum().reset_index()
heatmap_df = heatmap_df.pivot(index='std', columns='num', values='count') 
heatmap_df = heatmap_df.fillna(0)
heatmap_df = heatmap_df.div(heatmap_df.sum(axis=0), axis=1)
heatmap_df = heatmap_df.sort_index(ascending=False)

fig, ax = plt.subplots()

# 先绘制热力图
sns.heatmap(heatmap_df, cmap='Blues', ax=ax)

# --- 关键:坐标映射修正 ---
# 1. 获取热力图x轴的分类标签,转成数值类型
heatmap_x_labels = [float(label.get_text()) for label in ax.get_xticklabels()]
# 2. 把理论曲线的x值(vals)映射到热力图x轴的分类位置
x_positions = [heatmap_x_labels.index(val) for val in vals]
# 3. 用映射后的位置绘制曲线,确保和热力图x轴对齐
sns.lineplot(
    x=x_positions, 
    y=theoretical_values.values, 
    ax=ax,
    linestyle='dashed',
    color='crimson',
    label=r'$y=1/\sqrt{x}$'
)

# 优化显示:旋转x轴标签避免重叠
ax.set_xticklabels(ax.get_xticklabels(), rotation=45)
plt.legend()
plt.tight_layout()
plt.show()

3. 迁移到你的Sharpe Ratio问题

只需要把同样的映射逻辑套用到你的原始代码中,替换原来的sns.lineplot部分:

# 绘制热力图后添加以下代码
sns.heatmap(heatmap_df, cmap = 'Blues', ax = ax)

# --- 坐标映射修正 ---
heatmap_x_labels = [int(label.get_text()) for label in ax.get_xticklabels()]
x_positions = [heatmap_x_labels.index(num) for num in trials_per_sim_list]

# 绘制对齐后的理论曲线
sns.lineplot(
    x=x_positions,
    y=sharpe_ratio_theoretical['max{SR}'].values,
    linestyle='dashed',
    ax=ax,
    color='crimson',
    label='Theoretical Max SR'
)

plt.show()

额外优化建议

如果希望x轴显示为更直观的对数刻度样式(比如10^1、10^2),可以在绘制完热力图后添加:

from matplotlib.ticker import LogFormatter
ax.set_xticklabels([f'$10^{int(np.log10(float(label.get_text())))}' for label in ax.get_xticklabels()])

这样处理后,理论曲线就能完美和热力图的轴对齐,不会再出现错位的情况了。

备注:内容来源于stack exchange,提问作者Charles0349

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 16:48:12