从pandas DataFrame生成热力图

185

我有一个由Python的Pandas包生成的数据框。如何使用来自pandas包的DataFrame生成热图。

import numpy as np 
from pandas import *

Index= ['aaa','bbb','ccc','ddd','eee']
Cols = ['A', 'B', 'C','D']
df = DataFrame(abs(np.random.randn(5, 4)), index= Index, columns=Cols)

>>> df
          A         B         C         D
aaa  2.431645  1.248688  0.267648  0.613826
bbb  0.809296  1.671020  1.564420  0.347662
ccc  1.501939  1.126518  0.702019  1.596048
ddd  0.137160  0.147368  1.504663  0.202822
eee  0.134540  3.708104  0.309097  1.641090
>>> 

1
你在创建热力图或者研究方面尝试了什么?如果没有更多信息的话,我建议将你的数据转换并使用这种方法:http://code.activestate.com/recipes/578175-hierarchical-clustering-heatmap-python/ - learner
这不是一个答案,而是一条评论,但问题在于我没有足够的声望来发表评论。我有点困惑,因为矩阵的输出值和原始数组完全不同。我想在热图中打印真实值,而不是一些不同的值。有人能解释一下这是为什么吗?例如:*原始索引数据:aaa/A = 2.431645 *在热图中打印的值:aaa/A = 1.06192 - John Perez
@Monitotier 请提出一个新问题,并包含一个完整的代码示例,说明您已经尝试过什么。这是获得帮助解决问题的最佳方式!如果您认为相关,可以链接到此问题。 - joelostblom
11个回答

269

对于今天查看此内容的人,我建议使用Seaborn heatmap(),如此处所述。

上面的示例可以按以下方式完成:

import numpy as np 
from pandas import DataFrame
import seaborn as sns
%matplotlib inline

Index= ['aaa', 'bbb', 'ccc', 'ddd', 'eee']
Cols = ['A', 'B', 'C', 'D']
df = DataFrame(abs(np.random.randn(5, 4)), index=Index, columns=Cols)

sns.heatmap(df, annot=True)

%matplotlib是IPython的魔法函数,对于那些不熟悉它的人来说。


1
为什么不使用pandas? - tommy.carstensen
14
Seaborn和Pandas很搭配,因此您仍需要使用Pandas将数据整理成正确的格式。Seaborn专门用于静态图表,使从Pandas DataFrame创建热力图变得非常简单。 - Brideau
这个链接好像失效了,你能更新一下吗?另外,我该如何使用 import matplotlib.pyplot as plt 运行上面的代码呢? - Cleb
嘿@Cleb,我不得不更新到存档页面,因为它似乎没有在任何地方上线。请查看他们的文档以了解如何与pyplot一起使用:http://stanford.edu/~mwaskom/software/seaborn-dev/tutorial/aesthetics.html - Brideau
1
使用import matplotlib.pyplot as plt代替%matplotlib inline,并在最后加上plt.show()以便实际查看绘图。 - tsveti_iko
将具有两位以上数字的数字显示为科学计数法:1.4e + 02等。如何显示为140(是否称为整数)?答案:https://dev59.com/p10b5IYBdhLWcg3wENmK: sns.heatmap(table2,annot=True,cmap='Blues', fmt='g') - statHacker

183

如果你不需要一个实际的图表,只是想在表格中添加颜色来表示值,你可以使用pandas数据框的style.background_gradient()方法。该方法会给在JupyterLab笔记本等查看pandas数据框时显示的HTML表格上色,结果类似于在电子表格软件中使用“条件格式”:

import numpy as np 
import pandas as pd


index= ['aaa', 'bbb', 'ccc', 'ddd', 'eee']
cols = ['A', 'B', 'C', 'D']
df = pd.DataFrame(abs(np.random.randn(5, 4)), index=index, columns=cols)
df.style.background_gradient(cmap='Blues')

enter image description here

有关详细用法,请参见我之前在同一主题上提供的更详细的答案以及Pandas文档中的样式部分


