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

在heatmap上绘制带标签点:是否有更优实现方法?

在Seaborn热力图上绘制带标签点的更优方法

问题描述

我需要在已有的热力图上绘制特定的带标签点,目前已通过一段代码实现预期效果,但想知道是否存在更简洁高效的实现方式。

现有实现代码:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# 创建热力图数据
df = pd.DataFrame({
    'x': [1.1, 1.1, 2.1, 1.1, 2.1, 2.1],
    'y': [1.4, 2.4, 1.4, 3.4, 2.4, 3.4],
    'value': [9, 8, 1, 0, 6, 2]
})

# 转换为热力图所需的透视表格式
df = df.pivot(index='y', columns='x', values='value')

# 绘制热力图
fig, ax = plt.subplots()
sns.heatmap(df, annot=False, cmap='viridis')

# 待绘制的点数据
points = pd.DataFrame({
    'x': [1.8, 2.1],
    'y': [2.3, 1.0]
})

# 获取坐标轴标签和位置,手动转换坐标
x_labels = ax.get_xticklabels()
x_tick_start = x_labels[0].get_position()[0]
x_label_start = float(x_labels[0].get_text())
x_tick_end = x_labels[-1].get_position()[0]
x_label_end = float(x_labels[-1].get_text())
y_labels = ax.get_yticklabels()
y_tick_start = y_labels[0].get_position()[1]
y_label_start = float(y_labels[0].get_text())
y_tick_end = y_labels[-1].get_position()[1]
y_label_end = float(y_labels[-1].get_text())

# 转换点坐标到热力图网格坐标系
points['x_new'] = points['x'].apply(lambda x: ((x - x_label_start) * (x_tick_end - x_tick_start)) / (x_label_end - x_label_start) + x_tick_start)
points['y_new'] = points['y'].apply(lambda y: ((y - y_label_start) * (y_tick_end - y_tick_start)) / (y_label_end - y_label_start) + y_tick_start)

# 绘制散点
ax.scatter(x=points['x_new'], y=points['y_new'], color='black')

# 添加标签
for i, row in points.iterrows():
    plt.text(row['x_new']+0.04, row['y_new']+0.05, i+1)

plt.show()

更优解决方案

可以利用Matplotlib内置的坐标转换功能,或借助Numpy的线性插值实现坐标映射,避免手动计算的冗余步骤,代码更简洁可靠:

方法一:使用ax.transLimits快速转换坐标

ax.transLimits可直接将数据坐标转换为轴的归一化坐标,再映射到显示坐标,步骤更简洁:

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# 创建热力图数据
df = pd.DataFrame({
    'x': [1.1, 1.1, 2.1, 1.1, 2.1, 2.1],
    'y': [1.4, 2.4, 1.4, 3.4, 2.4, 3.4],
    'value': [9, 8, 1, 0, 6, 2]
})
df = df.pivot(index='y', columns='x', values='value')

# 绘制热力图
fig, ax = plt.subplots()
sns.heatmap(df, annot=False, cmap='viridis')

# 点数据(直接包含标签)
points = pd.DataFrame({'x': [1.8, 2.1], 'y': [2.3, 1.0], 'label': [1, 2]})

# 转换坐标:利用transLimits将数据坐标转为轴坐标
points['x_ax'] = points['x'].apply(lambda x: ax.transLimits.transform((x, 0))[0])
points['y_ax'] = points['y'].apply(lambda y: ax.transLimits.transform((0, y))[1])

# 绘制点和标签
for _, row in points.iterrows():
    ax.scatter(row['x_ax'], row['y_ax'], color='black')
    ax.text(row['x_ax'] + 0.02, row['y_ax'] + 0.02, row['label'], color='red', fontweight='bold')

plt.show()

方法二:基于Numpy线性插值映射坐标

通过热力图的x/y轴刻度值和对应位置,用线性插值快速完成坐标转换:

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

# 创建热力图数据
df = pd.DataFrame({
    'x': [1.1, 1.1, 2.1, 1.1, 2.1, 2.1],
    'y': [1.4, 2.4, 1.4, 3.4, 2.4, 3.4],
    'value': [9, 8, 1, 0, 6, 2]
})
df = df.pivot(index='y', columns='x', values='value')

# 绘制热力图
fig, ax = plt.subplots()
sns.heatmap(df, annot=False, cmap='viridis')

# 获取热力图的x/y轴刻度值和对应轴位置
x_vals = df.columns.values
x_pos = ax.get_xticks()
y_vals = df.index.values
y_pos = ax.get_yticks()

# 点数据
points = pd.DataFrame({'x': [1.8, 2.1], 'y': [2.3, 1.0], 'label': [1, 2]})

# 用Numpy线性插值转换坐标
points['x_ax'] = np.interp(points['x'], x_vals, x_pos)
points['y_ax'] = np.interp(points['y'], y_vals, y_pos)

# 绘制点和标签
for _, row in points.iterrows():
    ax.scatter(row['x_ax'], row['y_ax'], color='black', s=50)
    ax.text(row['x_ax'] + 0.05, row['y_ax'] + 0.05, row['label'], fontsize=12)

plt.show()

方案优势

  • 代码更简洁,避免手动计算坐标转换的冗余逻辑
  • 依赖内置函数实现转换,可靠性更高,不易出错
  • 可读性更强,后续维护成本更低

内容的提问来源于stack exchange,提问作者edgar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 00:04:51