【问题标题】:Discrete legend in seaborn heatmap plotseaborn 热图图中的离散图例
【发布时间】:2016-12-14 15:19:53
【问题描述】:

我正在使用此处提供的数据使用 seaborn 和 pandas 构建此热图。

代码:

    import pandas
    import seaborn.apionly as sns

    # Read in csv file
    df_trans = pandas.read_csv('LUH2_trans_matrix.csv')

    sns.set(font_scale=0.8)
    cmap = sns.cubehelix_palette(start=2.8, rot=.1, light=0.9, as_cmap=True)
    cmap.set_under('gray')  # 0 values in activity matrix are shown in gray (inactive transitions)
    df_trans = df_trans.set_index(['Unnamed: 0'])
    ax = sns.heatmap(df_trans, cmap=cmap, linewidths=.5, linecolor='lightgray')

    # X - Y axis labels
    ax.set_ylabel('FROM')
    ax.set_xlabel('TO')

    # Rotate tick labels
    locs, labels = plt.xticks()
    plt.setp(labels, rotation=0)
    locs, labels = plt.yticks()
    plt.setp(labels, rotation=0)

    # revert matplotlib params
    sns.reset_orig()

从 csv 文件中可以看出,它包含 3 个离散值:0、-1 和 1。我想要一个离散图例而不是颜色条。将 0 标记为 A,将 -1 标记为 B,将 1 标记为 C。我该怎么做?

【问题讨论】:

标签: python pandas matplotlib seaborn


【解决方案1】:

@Fabio Lamanna 提供的链接是一个很好的开始。

从那里,您仍然希望在正确的位置设置颜色条标签并使用与您的数据相对应的刻度标签。

假设您的数据中具有等间距的级别,这会产生一个很好的离散颜色条:

基本上,这归结为关闭 seaborn colorbar 并自己用离散化的 colorbar 替换它。

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

def cmap_discretize(cmap, N):
    """Return a discrete colormap from the continuous colormap cmap.

        cmap: colormap instance, eg. cm.jet. 
        N: number of colors.

    Example
        x = resize(arange(100), (5,100))
        djet = cmap_discretize(cm.jet, 5)
        imshow(x, cmap=djet)
    """

    if type(cmap) == str:
        cmap = plt.get_cmap(cmap)
    colors_i = np.concatenate((np.linspace(0, 1., N), (0.,0.,0.,0.)))
    colors_rgba = cmap(colors_i)
    indices = np.linspace(0, 1., N+1)
    cdict = {}
    for ki,key in enumerate(('red','green','blue')):
        cdict[key] = [ (indices[i], colors_rgba[i-1,ki], colors_rgba[i,ki]) for i in xrange(N+1) ]
    # Return colormap object.
    return matplotlib.colors.LinearSegmentedColormap(cmap.name + "_%d"%N, cdict, 1024)

def colorbar_index(ncolors, cmap, data):

    """Put the colorbar labels in the correct positions
        using uique levels of data as tickLabels
    """

    cmap = cmap_discretize(cmap, ncolors)
    mappable = matplotlib.cm.ScalarMappable(cmap=cmap)
    mappable.set_array([])
    mappable.set_clim(-0.5, ncolors+0.5)
    colorbar = plt.colorbar(mappable)
    colorbar.set_ticks(np.linspace(0, ncolors, ncolors))
    colorbar.set_ticklabels(np.unique(data))


# Read in csv file
df_trans = pandas.read_csv('d:/LUH2_trans_matrix.csv')

sns.set(font_scale=0.8)
cmap = sns.cubehelix_palette(n_colors=3,start=2.8, rot=.1, light=0.9, as_cmap=True)
cmap.set_under('gray')  # 0 values in activity matrix are shown in gray (inactive transitions)
df_trans = df_trans.set_index(['Unnamed: 0'])

N = df_trans.max().max() - df_trans.min().min() + 1

f, ax = plt.subplots()
ax = sns.heatmap(df_trans, cmap=cmap, linewidths=.5, linecolor='lightgray',cbar=False)
colorbar_index(ncolors=N, cmap=cmap,data=df_trans)    

# X - Y axis labels
ax.set_ylabel('FROM')
ax.set_xlabel('TO')

# Rotate tick labels
locs, labels = plt.xticks()
plt.setp(labels, rotation=0)
locs, labels = plt.yticks()
plt.setp(labels, rotation=0)

# revert matplotlib params
sns.reset_orig()

herehere回收和改编的点点滴滴