9
该答案正是我在寻找的。我认为它应该被评价更高(+1)。 - ponadto
11
这个答案并不是对所发布问题的有效解决方案。Pandas 的背景渐变着色会单独考虑每行或每列,而 matplotlib 的 pcolor 或 pcolormesh 着色则会考虑整个矩阵。以以下代码为例:pd.DataFrame([[1, 1], [0, 3]]).style.background_gradient(cmap='summer')结果是一个表格,其中有两个数字1,它们分别具有不同的颜色。 - Toni Penya-Alba
8
这个问题是关于如何从pandas dataframe生成热力图,而不是复制pcolor或pcolormesh的行为。如果你对后者感兴趣,可以使用 axis=None(自pandas 0.24.0起)。 - joelostblom
4
我并不是打算让你复制某个工具或行为,而是通常希望矩阵中的所有元素都遵循相同的比例尺,而不是每行/列都有不同的比例尺。正如您所指出的,axis=None 可以实现这一点,我认为它应该成为您回答的一部分(特别是因为似乎没有记录在0中)。 - Toni Penya-Alba
3
我已经在上述详细答案中包含了 axis=None,以及其他几个选项,因为我同意您的观点,这些选项可以实现通常所需的行为。我还注意到昨天缺乏文档,并提交了一份PR - joelostblom
显示剩余6条评论

114

您需要使用 matplotlib.pcolor

import numpy as np 
from pandas import DataFrame
import matplotlib.pyplot as plt

index = ['aaa', 'bbb', 'ccc', 'ddd', 'eee']
columns = ['A', 'B', 'C', 'D']
df = DataFrame(abs(np.random.randn(5, 4)), index=index, columns=columns)

plt.pcolor(df)
plt.yticks(np.arange(0.5, len(df.index), 1), df.index)
plt.xticks(np.arange(0.5, len(df.columns), 1), df.columns)
plt.show()

这样做得到的结果是:

输出示例


5
这里有一些关于pcolorimshow的有趣讨论:链接 - LondonRob
2
还有pcolormesh,它针对这种图形进行了优化。 - Eric O. Lebigot

24

有用的sns.heatmapapi在这里。请查看参数,其中有很多。例如:

import seaborn as sns
%matplotlib inline

idx= ['aaa','bbb','ccc','ddd','eee']
cols = list('ABCD')
df = DataFrame(abs(np.random.randn(5,4)), index=idx, columns=cols)

# _r reverses the normal order of the color map 'RdYlGn'
sns.heatmap(df, cmap='RdYlGn_r', linewidths=0.5, annot=True)

输入图像描述


7
如果您想从Pandas DataFrame获得交互式热图,并且正在运行Jupyter笔记本电脑,您可以尝试交互式小部件Clustergrammer-Widget,在NBViewer上查看交互式笔记本here,文档here

enter image description here

对于更大的数据集,您可以尝试正在开发中的Clustergrammer2 WebGL小部件(示例笔记本在这里


2
哇,这非常整洁!很高兴看到一些不错的包来到Python - 厌倦了使用R magics。 - Sos
你知道如何在这个函数中使用Pd.Dataframe吗?当我只是将df传递到net.load时,Python会抛出一个错误。 - Luis
你可以使用 'net.load_df(df); net.widget();'。你可以在这个笔记本中尝试 https://colab.research.google.com/drive/11M6RkGuh-5zR9OVXgeDNKKHm440-DgTA - Nick Fernandez

6
请注意,seaborn 的作者只希望 seaborn.heatmap 与分类数据框架一起使用。它不是通用的。
如果您的索引和列是数字和/或日期时间值,则此代码将为您提供良好的服务。
Matplotlib 热图函数 pcolormesh 需要 bins 而不是 indices,因此有一些花哨的代码从您的数据框架索引中构建 bins(即使您的索引不是均匀分布的!)。
其余部分只是 np.meshgridplt.pcolormesh
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

def conv_index_to_bins(index):
    """Calculate bins to contain the index values.
    The start and end bin boundaries are linearly extrapolated from 
    the two first and last values. The middle bin boundaries are 
    midpoints.

    Example 1: [0, 1] -> [-0.5, 0.5, 1.5]
    Example 2: [0, 1, 4] -> [-0.5, 0.5, 2.5, 5.5]
    Example 3: [4, 1, 0] -> [5.5, 2.5, 0.5, -0.5]"""
    assert index.is_monotonic_increasing or index.is_monotonic_decreasing

    # the beginning and end values are guessed from first and last two
    start = index[0] - (index[1]-index[0])/2
    end = index[-1] + (index[-1]-index[-2])/2

    # the middle values are the midpoints
    middle = pd.DataFrame({'m1': index[:-1], 'p1': index[1:]})
    middle = middle['m1'] + (middle['p1']-middle['m1'])/2

    if isinstance(index, pd.DatetimeIndex):
        idx = pd.DatetimeIndex(middle).union([start,end])
    elif isinstance(index, (pd.Float64Index,pd.RangeIndex,pd.Int64Index)):
        idx = pd.Float64Index(middle).union([start,end])
    else:
        print('Warning: guessing what to do with index type %s' % 
              type(index))
        idx = pd.Float64Index(middle).union([start,end])

    return idx.sort_values(ascending=index.is_monotonic_increasing)

def calc_df_mesh(df):
    """Calculate the two-dimensional bins to hold the index and 
    column values."""
    return np.meshgrid(conv_index_to_bins(df.index),
                       conv_index_to_bins(df.columns))

def heatmap(df):
    """Plot a heatmap of the dataframe values using the index and 
    columns"""
    X,Y = calc_df_mesh(df)
    c = plt.pcolormesh(X, Y, df.values.T)
    plt.colorbar(c)

使用heatmap(df)调用它,使用plt.show()查看它。

enter image description here


你能用虚拟数据展示一下吗?我在索引处遇到了一些断言错误。 - jonboy
1
@jonboy 如果你在我的断言中遇到了一个断言错误,即索引已排序(代码行 assert index.is_monotonic_increasing or ...lexsorted),这意味着你需要在将数据框传递到此函数之前对其进行索引和列的排序。抱歉,我现在非常忙碌,等我有时间了会制作一些虚拟数据。 - JoseOrtiz3

6

惊讶地发现没有人提到更有能力、互动性和易于使用的替代方案。

A)您可以使用Plotly:

  1. 只需两行代码即可获得:

  2. 交互性,

  3. 平稳的比例尺,

  4. 基于整个数据框而不是单独列的颜色,

  5. 轴上的列名和行索引,

  6. 缩放功能,

  7. 平移功能,

  8. 内置一键操作将其保存为PNG格式,

  9. 自动缩放,

  10. 悬停比较,

  11. 气泡显示值,因此热图仍然很好看,您可以在任何地方查看值:

