100 votes

Heatmap dans matplotlib avec pcolor ?

Je voudrais faire une carte thermique comme celle-ci (montrée sur FlowingData ) : heatmap

Les données sources sont ici mais des données et des étiquettes aléatoires peuvent être utilisées.

import numpy
column_labels = list('ABCD')
row_labels = list('WXYZ')
data = numpy.random.rand(4,4)

La création de la carte thermique est assez facile avec matplotlib :

from matplotlib import pyplot as plt
heatmap = plt.pcolor(data)

Et j'ai même trouvé un carte des couleurs des arguments qui semblent corrects : heatmap = plt.pcolor(data, cmap=matplotlib.cm.Blues)

Mais au-delà de ça, je n'arrive pas à trouver comment afficher les étiquettes pour les colonnes et les lignes et afficher les données dans la bonne orientation (origine en haut à gauche au lieu de bas à gauche).

Tentatives de manipulation heatmap.axes (par exemple heatmap.axes.set_xticklabels = column_labels ) ont tous échoué. Qu'est-ce que je rate ici ?

UPDATE :

Merci à Paul H et unutbu (qui a répondu cette question ), j'ai des résultats plutôt sympathiques :

import matplotlib.pyplot as plt
import numpy as np
column_labels = list('ABCD')
row_labels = list('WXYZ')
data = np.random.rand(4,4)
fig, ax = plt.subplots()
heatmap = ax.pcolor(data, cmap=plt.cm.Blues)

# put the major ticks at the middle of each cell
ax.set_xticks(np.arange(data.shape[0])+0.5, minor=False)
ax.set_yticks(np.arange(data.shape[1])+0.5, minor=False)

# want a more natural, table-like display
ax.invert_yaxis()
ax.xaxis.tick_top()

ax.set_xticklabels(row_labels, minor=False)
ax.set_yticklabels(column_labels, minor=False)
plt.show()

Et voici le résultat :

Matplotlib HeatMap

122voto

joelotz Points 657

C'est un peu tard, mais voici mon implémentation python de la carte thermique de la NBA de Flowingdata.

mis à jour:1/4/2014 : merci à tous

# -*- coding: utf-8 -*-
# <nbformat>3.0</nbformat>

# ------------------------------------------------------------------------
# Filename   : heatmap.py
# Date       : 2013-04-19
# Updated    : 2014-01-04
# Author     : @LotzJoe >> Joe Lotz
# Description: My attempt at reproducing the FlowingData graphic in Python
# Source     : http://flowingdata.com/2010/01/21/how-to-make-a-heatmap-a-quick-and-easy-solution/
#
# Other Links:
#     http://stackoverflow.com/questions/14391959/heatmap-in-matplotlib-with-pcolor
#
# ------------------------------------------------------------------------

import matplotlib.pyplot as plt
import pandas as pd
from urllib2 import urlopen
import numpy as np
%pylab inline

page = urlopen("http://datasets.flowingdata.com/ppg2008.csv")
nba = pd.read_csv(page, index_col=0)

# Normalize data columns
nba_norm = (nba - nba.mean()) / (nba.max() - nba.min())

# Sort data according to Points, lowest to highest
# This was just a design choice made by Yau
# inplace=False (default) ->thanks SO user d1337
nba_sort = nba_norm.sort('PTS', ascending=True)

nba_sort['PTS'].head(10)

# Plot it out
fig, ax = plt.subplots()
heatmap = ax.pcolor(nba_sort, cmap=plt.cm.Blues, alpha=0.8)

# Format
fig = plt.gcf()
fig.set_size_inches(8, 11)

# turn off the frame
ax.set_frame_on(False)

# put the major ticks at the middle of each cell
ax.set_yticks(np.arange(nba_sort.shape[0]) + 0.5, minor=False)
ax.set_xticks(np.arange(nba_sort.shape[1]) + 0.5, minor=False)

# want a more natural, table-like display
ax.invert_yaxis()
ax.xaxis.tick_top()

# Set the labels

# label source:https://en.wikipedia.org/wiki/Basketball_statistics
labels = [
    'Games', 'Minutes', 'Points', 'Field goals made', 'Field goal attempts', 'Field goal percentage', 'Free throws made', 'Free throws attempts', 'Free throws percentage',
    'Three-pointers made', 'Three-point attempt', 'Three-point percentage', 'Offensive rebounds', 'Defensive rebounds', 'Total rebounds', 'Assists', 'Steals', 'Blocks', 'Turnover', 'Personal foul']

# note I could have used nba_sort.columns but made "labels" instead
ax.set_xticklabels(labels, minor=False)
ax.set_yticklabels(nba_sort.index, minor=False)

# rotate the
plt.xticks(rotation=90)

ax.grid(False)

# Turn off all the ticks
ax = plt.gca()

for t in ax.xaxis.get_major_ticks():
    t.tick1On = False
    t.tick2On = False
for t in ax.yaxis.get_major_ticks():
    t.tick1On = False
    t.tick2On = False

Le résultat ressemble à ceci : flowingdata-like nba heatmap

