Hacer un mapa de calor a partir de pandas DataFrame

112

Tengo un marco de datos generado a partir del paquete Pandas de Python. ¿Cómo puedo generar un mapa de calor usando DataFrame del paquete pandas?

import numpy as np 
from pandas import *

Index= ['aaa','bbb','ccc','ddd','eee']
Cols = ['A', 'B', 'C','D']
df = DataFrame(abs(np.random.randn(5, 4)), index= Index, columns=Cols)

>>> df
          A         B         C         D
aaa  2.431645  1.248688  0.267648  0.613826
bbb  0.809296  1.671020  1.564420  0.347662
ccc  1.501939  1.126518  0.702019  1.596048
ddd  0.137160  0.147368  1.504663  0.202822
eee  0.134540  3.708104  0.309097  1.641090
>>> 
Curioso
fuente
¿Qué ha intentado en términos de creación de un mapa de calor o investigación? Sin saber más, recomendaría convertir sus datos y usar este método
alumno
@joelostblom Esto no es una respuesta, es un comentario, pero el problema es que no tengo suficiente reputación para poder hacer un comentario. Estoy un poco desconcertado porque el valor de salida de la matriz y la matriz original son totalmente diferentes. Me gustaría imprimir en el mapa de calor los valores reales, no algunos diferentes. ¿Alguien puede explicarme por qué está pasando esto? Por ejemplo: * datos indexados originales: aaa / A = 2.431645 * valores impresos en el mapa de calor: aaa / A = 1.06192
Monitotier
@Monitotier Haga una nueva pregunta e incluya un ejemplo de código completo de lo que ha probado. ¡Esta es la mejor manera de conseguir que alguien le ayude a descubrir qué está mal! Puede vincular a esta pregunta si cree que es relevante.
joelostblom

Respuestas:

82

Quieres matplotlib.pcolor:

import numpy as np 
from pandas import DataFrame
import matplotlib.pyplot as plt

index = ['aaa', 'bbb', 'ccc', 'ddd', 'eee']
columns = ['A', 'B', 'C', 'D']
df = DataFrame(abs(np.random.randn(5, 4)), index=index, columns=columns)

plt.pcolor(df)
plt.yticks(np.arange(0.5, len(df.index), 1), df.index)
plt.xticks(np.arange(0.5, len(df.columns), 1), df.columns)
plt.show()

Esto da:

Muestra de salida

chthonicdaemon
fuente
5
Hay alguna discusión interesante aquí sobre el pcolorfrente imshow.
LondonRob
1
… Y además pcolormesh, que está optimizado para este tipo de gráficos.
Eric O Lebigot
180

Para las personas que miran esto hoy, recomendaría el Seaborn heatmap()como se documenta aquí .

El ejemplo anterior se haría de la siguiente manera:

import numpy as np 
from pandas import DataFrame
import seaborn as sns
%matplotlib inline

Index= ['aaa', 'bbb', 'ccc', 'ddd', 'eee']
Cols = ['A', 'B', 'C', 'D']
df = DataFrame(abs(np.random.randn(5, 4)), index=Index, columns=Cols)

sns.heatmap(df, annot=True)

¿Dónde %matplotlibhay una función mágica de IPython para aquellos que no están familiarizados?

Brideau
fuente
¿Por qué no usarías pandas?
tommy.carstensen
9
Seaborn y Pandas funcionan muy bien juntos, por lo que aún usaría Pandas para obtener sus datos en la forma correcta. Sin embargo, Seaborn se especializa en gráficos estáticos y hace que hacer un mapa de calor a partir de un Pandas DataFrame sea muy simple.
Brideau
Parece que este enlace está muerto; ¿podrías actualizarlo? Además, ¿cómo ejecutaría el código anterior import matplotlib.pyplot as plt?
Cleb
Hola @Cleb, tuve que actualizarlo a la página archivada porque no parece que esté en ninguna parte. Eche un vistazo a sus documentos para usarlo con pyplot: stanford.edu/~mwaskom/software/seaborn-dev/tutorial/…
Brideau
Use en import matplotlib.pyplot as pltlugar de %matplotlib inliney termine con plt.show()para ver realmente la trama.
tsveti_iko
83

Si no necesita un gráfico por decir, y simplemente está interesado en agregar color para representar los valores en un formato de tabla, puede usar el style.background_gradient()método del marco de datos de pandas. Este método da color a la tabla HTML que se muestra al visualizar los marcos de datos de pandas en, por ejemplo, el JupyterLab Notebook y el resultado es similar al uso de "formato condicional" en el software de hoja de cálculo:

import numpy as np 
import pandas as pd


index= ['aaa', 'bbb', 'ccc', 'ddd', 'eee']
cols = ['A', 'B', 'C', 'D']
df = pd.DataFrame(abs(np.random.randn(5, 4)), index=index, columns=cols)
df.style.background_gradient(cmap='Blues')

ingrese la descripción de la imagen aquí

Para un uso detallado, consulte la respuesta más elaborada que proporcioné sobre el mismo tema anteriormente y la sección de estilo de la documentación de pandas .