import plotly.express as px
fig = px.imshow(df.corr())
fig.show()

在此输入图片描述

B) 你也可以使用Bokeh:

所有相同的功能,但需要更多的麻烦。但是如果您不想选择plotly,同时又想要所有这些功能,仍然值得一试:

from bokeh.plotting import figure, show, output_notebook
from bokeh.models import ColumnDataSource, LinearColorMapper
from bokeh.transform import transform
output_notebook()
colors = ['#d7191c', '#fdae61', '#ffffbf', '#a6d96a', '#1a9641']
TOOLS = "hover,save,pan,box_zoom,reset,wheel_zoom"
data = df.corr().stack().rename("value").reset_index()
p = figure(x_range=list(df.columns), y_range=list(df.index), tools=TOOLS, toolbar_location='below',
           tooltips=[('Row, Column', '@level_0 x @level_1'), ('value', '@value')], height = 500, width = 500)

p.rect(x="level_1", y="level_0", width=1, height=1,
       source=data,
       fill_color={'field': 'value', 'transform': LinearColorMapper(palette=colors, low=data.value.min(), high=data.value.max())},
       line_color=None)
color_bar = ColorBar(color_mapper=LinearColorMapper(palette=colors, low=data.value.min(), high=data.value.max()), major_label_text_font_size="7px",
                     ticker=BasicTicker(desired_num_ticks=len(colors)),
                     formatter=PrintfTickFormatter(format="%f"),
                     label_standoff=6, border_line_color=None, location=(0, 0))
p.add_layout(color_bar, 'right')

show(p)

enter image description here


3

0
在处理大量特征之间的相关性时,将相关特征聚类在一起是非常有用的。可以使用 seaborn 的 clustermap 绘图来完成。
import seaborn as sns
import matplotlib.pyplot as plt

g = sns.clustermap(df.corr(), 
                   method = 'complete', 
                   cmap   = 'RdBu', 
                   annot  = True, 
                   annot_kws = {'size': 8})
plt.setp(g.ax_heatmap.get_xticklabels(), rotation=60);

enter image description here

clustermap函数使用层次聚类将相关特征排列在一起,并生成树状图。

此图中有两个显著的聚类:

  1. y_desdew.point_des
  2. irradiancey_seasonaldew.point_seasonal

值得一提的是,生成此图表所需的气象数据可以通过这个Jupyter笔记本访问。


0
你可以使用seaborn和DataFrame corr()来查看列之间的相关性。
sns.heatmap(df.corr())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接