Heatmap dans matplotlib avec pcolor?

100

J'aimerais créer une carte thermique comme celle-ci (affichée sur FlowingData ): carte de chaleur

Les données sources sont ici , mais des données aléatoires et des étiquettes seraient bien à utiliser, c'est-à-dire

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

Faire la carte thermique est assez simple dans matplotlib:

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

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

Mais au-delà de cela, je ne peux pas comprendre 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 en bas à gauche).

Les tentatives de manipulation heatmap.axes(par exemple heatmap.axes.set_xticklabels = column_labels) ont toutes échoué. Qu'est-ce que j'oublie ici?

Jason Sundram
la source
Il y a beaucoup de chevauchement avec cette question de carte de chaleur - pourrait être une bonne information pour vous là-bas.
John Lyon
Les techniques d'étiquetage de cet article pourraient aider stackoverflow.com/questions/6352740/matplotlib-label-each-bin
tacaswell

Réponses:

123

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

mise à 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

La sortie ressemble à ceci: carte thermique nba de type flowingdata

Il y a un notebook ipython avec tout ce code ici . J'ai beaucoup appris du débordement alors j'espère que quelqu'un trouvera cela utile.

Guppys à bulles
la source
1
Le code ci-dessus ne fonctionnait pas dans le notebook iPythnon. J'ai fait quelques légères modifications, en changeant nba_sort = nba_norm.sort ('PTS', ascending = True, inplace = True) en nba_sort = nba_norm.copy () nba_sort.sort ('PTS', ascending = True, inplace = True) car le tri fonctionne par effet secondaire et non par retour de fonction! Merci pour le merveilleux exemple conceret!
Yu Shen
1
Hmmm ... vous semblez avoir raison. Je ne sais pas de quoi il s'agit. Je vais corriger le code. Merci!
BubbleGuppies
Quelle serait la manière la plus simple de créer un graphique comme celui-ci mais d'afficher la valeur de la statistique dans le tableau. Ie je veux faire un pcolorcomme celui-ci mais qui a des valeurs numériques montrées aussi. OU: Je veux créer un matplotlib tablequi colore ses cellules. J'ai vu des solutions à l'autre problème, et elles sont esthétiquement laides. Cela a l'air génial, si seulement je savais comment superposer les chiffres.
8one6
Ouais. J'ai trébuché là
dessus
@joelotz Seriez-vous prêt à contribuer une version (modifiée) de ceci à la documentation matplotlib? Si oui, ouvrez simplement un PR ou envoyez-moi un ping par e-mail (voir mon profil).
tacaswell
12

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

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: carte thermique seaborn nba j'ai utilisé la carte de couleurs matplotlib Blues, mais je trouve personnellement les couleurs par défaut assez belles. J'ai utilisé matplotlib pour faire pivoter les étiquettes de l'axe x, car je ne trouvais pas la syntaxe seaborn. Comme noté par grexor, il était nécessaire de spécifier les dimensions (fig.set_size_inches) par essais et erreurs, 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 ne pense pas que cela ait amélioré la figure. Plusieurs extraits de code ont été extraits de l'excellente réponse de joelotz.

Mark Teese
la source
11

Le problème principal est que vous devez d'abord définir l'emplacement de vos graduations x et y. En outre, cela aide à utiliser l'interface plus orientée objet vers matplotlib. À savoir, interagissez axesdirectement avec l' objet.

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 pourra aider.

Paul H
la source
Merci, @Paul H, cela fonctionne à merveille. J'utilisais la heatmap.axespropriété, qui pour une raison quelconque ne fait rien.
Jason Sundram
Savez-vous comment déplacer les étiquettes de l'axe X pour qu'elles soient en haut? J'ai essayé l'évidence ax.xaxis.set_label_position('top')en vain.
Jason Sundram
@JasonSundram Vous devriez ouvrir une nouvelle question pour déplacer le positionnement de l'étiquette, car cela devrait fonctionner et c'est étrange que ce ne soit pas le cas.
tacaswell
1
@tcaswell, bon point. Nouvelle question ici: stackoverflow.com/questions/14406214/…
Jason Sundram
1
@ Tgsmith61591 J'utiliserais la fonction de carte de chaleur de seaborn, en définissant une annot=Truefois appelé ( stanford.edu/~mwaskom/software/seaborn/generated/… )
Paul H
3

Quelqu'un a édité 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 répondu à cette question! Je pense que la plupart des autres réponses sont meilleures que ce code, je laisse cela ici à des fins de référence.

Avec mes remerciements à Paul H et à unutbu (qui a répondu à cette question ), j'ai une sortie assez jolie:

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 la sortie:

Matplotlib HeatMap

Jason Sundram
la source