web-dev-qa-db-fra.com

Générer une carte thermique dans MatPlotLib à l'aide d'un ensemble de données scatter

J'ai un ensemble de points de données X, Y (environ 10k) qu'il est facile de tracer sous forme de nuage de points, mais que j'aimerais représenter sous forme de carte thermique.

J'ai parcouru les exemples de MatPlotLib et ils semblent tous déjà commencer par les valeurs de cellules heatmap pour générer l'image.

Existe-t-il une méthode qui convertit un groupe de x, y, tous différents, en cartes thermiques (où les zones avec une fréquence plus élevée de x, y seraient "plus chaudes")?

153
greye

Si vous ne voulez pas d'hexagones, vous pouvez utiliser la fonction histogram2d de numpy:

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

Cela fait un heatmap 50x50. Si vous voulez, disons 512x384, vous pouvez mettre bins=(512, 384) dans l'appel à histogram2d.

Exemple: Matplotlib heat map example

151
ptomato

Dans Matplotlib Lexicon, je pense que vous voulez un tracé hexbin .

Si vous n'êtes pas familiarisé avec ce type de tracé, il ne s'agit que d'un histogramme à deux variables dans lequel le plan xy est quadrillé par une grille régulière d'hexagones.

Ainsi, à partir d'un histogramme, il vous suffit de compter le nombre de points situés dans chaque hexagone, de discrétiser la région de traçage en tant qu'ensemble de fenêtres , d'affecter chaque point à l'une de ces fenêtres. Enfin, mappez les fenêtres sur un tableau de couleurs et vous obtenez un diagramme hexbin.

