使用 Pandas 数据框中的值注释热图

Posted

技术标签:

【中文标题】使用 Pandas 数据框中的值注释热图【英文标题】:Annotate heatmap with value from Pandas dataframe 【发布时间】:2014-01-28 05:32:24 【问题描述】:

我想用我从数据框传递到下面函数的值来注释热图。我查看了 matplotlib.text 但无法在我的热图中以所需的方式从我的数据框中获取值。我在下面粘贴了用于生成热图的函数,之后是我的数据框和热图调用的输出。我想在热图中每个单元格的中心绘制我的数据框中的每个值。

生成热图的函数:

import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors

def heatmap_binary(df,
            edgecolors='w',
            #cmap=mpl.cm.RdYlGn,
            log=False):    
    width = len(df.columns)/7*10
    height = len(df.index)/7*10

    fig, ax = plt.subplots(figsize=(20,10))#(figsize=(width,height))

    cmap, norm = mcolors.from_levels_and_colors([0, 0.05, 1],['Teal', 'MidnightBlue'] ) # ['MidnightBlue', Teal]['Darkgreen', 'Darkred']

    heatmap = ax.pcolor(df ,
                        edgecolors=edgecolors,  # put white lines between squares in heatmap
                        cmap=cmap,
                        norm=norm)


    ax.autoscale(tight=True)  # get rid of whitespace in margins of heatmap
    ax.set_aspect('equal')  # ensure heatmap cells are square
    ax.xaxis.set_ticks_position('top')  # put column labels at the top
    ax.tick_params(bottom='off', top='off', left='off', right='off')  # turn off ticks

    plt.yticks(np.arange(len(df.index)) + 0.5, df.index, size=20)
    plt.xticks(np.arange(len(df.columns)) + 0.5, df.columns, rotation=90, size= 15)

    # ugliness from http://matplotlib.org/users/tight_layout_guide.html
    from mpl_toolkits.axes_grid1 import make_axes_locatable
    divider = make_axes_locatable(ax)
    cax = divider.append_axes("right", "3%", pad="1%")
    plt.colorbar(heatmap, cax=cax)


plt.show()

这是我的数据框的一个例子:

dataframe :

             0-5 km / h  5-40 km / h  40-80 km / h  80-120 km / h  \
NORDIC         0.113955     0.191888      0.017485      -0.277528   
MIDDLE  EU     0.117903     0.197084     -0.001447      -0.332677   
KOREA          0.314008     0.236503     -0.067174      -0.396518   
CHINA          0.314008     0.236503     -0.067174      -0.396518   

             120-160 km / h  160-190 km / h  190 km / h  
NORDIC            -0.054365        0.006107    0.002458  
MIDDLE  EU         0.002441        0.012097    0.004599  
KOREA             -0.087191        0.000331    0.000040  
CHINA             -0.087191        0.000331    0.000040  

生成热图:

heatmap_binary(dataframe)

有什么想法吗?


更新以澄清我的问题

我尝试了问题中提出的解决方案,该解决方案具有我正在寻找的结果: how to annotate heatmap with text in matplotlib? 但是,使用 matplotlib.text 函数在热图中定位值仍然存在问题: 这是我尝试此解决方案的代码:

import matplotlib.pyplot as plt
import numpy as np


data = dataframe.values
heatmap_binary(dataframe)

for y in range(data.shape[0]):
    for x in range(data.shape[1]):
        plt.text(data[y,x] +0.05 , data[y,x] + 0.05, '%.4f' % data[y, x], #data[y,x] +0.05 , data[y,x] + 0.05
                 horizontalalignment='center',
                 verticalalignment='center',
                 color='w')

#plt.colorbar(heatmap)

plt.show()

添加情节:(不同的颜色,但同样的问题)

【问题讨论】:

尝试这个时出了什么问题?可以发张图片吗? 添加了一张图片,不同的颜色但同样的问题。文本调用将值放在右上角.... 你能一次性发布你的代码副本,而不是分成两个不同的块吗?我认为问题在于您在添加colorbar 之后 运行for 循环。尝试在调用pcolor之后立即运行textfor循环。 我试图在 pcolor 之后将 for 循环放在 heatmap_binary 函数旁边...但没有工作...我得到了相同的结果 【参考方案1】:

此功能由seaborn 包提供。它可以生成类似的地图

seaborn 的一个示例用法是

import seaborn as sns
sns.set()

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

# Draw a heatmap with the numeric values in each cell
sns.heatmap(flights, annot=True, fmt="d", linewidths=.5)

【讨论】:

【参考方案2】:

您在for 循环中用于坐标的值被搞砸了。此外,您使用的是plt.colorbar,而不是像fig.colorbar 这样更干净的东西。试试这个(它可以完成工作,无需努力清理代码):

