Domanda

Ho un set di punti dati X, Y (circa 10k) che sono facili da tracciare come diagramma a dispersione ma che vorrei rappresentare come mappa di calore.

Ho guardato attraverso gli esempi di Matplotlib e sembrano già iniziare con i valori della cella di calore per generare l'immagine.

Esiste un metodo che converte un mucchio di x, y, tutti diversi, in una mappa di calore (dove le zone con frequenza più elevata di x, y sarebbero "più calde")?

È stato utile?

Soluzione

Se non vuoi esagoni, puoi usare Numpy's histogram2d funzione:

import numpy as np
import numpy.random
import matplotlib.pyplot as plt

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

heatmap, xedges, yedges = np.histogram2d(x, y, bins=50)
extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]

plt.clf()
plt.imshow(heatmap.T, extent=extent, origin='lower')
plt.show()

Questo crea una mappa di calore 50x50. Se vuoi, diciamo, 512x384, puoi mettere bins=(512, 384) Nella chiamata a histogram2d.

Esempio: Matplotlib heat map example

Altri suggerimenti

In Matplotlib lessico, penso che tu voglia un hexbin complotto.

Se non hai familiarità con questo tipo di trama, è solo un istogramma bivariato in cui il piano XY è toccato da una griglia regolare di esagoni.

Quindi da un istogramma, puoi solo contare il numero di punti che cadono in ciascun esagono, discretizza la regione di trama come un insieme di finestre, assegnare ogni punto a una di queste finestre; Infine, mappare le finestre su un file Array a colori, e hai un diagramma esabico.

