7

I want seaborn heatmap to display multiple values in each cell of the heatmap. Here is a manual example of what I want to see, just to be clear:

data = np.array([[0.000000,0.000000],[-0.231049,0.000000],[-0.231049,0.000000]])
labels =  np.array([['A\nExtra Stuff','B'],['C','D'],['E','F']])
fig, ax = plt.subplots()
ax = sns.heatmap(data, annot = labels, fmt = '')

enter image description here

Here as an example to get seaborn.heat to display flightsRoundUp values in the cells.

import matplotlib.pyplot as plt
import seaborn as sns
sns.set()

def RoundUp(x):
    return int(np.ceil(x/10)*10)

# Load the example flights dataset and conver to long-form
flights_long = sns.load_dataset("flights")
flights = flights_long.pivot("month", "year", "passengers")
flightsRoundUp =  flights.applymap(RoundUp)

# Draw a heatmap with the numeric values in each cell
f, ax = plt.subplots(figsize=(9, 6))
sns.heatmap(flights, annot=flightsRoundUp, fmt="", linewidths=.5, ax=ax)

What is the best way to display both flightsRoundUp and flights in all cells? Something like the first manual example above, but for all the cells in a vectorized-like way...

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Rotail
  • 1,025
  • 4
  • 19
  • 40
  • Do you mean [this](https://stackoverflow.com/questions/9958506/element-wise-string-concatenation-in-numpy)? – ImportanceOfBeingErnest Mar 11 '19 at 18:28
  • Not exactly. But that helped me to reduce the issue to being able to combine X and Y via `np.core.defchararray.add(X, Y)` which I have not quite figure out yet. Here are example X and Y: ```X = pd.DataFrame({'a':[1, 2, np.nan], 'b':[10, np.nan, 30]}) Y = pd.DataFrame({'A':[11, 222, np.nan], 'B':[110, np.nan, 330]})``` – Rotail Mar 11 '19 at 20:03
  • If the elements are all strings without any nan. The link above you shared would be the exact solution to my question. – Rotail Mar 11 '19 at 20:04
  • 1
    Did you try `...add(X.values.astype(str), Y.values.astype(str))`? – ImportanceOfBeingErnest Mar 11 '19 at 21:40
  • @ImportanceOfBeingErnest That works. Thanks! – Rotail Mar 12 '19 at 18:39

3 Answers3

8

Rotail's answer didn't work for me, I got an error when applying that lambda function.

However, I found a solution that exploits the fact that seaborn plots sequential figures on top of each other. All you have to do is use one call to heatmap to establish the figure, and then a subsequent call for each of the annotations. Use the annot_kws arg to make sure the text aren't written over eachother.

X = pd.DataFrame({'a':[1, 2, 3], 'b':[4, 5, 6]})
Y = pd.DataFrame({'A':['A', 'B', 'C'], 'B':['E', 'F', 'G']})
Z = pd.DataFrame({'A':['(Extra Stuff)', '(Extra Stuff)', '(Extra Stuff)'], 'B':['(Extra Stuff)', '(Extra Stuff)', '(Extra Stuff)']})

sns.heatmap(X, annot=False)
sns.heatmap(X, annot=Y, annot_kws={'va':'bottom'}, fmt="", cbar=False)
sns.heatmap(X, annot=Z, annot_kws={'va':'top'}, fmt="", cbar=False)

Code above produces the following figure

HaplessEcologist
  • 375
  • 3
  • 11
0

Following works for me too:

X = pd.DataFrame({'a':[1, 2, np.nan], 'b':[10, 20, 30]})
Y = pd.DataFrame({'A':[11, 222, np.nan], 'B':[110, np.nan, 330]})

# convert to string
X_value_ann = (X).astype('|S5').reset_index()
Y_value_ann = (Y).astype('|S5').reset_index()

# define () and new line to glue on later
br = np.char.array(pd.DataFrame('\n(', index=X_value_ann.index, columns=X_value_ann.columns))
cl = np.char.array(pd.DataFrame(')', index=X_value_ann.index, columns=X_value_ann.columns))

# convert to chararray
X_value_ann = np.char.array(X_value_ann)
Y_value_ann = np.char.array(Y_value_ann)

# glue and reshape
my_annotation = pd.DataFrame(X_value_ann+br+Y_value_ann+cl)
my_annotation = my_annotation.applymap(lambda x: x.decode('utf-8')) 
my_annotation = my_annotation.drop(columns=[0])
my_annotation
Rotail
  • 1,025
  • 4
  • 19
  • 40
0

you should be able to set fmt="" and format you labels with appropriate "\n" to have multiple lines of annotations.

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

np.random.seed(0)
sns.set_theme()
uniform_data = np.random.rand(4, 4)
fig,ax = plt.subplots(figsize=(50,20))

uniform_data_labels = \[\]
for i in uniform_data:
    tmp_arr=\[\]
    for j in i:
        tmp_arr.append('Example\nExample')
    uniform_data_labels.append(tmp_arr)
    
sns.heatmap(uniform_data, vmin=0, vmax=1, annot=uniform_data_labels ,ax=ax,fmt="",annot_kws={"fontsize":30})
plt.show()

1

Flair
  • 2,609
  • 1
  • 29
  • 41