def heatmap_binary(df,
            edgecolors='w',
            #cmap=mpl.cm.RdYlGn,
            log=False):    
    width = len(df.columns)/7*10
    height = len(df.index)/7*10

    fig, ax = plt.subplots(figsize=(20,10))#(figsize=(width,height))

    cmap, norm = mcolors.from_levels_and_colors([0, 0.05, 1],['Teal', 'MidnightBlue'] ) # ['MidnightBlue', Teal]['Darkgreen', 'Darkred']

    heatmap = ax.pcolor(df ,
                        edgecolors=edgecolors,  # put white lines between squares in heatmap
                        cmap=cmap,
                        norm=norm)
    data = df.values
    for y in range(data.shape[0]):
        for x in range(data.shape[1]):
            plt.text(x + 0.5 , y + 0.5, '%.4f' % data[y, x], #data[y,x] +0.05 , data[y,x] + 0.05
                 horizontalalignment='center',
                 verticalalignment='center',
                 color='w')


    ax.autoscale(tight=True)  # get rid of whitespace in margins of heatmap
    ax.set_aspect('equal')  # ensure heatmap cells are square
    ax.xaxis.set_ticks_position('top')  # put column labels at the top
    ax.tick_params(bottom='off', top='off', left='off', right='off')  # turn off ticks

    ax.set_yticks(np.arange(len(df.index)) + 0.5)
    ax.set_yticklabels(df.index, size=20)
    ax.set_xticks(np.arange(len(df.columns)) + 0.5)
    ax.set_xticklabels(df.columns, rotation=90, size= 15)

    # ugliness from http://matplotlib.org/users/tight_layout_guide.html
    from mpl_toolkits.axes_grid1 import make_axes_locatable
    divider = make_axes_locatable(ax)
    cax = divider.append_axes("right", "3%", pad="1%")
    fig.colorbar(heatmap, cax=cax)

然后

df1 = pd.DataFrame(np.random.choice([0, 0.75], size=(4,5)), columns=list('ABCDE'), index=list('WXYZ'))
heatmap_binary(df1)

给予:

【讨论】:

谢谢!这正是我所希望的。 乐于助人。我想知道这是否值得建议作为对 Pandas 的增强。我希望本着这种精神,我可以用 Pandas DataFrames 做各种各样的事情。仅对某些列着色。着色前标准化。等等。对我来说,悬而未决的问题是“什么是正确的一般方法”? IE。它是基于pcolor 的东西还是应该涉及 DataFrame HTML 表示的东西?大家觉得呢? 是的,这样的图表增强会很有帮助。本着简化数据处理的精神,他们非常受欢迎。特别是,非常需要在热图上标记值。如此常见,使用一些简单的 API 会节省一些精力。【参考方案3】:

这是因为您在添加其他坐标区后使用了plt.text

状态机将在 当前 轴上绘图,在您使用 divider.append_axes 添加新轴后,颜色条的轴就是当前轴。 (仅调用 plt.colorbar 不会导致这种情况,因为如果它自己创建轴,它会将当前轴设置回原来的轴。如果使用 cax kwarg 传入特定的轴对象,它不会重置“当前”轴,因为这不是您通常想要的。)

这样的事情是你会看到这么多人建议你使用 OO 接口而不是状态机接口来 matplotlib 的主要原因。这样您就知道要在哪个坐标区对象上绘图。

例如,在您的情况下,您可以让 heatmap_binary 返回它创建的 ax 对象,并使用 ax.text 而不是 plt.text 进行绘图(其他绘图方法类似)。

【讨论】:

谢谢。那么,您对如何从我的函数返回轴对象有任何建议吗?恐怕我对python语言不太熟练..【参考方案4】:

您也可以使用 plotly.figure_factory 从 DataFrame 创建热图,但您已将其转换 到列表中.

    import plotly.figure_factory as ff

    z = [your_dataframe].values.tolist()
    x = [your_dataframe].columns.tolist()
    y = [your_dataframe].index.tolist()

    fig = ff.create_annotated_heatmap(z, x=x, y=y, annotation_text=z, colorscale='viridis')

    # for add annotation into Heatmap
    for i in range(len(fig.layout.annotations)):
        fig.layout.annotations[i].font.size = 12

    # show your Heatmap
    fig.show()

【讨论】:

以上是关于使用 Pandas 数据框中的值注释热图的主要内容,如果未能解决你的问题,请参考以下文章

seaborn 热图的人工刻度标签

具有二进制颜色编码和原始输入注释的 seaborn 热图

从 pandas DataFrame 制作热图

从订单簿绘制热图(熊猫数据框)

自定义注释 Seaborn 热图

使用 pandas TimeSeries 创建热图