Sebbene meno comunemente usati di EG, cerchi o quadrati, che gli esagoni sono una scelta migliore per la geometria del contenitore binning è intuitiva:

  • Gli esagoni hanno Simmetria più vicina (Ad esempio, i bidoni quadrati non lo fanno, ad es. La distanza da Un punto sul bordo di una piazza a un punto all'interno di quel quadrato non è ovunque uguale) e

  • L'esagono è il più alto n-poligone che dà Tessellazione dell'aereo regolare (vale a dire, puoi modellare in modo sicuro il pavimento della tua cucina con piastrelle a forma esagonale perché non avrai alcun spazio vuoto tra le piastrelle quando hai finito-non vero per tutti gli altri poligoni più alti-N, n> = 7, ).

(Matplotlib usa il termine hexbin complotto; così fai (afaik) tutti i Proggere di biblioteche per R; Ancora non so se questo è il termine generalmente accettato per i grafici di questo tipo, anche se sospetto che sia probabilmente dato hexbin è breve per binning esagonale, che descrive il passaggio essenziale per preparare i dati per la visualizzazione.)


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

enter image description here

Invece di usare np.hist2d, che in generale produce istogrammi piuttosto brutti, vorrei riciclare Py-Sphviewer, un pacchetto Python per il rendering di simulazioni di particelle utilizzando un kernel di livellamento adattivo e che può essere facilmente installato da PIP (vedere la documentazione della pagina web). Considera il seguente codice, che si basa sull'esempio:

import numpy as np
import numpy.random
import matplotlib.pyplot as plt
import sphviewer as sph

def myplot(x, y, nb=32, xsize=500, ysize=500):   
    xmin = np.min(x)
    xmax = np.max(x)
    ymin = np.min(y)
    ymax = np.max(y)

    x0 = (xmin+xmax)/2.
    y0 = (ymin+ymax)/2.

    pos = np.zeros([3, len(x)])
    pos[0,:] = x
    pos[1,:] = y
    w = np.ones(len(x))

    P = sph.Particles(pos, w, nb=nb)
    S = sph.Scene(P)
    S.update_camera(r='infinity', x=x0, y=y0, z=0, 
                    xsize=xsize, ysize=ysize)
    R = sph.Render(S)
    R.set_logscale()
    img = R.get_image()
    extent = R.get_extent()
    for i, j in zip(xrange(4), [x0,x0,y0,y0]):
        extent[i] += j
    print extent
    return img, extent

fig = plt.figure(1, figsize=(10,10))
ax1 = fig.add_subplot(221)
ax2 = fig.add_subplot(222)
ax3 = fig.add_subplot(223)
ax4 = fig.add_subplot(224)


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

#Plotting a regular scatter plot
ax1.plot(x,y,'k.', markersize=5)
ax1.set_xlim(-3,3)
ax1.set_ylim(-3,3)

heatmap_16, extent_16 = myplot(x,y, nb=16)
heatmap_32, extent_32 = myplot(x,y, nb=32)
heatmap_64, extent_64 = myplot(x,y, nb=64)

ax2.imshow(heatmap_16, extent=extent_16, origin='lower', aspect='auto')
ax2.set_title("Smoothing over 16 neighbors")

ax3.imshow(heatmap_32, extent=extent_32, origin='lower', aspect='auto')
ax3.set_title("Smoothing over 32 neighbors")

#Make the heatmap using a smoothing over 64 neighbors
ax4.imshow(heatmap_64, extent=extent_64, origin='lower', aspect='auto')
ax4.set_title("Smoothing over 64 neighbors")

plt.show()

che produce l'immagine seguente:

enter image description here

Come vedi, le immagini sembrano piuttosto belle e siamo in grado di identificare diverse sottostrutture. Queste immagini sono costruite diffondendo un determinato peso per ogni punto all'interno di un determinato dominio, definito dalla lunghezza di levigatura, che a sua volta è data dalla distanza da più vicino nb vicino (ho scelto 16, 32 e 64 per gli esempi). Pertanto, le regioni di densità più elevata sono in genere distribuite su regioni più piccole rispetto alle regioni a bassa densità.

La funzione Myplot è solo una funzione molto semplice che ho scritto per dare i dati X, Y a Py-Sphviewer per fare la magia.

Se stai usando 1.2.x

import numpy as np
import matplotlib.pyplot as plt

x = np.random.randn(100000)
y = np.random.randn(100000)
plt.hist2d(x,y,bins=100)
plt.show()

gaussian_2d_heat_map

EDIT: Per una migliore approssimazione della risposta di Alejandro, vedi sotto.

So che questa è una vecchia domanda, ma volevo aggiungere qualcosa ad Anwser di Alejandro: se vuoi una bella immagine levigata senza usare Py-Sphviewer puoi invece usare np.histogram2d e applicare un filtro gaussiano (da scipy.ndimage.filters) alla mappa di calore:

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:

Output images

Il diagramma a dispersione e S = 16 tracciati l'uno sull'altro per Agape Gal'lo (fai clic per una vista migliore):

On top of eachother


Una differenza che ho notato con il mio approccio al filtro gaussiano e l'approccio di Alejandro era che il suo metodo mostra strutture locali molto meglio delle mie. Pertanto ho implementato un semplice metodo vicino più vicino a livello di pixel. Questo metodo calcola per ciascun pixel la somma inversa delle distanze del n Punti più vicini nei dati. Questo metodo è ad alta risoluzione piuttosto costoso computazionale e penso che ci sia un modo più rapido, quindi fammi sapere se hai dei miglioramenti. Comunque, ecco il codice:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm


def data_coord2view_coord(p, vlen, pmin, pmax):
    dp = pmax - pmin
    dv = (p - pmin) / dp * vlen
    return dv


def nearest_neighbours(xs, ys, reso, n_neighbours):
    im = np.zeros([reso, reso])
    extent = [np.min(xs), np.max(xs), np.min(ys), np.max(ys)]

    xv = data_coord2view_coord(xs, reso, extent[0], extent[1])
    yv = data_coord2view_coord(ys, reso, extent[2], extent[3])
    for x in range(reso):
        for y in range(reso):
            xp = (xv - x)
            yp = (yv - y)

            d = np.sqrt(xp**2 + yp**2)

            im[y][x] = 1 / np.sum(d[np.argpartition(d.ravel(), n_neighbours)[:n_neighbours]])

    return im, extent


n = 1000
xs = np.random.randn(n)
ys = np.random.randn(n)
resolution = 250

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

for ax, neighbours in zip(axes.flatten(), [0, 16, 32, 64]):
    if neighbours == 0:
        ax.plot(xs, ys, 'k.', markersize=2)
        ax.set_aspect('equal')
        ax.set_title("Scatter Plot")
    else:
        im, extent = nearest_neighbours(xs, ys, resolution, neighbours)
        ax.imshow(im, origin='lower', extent=extent, cmap=cm.jet)
        ax.set_title("Smoothing over %d neighbours" % neighbours)
        ax.set_xlim(extent[0], extent[1])
        ax.set_ylim(extent[2], extent[3])
plt.show()

Risultato:

Nearest Neighbour Smoothing

Seaborn ora ha il Funzione di pilota che dovrebbe funzionare bene qui:

import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt

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

sns.jointplot(x=x, y=y, kind='hex')
plt.show()

demo image

E la domanda iniziale era ... come convertire i valori di dispersione in valori della griglia, giusto?histogram2d Conta la frequenza per cella, tuttavia, se si dispone di altri dati per cella rispetto alla semplice frequenza, avresti bisogno di un lavoro aggiuntivo da svolgere.

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

Quindi, ho un set di dati con z-results per coordinate X e Y. Tuttavia, stavo calcolando pochi punti al di fuori dell'area di interesse (grandi lacune) e cumuli di punti in una piccola area di interesse.

Sì, qui diventa più difficile ma anche più divertente. Alcune biblioteche (scusate):

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

Pyplot è il mio motore grafico oggi, CM è una gamma di mappe dei colori con una scelta di più. Numpy per i calcoli e griddata per attaccare i valori a una griglia fissa.

L'ultimo è importante soprattutto perché la frequenza dei punti XY non è ugualmente distribuita nei miei dati. Innanzitutto, iniziamo con alcuni confini che si adattano ai miei dati e alla dimensione della griglia arbitraria. I dati originali hanno punti dati anche al di fuori di quei confini X e Y.

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

Quindi abbiamo definito una griglia con 500 pixel tra i valori Min e Max di X e Y.

Nei miei dati, ci sono molti più dei 500 valori disponibili nell'area di alto interesse; Considerando che nell'area a basso interesse, non ci sono nemmeno 200 valori nella griglia totale; tra i confini grafici di x_min e x_max Ce ne sono ancora meno.

Quindi, per ottenere una bella immagine, l'attività è ottenere una media per i valori elevati di interesse e colmare le lacune altrove.

Definisco la mia griglia adesso. Per ogni coppia xx-yy, voglio avere un colore.

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

Perché la strana forma? Scipy.Griddata vuole una forma di (n, d).

Griddata calcola un valore per punto nella griglia, con un metodo predefinito. Scelgo "più vicino" - I punti della griglia vuoti saranno riempiti di valori dal vicino più vicino. Sembra che le aree con meno informazioni abbiano celle più grandi (anche se non è così). Si potrebbe scegliere di interpolare "lineare", quindi le aree con meno informazioni sembrano meno nitide. Materia di gusto, davvero.

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

E saltiamo, consegniamo a Matplotlib per visualizzare 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()

Intorno alla parte appuntita della forma a V, vedi che ho fatto molti calcoli durante la mia ricerca per il punto debole, mentre le parti meno interessanti quasi ovunque hanno una risoluzione inferiore.

Heatmap of a SVC in high resolution

Make a 2-dimensional array that corresponds to the cells in your final image, called say heatmap_cells and instantiate it as all zeroes.

Choose two scaling factors that define the difference between each array element in real units, for each dimension, say x_scale and y_scale. Choose these such that all your datapoints will fall within the bounds of the heatmap array.

For each raw datapoint with x_value and y_value:

heatmap_cells[floor(x_value/x_scale),floor(y_value/y_scale)]+=1

Very similar to @Piti's answer, but using 1 call instead of 2 to generate the points:

import numpy as np
import matplotlib.pyplot as plt

pts = 1000000
mean = [0.0, 0.0]
cov = [[1.0,0.0],[0.0,1.0]]

x,y = np.random.multivariate_normal(mean, cov, pts).T
plt.hist2d(x, y, bins=50, cmap=plt.cm.jet)
plt.show()

Output:

2d_gaussian_heatmap

I'm afraid I'm a little late to the party but I had a similar question a while ago. The accepted answer (by @ptomato) helped me out but I'd also want to post this in case it's of use to someone.


''' I wanted to create a heatmap resembling a football pitch which would show the different actions performed '''

import numpy as np
import matplotlib.pyplot as plt
import random

#fixing random state for reproducibility
np.random.seed(1234324)

fig = plt.figure(12)
ax1 = fig.add_subplot(121)
ax2 = fig.add_subplot(122)

#Ratio of the pitch with respect to UEFA standards 
hmap= np.full((6, 10), 0)
#print(hmap)

xlist = np.random.uniform(low=0.0, high=100.0, size=(20))
ylist = np.random.uniform(low=0.0, high =100.0, size =(20))

#UEFA Pitch Standards are 105m x 68m
xlist = (xlist/100)*10.5
ylist = (ylist/100)*6.5

ax1.scatter(xlist,ylist)

#int of the co-ordinates to populate the array
xlist_int = xlist.astype (int)
ylist_int = ylist.astype (int)

#print(xlist_int, ylist_int)

for i, j in zip(xlist_int, ylist_int):
    #this populates the array according to the x,y co-ordinate values it encounters 
    hmap[j][i]= hmap[j][i] + 1   

#Reversing the rows is necessary 
hmap = hmap[::-1]

#print(hmap)
im = ax2.imshow(hmap)


Here's the result enter image description here

enter image description here

Here's one I made on a 1 Million point set with 3 categories (colored Red, Green, and Blue). Here's a link to the repository if you'd like to try the function. Github Repo

histplot(
    X,
    Y,
    labels,
    bins=2000,
    range=((-3,3),(-3,3)),
    normalize_each_label=True,
    colors = [
        [1,0,0],
        [0,1,0],
        [0,0,1]],
    gain=50)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top