joelostblom
fuente
4
Maldita sea, esta respuesta es en realidad la que estaba buscando. OMI, debería ser mayor (+1).
ponadto
7
Esta respuesta no es una solución válida a la pregunta publicada. La coloración del gradiente de fondo de Pandas tiene en cuenta cada fila o cada columna por separado, mientras que la coloración pcolor o pcolormesh de matplotlib tiene en cuenta toda la matriz. Tomemos, por ejemplo, el siguiente código que pd.DataFrame([[1, 1], [0, 3]]).style.background_gradient(cmap='summer') da como resultado una tabla con dos, cada uno de ellos con un color diferente.
Toni Penya-Alba
4
@ ToniPenya-Alba La pregunta es cómo generar un mapa de calor a partir de un marco de datos de pandas, no cómo replicar el comportamiento de pcolor o pcolormesh. Si está interesado en este último para sus propios fines, puede usar axis=None(desde pandas 0.24.0).
joelostblom
2
@joelostblom No quise decir mi comentario como "reproducir una herramienta u otro comportamiento", sino como "normalmente uno quiere que todos los elementos de la matriz sigan la misma escala en lugar de tener diferentes escalas para cada fila / columna". Como señala, axis=Nonelogra eso y, en mi opinión, debería ser parte de su respuesta (especialmente porque no parece estar documentado 0 )
Toni Penya-Alba
2
@ ToniPenya-Alba Ya hice axis=Noneparte de la respuesta detallada que enlazo arriba, junto con algunas otras opciones porque estoy de acuerdo con usted en que algunas de estas opciones permiten el comportamiento comúnmente deseado. También me di cuenta de la falta de documentación ayer y abrí un PR .
joelostblom
17

sns.heatmapApi útil está aquí . Mira los parámetros, hay un buen número de ellos. Ejemplo:

import seaborn as sns
%matplotlib inline

idx= ['aaa','bbb','ccc','ddd','eee']
cols = list('ABCD')
df = DataFrame(abs(np.random.randn(5,4)), index=idx, columns=cols)

# _r reverses the normal order of the color map 'RdYlGn'
sns.heatmap(df, cmap='RdYlGn_r', linewidths=0.5, annot=True)

ingrese la descripción de la imagen aquí

Brad Solomon
fuente
4

Si desea un mapa de calor interactivo de un Pandas DataFrame y está ejecutando un cuaderno Jupyter, puede probar el widget interactivo Clustergrammer-Widget , vea el cuaderno interactivo en NBViewer aquí , la documentación aquí

ingrese la descripción de la imagen aquí

Y para conjuntos de datos más grandes, puede probar el widget WebGL de Clustergrammer2 en desarrollo (cuaderno de ejemplo aquí )

Nick Fernandez
fuente
1
¡Guau, esto es muy bueno! Es bueno ver algunos paquetes agradables que vienen a Python - cansado de tener que usar magia R
Sos
2

Tenga en cuenta que los autores de seabornsolo quieren seaborn.heatmap trabajar con marcos de datos categóricos. No es general.

Si su índice y columnas son valores numéricos y / o de fecha y hora, este código le servirá bien.

La función de mapeo de calor de Matplotlib pcolormeshrequiere bins en lugar de índices , por lo que hay un código elegante para construir bins a partir de los índices de su marco de datos (¡incluso si su índice no está espaciado uniformemente!).

El resto es simple np.meshgridy plt.pcolormesh.

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

def conv_index_to_bins(index):
    """Calculate bins to contain the index values.
    The start and end bin boundaries are linearly extrapolated from 
    the two first and last values. The middle bin boundaries are 
    midpoints.

    Example 1: [0, 1] -> [-0.5, 0.5, 1.5]
    Example 2: [0, 1, 4] -> [-0.5, 0.5, 2.5, 5.5]
    Example 3: [4, 1, 0] -> [5.5, 2.5, 0.5, -0.5]"""
    assert index.is_monotonic_increasing or index.is_monotonic_decreasing

    # the beginning and end values are guessed from first and last two
    start = index[0] - (index[1]-index[0])/2
    end = index[-1] + (index[-1]-index[-2])/2

    # the middle values are the midpoints
    middle = pd.DataFrame({'m1': index[:-1], 'p1': index[1:]})
    middle = middle['m1'] + (middle['p1']-middle['m1'])/2

    if isinstance(index, pd.DatetimeIndex):
        idx = pd.DatetimeIndex(middle).union([start,end])
    elif isinstance(index, (pd.Float64Index,pd.RangeIndex,pd.Int64Index)):
        idx = pd.Float64Index(middle).union([start,end])
    else:
        print('Warning: guessing what to do with index type %s' % 
              type(index))
        idx = pd.Float64Index(middle).union([start,end])

    return idx.sort_values(ascending=index.is_monotonic_increasing)

def calc_df_mesh(df):
    """Calculate the two-dimensional bins to hold the index and 
    column values."""
    return np.meshgrid(conv_index_to_bins(df.index),
                       conv_index_to_bins(df.columns))

def heatmap(df):
    """Plot a heatmap of the dataframe values using the index and 
    columns"""
    X,Y = calc_df_mesh(df)
    c = plt.pcolormesh(X, Y, df.values.T)
    plt.colorbar(c)

Llámelo usando heatmap(df)y véalo usando plt.show().

ingrese la descripción de la imagen aquí

NaranjaSherbet
fuente