Lullapalanza
Lullapalanza

Reputation: 21

Interactive sns.heatmap displays x, y only hovering over the colorbar

The example script produces a simple figure. When hovering over the data the coordinates in the corner are (x=, y=).

No coordinates for the plot

While hovering the colorbar shows something, but the data without the coordinate is interpreted as y.

Coordinates for the colorbar

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

data = np.array([[15, 10], [20, 10]])

sns.heatmap(data, linewidth=0.01, square=True)
plt.show()

Environment Versions:

Ubuntu        22.04.2 LTS
python        3.11
matplotlib    3.7.1
numpy         1.24.3
seaborn       0.12.2

I tried to dig around the attributes of the axes of the seaborn generated figure, but no luck. I didn't find other stackoverflow/other forum questions when searching to solve this.

EDIT: Of course the cursor is not visible when taking a snippet of the screen -.-

Upvotes: 2

Views: 375

Answers (1)

MestreLion
MestreLion

Reputation: 13666

This is not a problem with Seaborn per se, as the underlying issue is a Matplotlib limitation: if you set labels for an axis, as seaborn does, the mouseover info for that axis is blank.

To test, let's try the same code without seaborn, with and without labels:

import numpy as np
import matplotlib.pyplot as plt

data = np.array([[15, 10], [20, 10]])

def heatmap(title):
    plt.title(title)
    plt.colorbar(plt.imshow(data, cmap="inferno"))
    plt.xticks(np.arange(data.shape[0]))
    plt.yticks(np.arange(data.shape[0]))

heatmap("No Labels")
plt.show()

heatmap("Labels on X")
plt.gca().set_xticklabels("AB")
plt.show()

heatmap("Labels on X and Y")
plt.gca().set_xticklabels("AB")
plt.gca().set_yticklabels("CD")
plt.show()

No Labels Labels on X Labels on X and Y

(You may have noticed that in all three cases there's an additional info in the mouseover as compared to seaborn: the data value at the (x, y) coordinate, in this case [15.00]. This is a courtesy of imshow(), while (unfortunately) seaborn uses the similar pcolormesh() which lacks this extra info)

A possible solution, as AFAIK we can't tell seaborn not to set tick labels, is to override the function(s) used to show this mouseover info. The simplest approach is to override fmt_xdata()/fmt_ydata():

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

data = np.array([[15, 10], [20, 10]])
ax = sns.heatmap(data, linewidth=0.01, square=True)

def fmt_data(v):
    return f"{v:.2f}"

ax.fmt_xdata = fmt_data
ax.fmt_ydata = fmt_data

plt.show()

overriding fmt_xdata/fmt_ydata

This already restores pcolormesh's x=, y= value that was lost when labels were set. But how about improving this and also show the data value just like imshow does?

For full control over the mouseover info, we override format_coord(). Simplifying an example from matplotlib documentation to your code:

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

data = np.array([[15, 10], [20, 10]])
ax = sns.heatmap(data, linewidth=0.01, square=True)

def format_coord(x, y):
    z = data[int(x), int(y)]
    return f"x={x:.2f}, y={y:.2f}\n[{z:.2f}]"

ax.format_coord = format_coord

plt.show()

overriding format_coord

Upvotes: 0

Related Questions