¿Mapa de calor en matplotlib con pcolor?

100

Me gustaría hacer un mapa de calor como este (que se muestra en FlowingData ): mapa de calor

Los datos de origen están aquí , pero los datos aleatorios y las etiquetas estarían bien para usar, es decir

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

Hacer el mapa de calor es bastante fácil en matplotlib:

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

E incluso encontré argumentos de un mapa de colores que parecen correctos:heatmap = plt.pcolor(data, cmap=matplotlib.cm.Blues)

Pero más allá de eso, no puedo averiguar cómo mostrar etiquetas para las columnas y filas y mostrar los datos en la orientación adecuada (origen en la parte superior izquierda en lugar de en la parte inferior izquierda).

Todos los intentos de manipular heatmap.axes(por ejemplo heatmap.axes.set_xticklabels = column_labels) han fallado. ¿Que me estoy perdiendo aqui?

Jason Sundram
fuente
Hay mucha superposición con esta pregunta sobre el mapa de calor ; podría ser una buena información para ti.
John Lyon
Las técnicas de etiquetado de esta publicación podrían ayudar a stackoverflow.com/questions/6352740/matplotlib-label-each-bin
tacaswell

Respuestas:

123

Esto es tarde, pero aquí está mi implementación en Python del mapa de calor de la NBA de datos fluidos.

actualizado: 1/4/2014 : gracias a todos

# -*- 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 salida se ve así: Mapa de calor de la NBA similar a los datos fluidos

Hay un cuaderno ipython con todo este código aquí . He aprendido mucho de 'overflow, así que espero que alguien lo encuentre útil.

BubbleGuppies
fuente
1
El código anterior no se ejecutó en el portátil iPythnon. Hice una pequeña modificación, cambiando nba_sort = nba_norm.sort ('PTS', ascendente = True, inplace = True) a nba_sort = nba_norm.copy () nba_sort.sort ('PTS', ascendente = True, inplace = True) ya que la ordenación funciona por efecto secundario, no por retorno de función. ¡Gracias por el maravilloso ejemplo de concierto!
Yu Shen
1
Hmmm ... parece que tienes razón. No estoy seguro de qué se trata todo eso. Corregiré el código. ¡Gracias!
BubbleGuppies
¿Cuál sería la forma más sencilla de hacer un gráfico como este, pero mostrar el valor de la estadística en la tabla? Es decir, quiero hacer algo pcolorasí, pero también se muestran valores numéricos. O: Quiero hacer un matplotlib tableque coloree sus celdas. He visto soluciones al otro problema y son estéticamente feas. Esto se ve muy bien, si tan solo supiera cómo superponer los números.
8one6
Si. Tropecé en mi camino a eso al responder la pregunta de otra persona: stackoverflow.com/a/21167108/2501018
8one6
@joelotz ¿Estaría dispuesto a contribuir con una versión (modificada) de esto a los documentos de matplotlib? Si es así, simplemente abra un PR o envíeme un ping por correo electrónico (consulte mi perfil).
Tacaswell
12

El módulo de python seaborn se basa en matplotlib y produce un mapa de calor muy agradable.

A continuación se muestra una implementación con seaborn, diseñada para el portátil 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")

La salida se ve así: mapa de calor de la nba seaborn utilicé el mapa de colores matplotlib Blues, pero personalmente encuentro los colores predeterminados bastante hermosos. Usé matplotlib para rotar las etiquetas del eje x, ya que no pude encontrar la sintaxis de seaborn. Como señaló grexor, fue necesario especificar las dimensiones (fig.set_size_inches) por prueba y error, lo que me pareció un poco frustrante.

Como señaló Paul H, puede agregar fácilmente los valores a los mapas de calor (annot = True), pero en este caso no pensé que mejorara la figura. Se tomaron varios fragmentos de código de la excelente respuesta de joelotz.

Mark Teese
fuente
11

El problema principal es que primero debe establecer la ubicación de sus tics xey. Además, ayuda a utilizar la interfaz más orientada a objetos para matplotlib. Es decir, interactúe con el axesobjeto directamente.

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

Espero que ayude.

Paul H
fuente
Gracias, @Paul H, eso funciona muy bien. Estaba usando la heatmap.axespropiedad, que por alguna razón no hace nada.
Jason Sundram
¿Sabes cómo mover las etiquetas del eje x para que estén en la parte superior? Intenté lo obvio ax.xaxis.set_label_position('top')sin éxito.
Jason Sundram
@JasonSundram Deberías abrir una nueva pregunta para mover la posición de la etiqueta, porque eso debería funcionar y es extraño que no.
tacaswell
1
@tcaswell, buen punto. Nueva pregunta aquí: stackoverflow.com/questions/14406214/…
Jason Sundram
1
@ Tgsmith61591 Usaría la función de mapa de calor de seaborn, configurando annot=Truecuando se llama ( stanford.edu/~mwaskom/software/seaborn/generated/… )
Paul H
3

Alguien editó esta pregunta para eliminar el código que usé, así que me vi obligado a agregarlo como respuesta. ¡Gracias a todos los que participaron en responder esta pregunta! Creo que la mayoría de las otras respuestas son mejores que este código, solo dejo esto aquí para fines de referencia.

Gracias a Paul H y a unutbu (que respondió a esta pregunta ), tengo un resultado bastante atractivo:

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

Y aquí está la salida:

Matplotlib HeatMap

Jason Sundram
fuente