【讨论】:

    【解决方案2】:

    我发现如果您使用 ListedColormap,在 seaborn 中创建离散化颜色条会更容易。无需定义自己的函数,只需添加几行即可基本自定义轴。

    import pandas
    import matplotlib.pyplot as plt
    import seaborn as sns
    from matplotlib.colors import ListedColormap
    
    
    # Read in csv file
    df_trans = pandas.read_csv('LUH2_trans_matrix.csv')
    
    sns.set(font_scale=0.8)
    # cmap is now a list of colors
    cmap = sns.cubehelix_palette(start=2.8, rot=.1, light=0.9, n_colors=3)
    df_trans = df_trans.set_index(['Unnamed: 0'])
    
    # Create two appropriately sized subplots
    grid_kws = {'width_ratios': (0.9, 0.03), 'wspace': 0.18}
    fig, (ax, cbar_ax) = plt.subplots(1, 2, gridspec_kw=grid_kws)
    
    ax = sns.heatmap(df_trans, ax=ax, cbar_ax=cbar_ax, cmap=ListedColormap(cmap),
                     linewidths=.5, linecolor='lightgray',
                     cbar_kws={'orientation': 'vertical'})
    
    # Customize tick marks and positions
    cbar_ax.set_yticklabels(['B', 'A', 'C'])
    cbar_ax.yaxis.set_ticks([ 0.16666667, 0.5, 0.83333333])
    
    
    # X - Y axis labels
    ax.set_ylabel('FROM')
    ax.set_xlabel('TO')
    
    # Rotate tick labels
    locs, labels = plt.xticks()
    plt.setp(labels, rotation=0)
    locs, labels = plt.yticks()
    plt.setp(labels, rotation=0)
    

    【讨论】:

      【解决方案3】:

      嗯,肯定有不止一种方法可以做到这一点。在这种情况下,只需要三种颜色,我将通过创建LinearSegmentedColormap 自己选择颜色,而不是使用cubehelix_palette 生成它们。如果有足够的颜色可以保证使用cubehelix_palette,我将使用cbar_kws 参数的boundaries 选项定义颜色图上的段。无论哪种方式,都可以使用set_ticksset_ticklabels 手动指定刻度。

      以下代码示例演示了 LinearSegmentedColormap 的手动创建,并包含有关在改用 cubehelix_palette 时如何指定边界的 cmets。

      import matplotlib.pyplot as plt
      import pandas
      import seaborn.apionly as sns
      from matplotlib.colors import LinearSegmentedColormap
      
      sns.set(font_scale=0.8)
      dataFrame = pandas.read_csv('LUH2_trans_matrix.csv').set_index(['Unnamed: 0'])
      
      # For only three colors, it's easier to choose them yourself.
      # If you still really want to generate a colormap with cubehelix_palette instead,
      # add a cbar_kws={"boundaries": linspace(-1, 1, 4)} to the heatmap invocation
      # to have it generate a discrete colorbar instead of a continous one.
      myColors = ((0.8, 0.0, 0.0, 1.0), (0.0, 0.8, 0.0, 1.0), (0.0, 0.0, 0.8, 1.0))
      cmap = LinearSegmentedColormap.from_list('Custom', myColors, len(myColors))
      
      ax = sns.heatmap(dataFrame, cmap=cmap, linewidths=.5, linecolor='lightgray')
      
      # Manually specify colorbar labelling after it's been generated
      colorbar = ax.collections[0].colorbar
      colorbar.set_ticks([-0.667, 0, 0.667])
      colorbar.set_ticklabels(['B', 'A', 'C'])
      
      # X - Y axis labels
      ax.set_ylabel('FROM')
      ax.set_xlabel('TO')
      
      # Only y-axis labels need their rotation set, x-axis labels already have a rotation of 0
      _, labels = plt.yticks()
      plt.setp(labels, rotation=0)
      
      plt.show()
      

      【讨论】:

        【解决方案4】:

        这是一个基于其他答案的简单解决方案,它概括超过 3 个类别并使用 dict (vmap) 来定义标签。

        import seaborn as sns
        import numpy as np
        
        # This just makes some sample 2D data and a corresponding vmap dict with labels for the values in the data
        data = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
        vmap = {i: chr(65 + i) for i in range(len(np.ravel(data)))}
        n = len(vmap)
        
        print(vmap)
        
        cmap = sns.color_palette("deep", n)
        ax = sns.heatmap(data, cmap=cmap)
        
        # Get the colorbar object from the Seaborn heatmap
        colorbar = ax.collections[0].colorbar
        # The list comprehension calculates the positions to place the labels to be evenly distributed across the colorbar
        r = colorbar.vmax - colorbar.vmin
        colorbar.set_ticks([colorbar.vmin + 0.5 * r / (n) + r * i / (n) for i in range(n)])
        colorbar.set_ticklabels(list(vmap.values()))
        

        【讨论】:

          猜你喜欢
          • 2017-05-09
          • 2018-10-31
          • 2020-01-13
          • 1970-01-01
          • 2022-01-21
          • 2017-04-12
          • 1970-01-01
          • 2019-04-15
          • 1970-01-01
          相关资源
          最近更新 更多