web-dev-qa-db-fra.com

Légende discrète dans le tracé de la carte thermique de Seaborn

J'utilise les données présentes ici pour construire cette carte de chaleur à l'aide de seaborn et de pandas.

Code:

    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()

Comme vous pouvez le voir dans le fichier csv, il contient 3 valeurs discrètes: 0, -1 et 1. Je veux une légende discrète au lieu de la barre de couleur. Étiqueter 0 comme A, -1 comme B et 1 comme C. Comment faire?

16
user308827

Eh bien, il y a certainement plus d'une façon d'y parvenir. Dans ce cas, avec seulement trois couleurs nécessaires, je choisirais les couleurs moi-même en créant un LinearSegmentedColormap au lieu de les générer avec cubehelix_palette. S'il y avait suffisamment de couleurs pour justifier l'utilisation de cubehelix_palette, Je définirais les segments sur la palette de couleurs en utilisant l'option boundaries de cbar_kws paramètre. Dans les deux cas, les graduations peuvent être spécifiées manuellement à l'aide de set_ticks et set_ticklabels.

L'exemple de code suivant illustre la création manuelle de LinearSegmentedColormap et inclut des commentaires sur la façon de spécifier les limites si vous utilisez un cubehelix_palette au lieu.

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()

Heatmap using red, green, and blue as colors with a discrete colorbar

14
heenenee

Je trouve qu'une barre de couleurs discrétisée dans seaborn est beaucoup plus facile à créer si vous utilisez un ListedColormap. Il n'est pas nécessaire de définir vos propres fonctions, ajoutez simplement quelques lignes pour personnaliser vos axes.

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)

enter image description here

6
lanery

Voici une solution simple basée sur les autres réponses qui généralise au-delà de 3 catégories et utilise un dict (vmap) pour définir les étiquettes.

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()))

enter image description here

5
Esostack

Le lien fourni par @Fabio Lamanna est un bon début.

À partir de là, vous souhaitez toujours définir des étiquettes de barre de couleur au bon emplacement et utiliser des étiquettes de coche correspondant à vos données.

en supposant que vous avez des niveaux également espacés dans vos données, cela produit une belle barre de couleurs discrète:

Fondamentalement, cela revient à désactiver la barre de couleurs marine et à la remplacer par une barre de couleurs discrétisée vous-même.

enter image description here

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()

morceaux et morceaux recyclés et adaptés de ici et ici

2
jlarsch