[python] Genere un mapa de calor en MatPlotLib usando un conjunto de datos de dispersión


Answers

En el léxico de Matplotlib , creo que quieres un diagrama de hexbin .

Si no está familiarizado con este tipo de trama, es simplemente un histograma bivariable en el que el plano xy está teselado por una cuadrícula regular de hexágonos.

Entonces, a partir de un histograma, puede contar el número de puntos que caen en cada hexágono, discretizar la región de trazado como un conjunto de ventanas , asignar cada punto a una de estas ventanas; finalmente, mapea las ventanas en una matriz de colores , y tienes un diagrama hexbin.

Aunque es menos utilizado que, por ejemplo, círculos o cuadrados, los hexágonos son una mejor opción para la geometría del contenedor de binning es intuitivo:

  • los hexágonos tienen simetría del vecino más cercano (p. ej., los contenedores cuadrados no, por ejemplo, la distancia desde un punto en el borde de un cuadrado hasta un punto dentro de ese cuadrado no es igual en todas partes) y

  • el hexágono es el n polígono más alto que proporciona teselación plana regular (es decir, puedes remodelar el suelo de tu cocina de forma segura con mosaicos de forma hexagonal porque no tendrás ningún espacio vacío entre los mosaicos cuando hayas terminado; no es cierto para todos los otros polos superiores n, n> = 7).

( Matplotlib usa el término gráfico de hexbin , así que haz (AFAIK) todas las librerías de trazado para R ; aún no sé si este es el término generalmente aceptado para las gráficas de este tipo, aunque sospecho que es probable dado que hexbin es corto para el agrupamiento hexagonal , que describe el paso esencial para preparar los datos para la visualización).

from matplotlib import pyplot as PLT
from matplotlib import cm as CM
from matplotlib import mlab as ML
import numpy as NP

n = 1e5
x = y = NP.linspace(-5, 5, 100)
X, Y = NP.meshgrid(x, y)
Z1 = ML.bivariate_normal(X, Y, 2, 2, 0, 0)
Z2 = ML.bivariate_normal(X, Y, 4, 1, 1, 1)
ZD = Z2 - Z1
x = X.ravel()
y = Y.ravel()
z = ZD.ravel()
gridsize=30
PLT.subplot(111)

# if 'bins=None', then color of each hexagon corresponds directly to its count
# 'C' is optional--it maps values to x-y coordinates; if 'C' is None (default) then 
# the result is a pure 2D histogram 

PLT.hexbin(x, y, C=z, gridsize=gridsize, cmap=CM.jet, bins=None)
PLT.axis([x.min(), x.max(), y.min(), y.max()])

cb = PLT.colorbar()
cb.set_label('mean value')
PLT.show()   

Question

Tengo un conjunto de puntos de datos X, Y (alrededor de 10k) que son fáciles de trazar como un diagrama de dispersión, pero que me gustaría representar como un mapa de calor.

Miré a través de los ejemplos en MatPlotLib y todos parecen comenzar con los valores de las celdas de mapa de calor para generar la imagen.

¿Hay algún método que convierta un grupo de x, y, todos diferentes, en un mapa de calor (donde las zonas con mayor frecuencia de x, y serían "más cálidas")?




Sé que esta es una vieja pregunta, pero quería agregar algo al guion de Alejandro: si quieres una imagen suavizada sin usar py-sphviewer puedes usar np.histogram2d y aplicar un filtro gaussiano (desde scipy.ndimage.filters ) a el mapa de calor

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from scipy.ndimage.filters import gaussian_filter


def myplot(x, y, s, bins=1000):
    heatmap, xedges, yedges = np.histogram2d(x, y, bins=bins)
    heatmap = gaussian_filter(heatmap, sigma=s)

    extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]
    return heatmap.T, extent


fig, axs = plt.subplots(2, 2)

# Generate some test data
x = np.random.randn(1000)
y = np.random.randn(1000)

sigmas = [0, 16, 32, 64]

for ax, s in zip(axs.flatten(), sigmas):
    if s == 0:
        ax.plot(x, y, 'k.', markersize=5)
        ax.set_title("Scatter plot")
    else:
        img, extent = myplot(x, y, s)
        ax.imshow(img, extent=extent, origin='lower', cmap=cm.jet)
        ax.set_title("Smoothing with  $\sigma$ = %d" % s)

plt.show()

Produce:




y la pregunta inicial fue ... cómo convertir los valores de dispersión a los valores de la grilla, ¿verdad? histogram2d sí cuenta la frecuencia por celda, sin embargo, si tiene otros datos por celda que solo la frecuencia, necesitaría un trabajo adicional para hacer.

x = data_x # between -10 and 4, log-gamma of an svc
y = data_y # between -4 and 11, log-C of an svc
z = data_z #between 0 and 0.78, f1-values from a difficult dataset

Sí, aquí se vuelve más difícil pero también más divertido. Algunas bibliotecas (lo siento):

from matplotlib import pyplot as plt
from matplotlib import cm
import numpy as np
from scipy.interpolate import griddata

pyplot es mi motor gráfico hoy en día, cm es una gama de mapas de color con algunas opciones inherentes. numpy para los cálculos, y griddata para unir valores a una grilla fija.

El último es importante especialmente porque la frecuencia de los puntos xy no se distribuye por igual en mis datos. Primero, comencemos con algunos límites que se ajusten a mis datos y a un tamaño de cuadrícula arbitrario.

#determine grid boundaries
gridsize = 500
x_min = -8
x_max = 2.5
y_min = -2
y_max = 7

En mis datos, hay mucho más que los 500 valores para esta grilla en el área de alto interés; mientras que en el área de bajo interés, hay un máximo de 200 valores en la grilla total; entre los límites gráficos de x_min y x_max hay incluso menos.

Yo defino mi grilla ahora Para cada par xx-yy, quiero tener un color.

xx = np.linspace(x_min, x_max, gridsize)
yy = np.linspace(y_min, y_max, gridsize)
grid = np.array(np.meshgrid(xx, yy.T))
grid = grid.reshape(2, grid.shape[1]*grid.shape[2]).T

¿Por qué la forma extraña? scipy.griddata quiere una forma de (n, D).

Griddata calcula un valor por punto en la cuadrícula, por un método predefinido. Elijo "más cercano": los puntos de cuadrícula vacíos se rellenarán con los valores del vecino más cercano. Parece que las áreas con menos información tienen celdas más grandes (incluso si no es el caso). Uno puede elegir interpolar "lineal", luego las áreas con menos información se ven menos nítidas. La materia del gusto, realmente.

points = np.array([x, y]).T # because griddata wants it that way
z_grid2 = griddata(points, z, grid, method='nearest')
# you get a 1D vector as result. Reshape to picture format!
z_grid2 = z_grid2.reshape(xx.shape[0], yy.shape[0])

Y hop, entregamos a matplotlib para mostrar la trama

fig = plt.figure(1, figsize=(10, 10))
ax1 = fig.add_subplot(111)
ax1.imshow(z_grid2, extent=[x_min, x_max,y_min, y_max,  ],
            origin='lower', cmap=cm.magma)
ax1.set_title("SVC: empty spots filled by nearest neighbours")
ax1.set_xlabel('log gamma')
ax1.set_ylabel('log C')
plt.show()

Alrededor de la parte puntiaguda de la forma de V, verá que tuve muchos cálculos durante mi búsqueda del punto óptimo, mientras que las partes menos interesantes en casi todos los demás tienen una resolución más baja.




Si está utilizando 1.2.x

x = randn(100000)
y = randn(100000)
hist2d(x,y,bins=100);




Related