Il y a un notebook ipython avec tout ce code ici . J'ai beaucoup appris de l'overflow et j'espère que quelqu'un trouvera cela utile.

12voto

Mark Teese Points 456

Le module python seaborn est basé sur matplotlib, et produit une très belle carte de chaleur.

Vous trouverez ci-dessous une implémentation avec seaborn, conçue pour le notebook ipython/jupyter.

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
# import the data directly into a pandas dataframe
nba = pd.read_csv("http://datasets.flowingdata.com/ppg2008.csv", index_col='Name  ')
# remove index title
nba.index.name = ""
# normalize data columns
nba_norm = (nba - nba.mean()) / (nba.max() - nba.min())
# relabel columns
labels = ['Games', 'Minutes', 'Points', 'Field goals made', 'Field goal attempts', 'Field goal percentage', 'Free throws made', 
          'Free throws attempts', 'Free throws percentage','Three-pointers made', 'Three-point attempt', 'Three-point percentage', 
          'Offensive rebounds', 'Defensive rebounds', 'Total rebounds', 'Assists', 'Steals', 'Blocks', 'Turnover', 'Personal foul']
nba_norm.columns = labels
# set appropriate font and dpi
sns.set(font_scale=1.2)
sns.set_style({"savefig.dpi": 100})
# plot it out
ax = sns.heatmap(nba_norm, cmap=plt.cm.Blues, linewidths=.1)
# set the x-axis labels on the top
ax.xaxis.tick_top()
# rotate the x-axis labels
plt.xticks(rotation=90)
# get figure (usually obtained via "fig,ax=plt.subplots()" with matplotlib)
fig = ax.get_figure()
# specify dimensions and save
fig.set_size_inches(15, 20)
fig.savefig("nba.png")

Le résultat ressemble à ceci : seaborn nba heatmap J'ai utilisé la carte de couleurs de matplotlib Blues, mais personnellement je trouve les couleurs par défaut assez belles. J'ai utilisé matplotlib pour faire pivoter les étiquettes de l'axe des x, car je ne trouvais pas la syntaxe seaborn. Comme l'a noté grexor, il a fallu spécifier les dimensions (fig.set_size_inches) par essai et erreur, ce que j'ai trouvé un peu frustrant.

Comme l'a noté Paul H, vous pouvez facilement ajouter les valeurs aux cartes thermiques (annot=True), mais dans ce cas, je n'ai pas pensé que cela améliorait la figure. Plusieurs extraits de code ont été tirés de l'excellente réponse de joelotz.

11voto

Paul H Points 5612

Le problème principal est que vous devez d'abord définir l'emplacement de vos ticks x et y. De plus, il est utile d'utiliser l'interface plus orientée objet de matplotlib. A savoir, interagir avec la fonction axes directement.

import matplotlib.pyplot as plt
import numpy as np
column_labels = list('ABCD')
row_labels = list('WXYZ')
data = np.random.rand(4,4)
fig, ax = plt.subplots()
heatmap = ax.pcolor(data)

# put the major ticks at the middle of each cell, notice "reverse" use of dimension
ax.set_yticks(np.arange(data.shape[0])+0.5, minor=False)
ax.set_xticks(np.arange(data.shape[1])+0.5, minor=False)

ax.set_xticklabels(row_labels, minor=False)
ax.set_yticklabels(column_labels, minor=False)
plt.show()

J'espère que cela vous aidera.

4voto

Jason Sundram Points 3237

Quelqu'un a modifié cette question pour supprimer le code que j'ai utilisé, j'ai donc été obligé de l'ajouter comme réponse. Merci à tous ceux qui ont participé à la réponse à cette question ! Je pense que la plupart des autres réponses sont meilleures que ce code, je le laisse ici à titre de référence.

Avec nos remerciements à Paul H et unutbu (qui a répondu cette question ), j'ai des résultats plutôt sympathiques :

import matplotlib.pyplot as plt
import numpy as np
column_labels = list('ABCD')
row_labels = list('WXYZ')
data = np.random.rand(4,4)
fig, ax = plt.subplots()
heatmap = ax.pcolor(data, cmap=plt.cm.Blues)

# put the major ticks at the middle of each cell
ax.set_xticks(np.arange(data.shape[0])+0.5, minor=False)
ax.set_yticks(np.arange(data.shape[1])+0.5, minor=False)

# want a more natural, table-like display
ax.invert_yaxis()
ax.xaxis.tick_top()

ax.set_xticklabels(column_labels, minor=False)
ax.set_yticklabels(row_labels, minor=False)
plt.show()

Et voici le résultat :

Matplotlib HeatMap

0voto

d1337 Points 160

Il semble y avoir une erreur dans la réponse acceptée mais je ne peux pas placer de commentaire ( ?!) donc je l'écris ici : inplace=True est utilisé mais le résultat est également censé être placé dans une nouvelle variable qui est ensuite utilisée dans tout le code. Cela ne fonctionnera pas. Donc, soit vous modifiez en inplace=False, soit vous changez tous les nba_sort en nba.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X