Bien qu’ils soient moins utilisés que les cercles ou les carrés, par exemple, les hexagones constituent un meilleur choix pour la géométrie du conteneur de classement est intuitif:

  • les hexagones ont la symétrie du plus proche voisin (par exemple, les cases carrées ne le sont pas, par exemple, la distance de un point sur la bordure d'un carré à un point à l'intérieur de ce carré n'est pas égal partout) et

  • hexagone est le n-polygone le plus élevé qui donne une tessellation plane régulière (c.-à-d. que vous pouvez remodeler en toute sécurité le sol de votre cuisine avec des carreaux de forme hexagonale, car vous ne disposerez d'aucun espace vide. entre les tuiles lorsque vous avez terminé - pas vrai pour tous les autres polygones supérieurs-n, n> = 7).

( Matplotlib utilise le terme hexbin plot; il en va de même (autant que je sache) de bibliothèques de traçage pour R; je ne sais toujours pas si c'est le terme généralement accepté pour les tracés de ce type, bien que je suppose que c'est probablement étant donné que hexbin est l'abréviation de binning hexagonal , qui décrit l'étape essentielle de la préparation des données pour l'affichage.)


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

105
doug

Au lieu d’utiliser np.hist2d, qui produit en général des histogrammes assez laids, j’aimerais recycler py-sphviewer , un package python permettant de rendre des simulations de particules à l’aide d’un noyau de lissage adaptatif et pouvant être facilement installé à partir de documentation de la page Web). Considérez le code suivant, basé sur l'exemple:

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

qui produit l'image suivante:

 enter image description here

Comme vous le voyez, les images ont une belle apparence et nous pouvons identifier différentes sous-structures. Ces images sont construites en répartissant un poids donné pour chaque point d’un domaine donné, défini par la longueur de lissage, qui est à son tour donnée par la distance au voisin le plus proche nb (j’ai choisi 16, 32 et 64 pour les exemples). Ainsi, les régions à haute densité sont généralement réparties sur des régions plus petites par rapport aux régions à faible densité. 

La fonction myplot est juste une fonction très simple que j'ai écrite afin de donner les données x, y à py-sphviewer pour faire la magie. 

26
Alejandro

Si vous utilisez 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

22

Edit: Pour une meilleure approximation de la réponse d'Alejandro, voir ci-dessous.

Je sais que c'est une vieille question, mais je voulais ajouter quelque chose à la réponse d'Alejandro: si vous voulez une image lissée de Nice sans utiliser py-sphviewer, vous pouvez utiliser plutôt np.histogram2d et appliquer un filtre gaussien (à partir de scipy.ndimage.filters) à la carte thermique:

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

Produit:

 Output images

Le diagramme de dispersion et s = 16 superposés pour Agape Gal'lo (cliquez pour avoir une meilleure vue):

 On top of eachother


Une différence que j'ai remarquée avec mon approche du filtre gaussien et celle d'Alejandro était que sa méthode montrait des structures locales bien meilleures que les miennes. Par conséquent, j'ai implémenté une méthode simple voisin le plus proche au niveau des pixels. Cette méthode calcule pour chaque pixel la somme inverse des distances des points n les plus proches dans les données. Cette méthode est à haute résolution assez coûteuse en calcul et je pense qu’il ya un moyen plus rapide, alors laissez-moi savoir si vous avez des améliorations. Quoi qu'il en soit, voici le code:

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

Résultat:

 Nearest Neighbour Smoothing

19
Jurgy

Seaborn a maintenant la fonction jointplot qui devrait bien fonctionner ici:

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

14
wordsforthewise

et la question initiale était de savoir comment convertir les valeurs de dispersion en valeurs de grille, non? histogram2d compte la fréquence par cellule. Toutefois, si vous avez d'autres données par cellule que la fréquence, vous aurez besoin de plus de données. Travail à faire.

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

Donc, j'ai un jeu de données avec Z-résultats pour les coordonnées X et Y. Cependant, je calculais peu de points en dehors de la zone d’intérêt (grandes lacunes) et des tas de points dans une petite zone d’intérêt.

Oui ici cela devient plus difficile mais aussi plus amusant. Quelques bibliothèques (désolé):

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

pyplot est mon moteur graphique d’aujourd’hui, cm est une gamme de cartes de couleurs offrant un choix très intéressant.

Le dernier est important, en particulier parce que la fréquence des points xy n'est pas également distribuée dans mes données. Tout d’abord, commençons par quelques limites correspondant à mes données et une taille de grille arbitraire. Les données d'origine ont des points de données également en dehors de ces limites x et y.

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

Nous avons donc défini une grille de 500 pixels entre les valeurs minimale et maximale de x et y.

Dans mes données, il y a beaucoup plus que les 500 valeurs disponibles dans la zone de grand intérêt; alors que dans la zone de faible intérêt, il n'y a même pas 200 valeurs dans le réseau total; entre les limites graphiques de x_min et x_max, il y en a encore moins.

Donc, pour obtenir une belle image, la tâche est d’obtenir une moyenne des valeurs d’intérêt élevé et de combler les lacunes ailleurs.

Je définis ma grille maintenant. Pour chaque paire de xx-yy, je veux avoir une couleur.

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

Pourquoi cette forme étrange? scipy.griddata veut une forme de (n, D).

Griddata calcule une valeur par point dans la grille, selon une méthode prédéfinie ..__ Je choisis "le plus proche" - les points de grille vides seront remplis avec les valeurs du voisin le plus proche. Cela donne à penser que les zones avec moins d’informations ont des cellules plus grandes (même si ce n’est pas le cas). On pourrait choisir d’interpoler "linéaire", les zones contenant moins d’informations semblant moins nettes. Question de goût, vraiment.

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

Et hop, nous passons à matplotlib pour afficher l'intrigue

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

Autour de la partie pointue de la forme en V, vous voyez que j’ai fait beaucoup de calculs lors de ma recherche du point idéal, alors que les parties les moins intéressantes ont presque partout une résolution inférieure.

 Heatmap of a SVC in high resolution

2
Anderas

Créez un tableau à 2 dimensions qui correspond aux cellules de votre image finale, appelé say heatmap_cells et instanciez-le en tant que zéros.

Choisissez deux facteurs de mise à l'échelle qui définissent la différence entre chaque élément du tableau en unités réelles, pour chaque dimension, par exemple, x_scale et y_scale. Choisissez-les de sorte que tous vos points de données tombent dans les limites du tableau heatmap.

Pour chaque point de données brut avec x_value et y_value:

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

2
meepmeep

J'ai bien peur d'être un peu en retard à la fête, mais j'avais une question similaire il y a quelque temps. La réponse acceptée (par @ptomato) m'a aidée, mais je voudrais aussi poster ceci au cas où cela serait utile à quelqu'un.


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


Voici le résultat  enter image description here 

0
Abhishek

Très similaire à @ Piti's answer , mais en utilisant 1 appel au lieu de 2 pour générer les 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()

Sortie:

 2d_gaussian_heatmap

0
Alaa M.

enter image description here

En voici une que j'ai faite sur un ensemble de 1 million de points avec 3 catégories (rouge, vert et bleu). Voici un lien vers le référentiel si vous souhaitez essayer la fonction. 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)
0
Joel Stansbury