9

I am trying to create a single image with heatmaps representing the correlation of features of data points for each label separately. With seaborn I can create a heatmap for a single class like so

grouped = df.groupby('target')
sns.heatmap(grouped.get_group('Class_1').corr())

An I get this which makes sense:

Class_1 correlation heatmap

But then I try to make a list of all the labels like so:

g = sns.FacetGrid(df, col='target')
g.map(lambda grp: sns.heatmap(grp.corr()))

And sadly I get this which makes no sense to me:

Failing attempt to plot all classes

fakedrake
  • 6,528
  • 8
  • 41
  • 64
  • You want nine heatmaps, each showing correlation within a single target? – cphlewis Apr 13 '15 at 06:01
  • Yes, I accepted @cphlewis' answer because it works but what I like about seaborn so far is that you can plot stuff quick and dirty, in the sense that you can pretty much tell it "plot this" and it will come up with something. My usecase is pretty much that: it's not very important to me how the heatmaps will be arranged or what axes will be displayed, just to see that information in pretty much the format I asked for. So getting the lambda thing to work would be very helpful :) – fakedrake Apr 13 '15 at 09:52
  • Yeah i rushed it and didn't tap the button properly thanx :) – fakedrake Apr 13 '15 at 09:57
  • 1
    funny how I noticed! anyway, I futzed around with setting up a lambda for a while. You can see each facet's data but not alter it in place, and the way heatmap expects its arguments isn't enough like how plot, etc., expect theirs to map over easily, and in short roll-your-own looked like the way to go. Maybe if you made the title more specific a seaborn expert would notice -- 'plotting correlation heatmaps into a FacetGrid', maybe? – cphlewis Apr 13 '15 at 10:04
  • 1
    Your `map_dataframe` solution is correct -- you may want to add it below as I did not see it at first and was about to write it up as an answer. But cphlewis' example is good too, as often it's easier just to make a grid of Axes in matplotlib and use a for-loop than to try and wrestle data into the format expected by `FacetGrid`. – mwaskom Apr 15 '15 at 13:58
  • PS it might be a good idea to explicitly set the limits of the colormap so that the different facets can be more directly compared. – mwaskom Apr 15 '15 at 14:00

2 Answers2

12

Turns out you can do it pretty concisely with just seaborn if you use map_dataframe instead of map:

g = sns.FacetGrid(df, col='target')
g.map_dataframe(lambda data, color: sns.heatmap(data.corr(), linewidths=0))

@mwaskom points out in his comment that it might be a good idea to explicitly set the limits of the colormap so that the different facets can be more directly compared. The documentation describes relevant heatmap parameters:

vmin, vmax : floats, optional

Values to anchor the colormap, otherwise they are inferred from the data and other keyword arguments.

tdy
  • 36,675
  • 19
  • 86
  • 83
fakedrake
  • 6,528
  • 8
  • 41
  • 64
4

Without FacetGrid, but making a corr heatmap for each group in a column:

import pandas as pd
import seaborn as sns
from numpy.random import randint
import matplotlib.pyplot as plt


df = pd.DataFrame(randint(0,10,(200,12)),columns=list('abcdefghijkl'))
grouped = df.groupby('a')
rowlength = grouped.ngroups/2 # fix up if odd number of groups
fig, axs = plt.subplots(figsize=(9,4), nrows=2, ncols=rowlength)

targets = zip(grouped.groups.keys(), axs.flatten())
for i, (key, ax) in enumerate(targets):
    sns.heatmap(grouped.get_group(key).corr(), ax=ax,
                xticklabels=(i >= rowlength),
                yticklabels=(i%rowlength==0),
                cbar=False) # Use cbar_ax into single side axis
    ax.set_title('a=%d'%key)
plt.show()

enter image description here Maybe there's a way to set up a lambda to correctly pass the data from the g.facet_data() generator through corr before going to heatmap.

cphlewis
  • 15,759
  • 4
  • 46
  • 55