it-swarm.com.de

Generieren Sie eine Heatmap in MatPlotLib mit einem Streudatensatz

Ich habe einen Satz von X, Y-Datenpunkten (ca. 10k), die leicht als Streudiagramm geplottet werden können, die ich jedoch als Heatmap darstellen möchte.

Ich habe die Beispiele in MatPlotLib durchgesehen und sie scheinen alle bereits mit Heatmap-Zellenwerten zu beginnen, um das Bild zu erzeugen.

Gibt es eine Methode, die einen Bündel von x, y, alle unterschiedlich, in eine Heatmap umwandelt (wobei Zonen mit höherer Frequenz von x, y "wärmer" wären)?

153
greye

Wenn Sie keine Sechsecke wollen, können Sie die histogram2d-Funktion von numpy verwenden:

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

Dies ergibt eine 50x50 Heatmap. Wenn Sie beispielsweise 512x384 wünschen, können Sie bins=(512, 384) in den Aufruf von histogram2d eingeben.

Beispiel: Matplotlib heat map example

151
ptomato

In Matplotlib Lexicon denke ich, dass Sie eine hexbin - Darstellung wollen. 

Wenn Sie mit dieser Art von Diagramm nicht vertraut sind, handelt es sich lediglich um ein bivariates Histogramm, in dem die xy-Ebene von einem regelmäßigen Raster aus Sechsecken dargestellt wird. 

Aus einem Histogramm können Sie also einfach die Anzahl der Punkte zählen, die in jedes Sechseck fallen, den Zeichnungsbereich als eine Menge von Fenstern diskretisieren, jeden Punkt einem dieser Fenster zuweisen; Schließlich ordnen Sie die Fenster einem Farbfeld zu, und Sie haben ein Hexbin-Diagramm. 

Obwohl weniger häufig als Kreise oder Quadrate verwendet, sind Sechsecke eine bessere Wahl für die Geometrie des Binning-Containers. Dies ist intuitiv:

  • sechsecke haben Nächste-Nachbar-Symmetrie (z. B. Quadratkreuze nicht........... zB Abstand von ein Punkt am Rand eines Quadrats nach Punkt innerhalb dieses Quadrats ist nicht überall gleich) und

  • sechseck ist das höchste n-Polygon, das reguläre Ebene Tessellation ergibt (dh Sie können Ihren Küchenboden sicher mit sechseckigen Fliesen umgestalten, da zwischen den Fliesen kein Leerraum vorhanden ist.) Wenn Sie fertig sind - gilt nicht für alle anderen Polygone mit höherem n, n> = 7. 

(Matplotlib verwendet den Ausdruck Hexbin plot, so (AFAIK) alle Plotting-Bibliotheken für R; immer noch nicht Ich weiß, ob dies der allgemein akzeptierte Begriff für Diagramme dieses Typs ist, obwohl ich vermute, dass es wahrscheinlich ist, dass Hexbin die Abkürzung für Hexagonal Binningist, was den wesentlichen Schritt bei der Vorbereitung der Daten beschreibt Zur Ausstellung.)


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

103
doug

Anstelle von np.hist2d, das im Allgemeinen recht hässliche Histogramme erzeugt, möchte ich py-sphviewer ein Python-Paket zum Rendern von Partikelsimulationen mit einem adaptiven Glättungskernel recyceln (siehe Webseiten-Dokumentation). Betrachten Sie den folgenden Code, der auf dem Beispiel basiert:

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

was folgendes Bild erzeugt:

 enter image description here

Wie Sie sehen, sehen die Bilder ziemlich schön aus, und wir können verschiedene Unterstrukturen darauf erkennen. Diese Bilder sind so aufgebaut, dass sie für jeden Punkt innerhalb eines bestimmten Bereichs ein gegebenes Gewicht ausbreiten, definiert durch die Glättungslänge, die wiederum durch die Entfernung zum nächsten Nachbarn nb angegeben wird (ich habe 16, 32 und 64 gewählt die Beispiele). Daher sind Regionen mit höherer Dichte typischerweise im Vergleich zu Regionen mit niedrigerer Dichte über kleinere Regionen verteilt. 

Die Funktion myplot ist nur eine sehr einfache Funktion, die ich geschrieben habe, um die XY-Daten an py-sphviewer zu übergeben, um die Magie auszuführen. 

26
Alejandro

Wenn Sie 1.2.x verwenden

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: Für eine bessere Annäherung an Alejandros Antwort siehe unten.

Ich weiß, das ist eine alte Frage, aber ich wollte Alejandros Antwort etwas hinzufügen: Wenn Sie ein Nice-geglättetes Bild ohne py-sphviewer wollen, können Sie stattdessen np.histogram2d verwenden und einen Gaußschen Filter (von scipy.ndimage.filters) auf die Heatmap anwenden:

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

Produziert:

 Output images

Das Streudiagramm und s = 16 für Agape Gal'lo (für eine bessere Ansicht) übereinander gezeichnet:

 On top of eachother


Ein Unterschied, den ich bei meinem Gauß-Filter-Ansatz und bei Alejandro festgestellt habe, war, dass seine Methode die lokalen Strukturen viel besser zeigt als meine. Deshalb habe ich auf Pixelebene eine einfache Nearest Neighbour-Methode implementiert. Diese Methode berechnet für jedes Pixel die inverse Summe der Abstände der n nächsten Punkte in den Daten. Diese Methode ist bei einer hohen Auflösung ziemlich rechenintensiv und ich denke, es gibt einen schnelleren Weg, also lassen Sie es mich wissen, wenn Sie Verbesserungen haben. Wie auch immer, hier ist der 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()

Ergebnis:

 Nearest Neighbour Smoothing

19
Jurgy

Seaborn hat jetzt die Jointplot-Funktion die hier gut funktionieren sollte:

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

und die anfängliche Frage war ... wie man Streuwerte in Rasterwerte umwandelt, richtig? histogram2d zählt die Häufigkeit pro Zelle. Wenn Sie jedoch andere Daten pro Zelle als nur die Häufigkeit haben, benötigen Sie zusätzliche Werte Arbeit zu tun.

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

Ich habe also einen Datensatz mit Z-Ergebnissen für X- und Y-Koordinaten. Ich berechnete jedoch nur wenige Punkte außerhalb des Interessengebiets (große Lücken) und viele Punkte in einem kleinen Interessengebiet.

Ja, hier wird es schwieriger, aber auch mehr Spaß. Einige Bibliotheken (sorry):

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

pyplot ist heute meine Grafik-Engine. cm ist eine Palette von Farbkarten mit einigen inkritischen Auswahlmöglichkeiten numpy für die Berechnungen, __.

Letzteres ist besonders wichtig, da die Häufigkeit der xy-Punkte in meinen Daten nicht gleichmäßig verteilt ist. Beginnen wir mit einigen Grenzen, die zu meinen Daten passen, und einer beliebigen Rastergröße. Die Originaldaten haben auch außerhalb dieser x- und y-Grenzen Datenpunkte.

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

Wir haben also ein Raster mit 500 Pixeln zwischen den minimalen und maximalen Werten von x und y definiert.

In meinen Daten gibt es viel mehr als die 500 Werte, die im Bereich von hohem Interesse verfügbar sind. Im Niedrigzinsbereich gibt es nicht einmal 200 Werte im gesamten Netz. zwischen den grafischen Grenzen von x_min und x_max gibt es noch weniger.

Um ein schönes Bild zu erhalten, besteht die Aufgabe darin, einen Durchschnitt für die hohen Zinsen zu ermitteln und die Lücken an anderer Stelle zu füllen.

Ich definiere jetzt mein Raster. Für jedes xx-yy-Paar möchte ich eine Farbe haben.

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

Warum die seltsame Form? scipy.griddata will eine Form von (n, D).

Griddata berechnet einen Wert pro Punkt im Raster durch eine vordefinierte Methode . Ich wähle "nächste" - leere Rasterpunkte werden mit Werten vom nächsten Nachbarn gefüllt. Dies sieht so aus, als hätten die Bereiche mit weniger Informationen größere Zellen (auch wenn dies nicht der Fall ist). Man könnte wählen, "linear" zu interpolieren, dann wirken Bereiche mit weniger Informationen weniger scharf. Geschmackssache wirklich.

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

Und hopp, wir übergeben Matplotlib, um den Plot anzuzeigen

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

Um den spitzen Teil der V-Form herum sehen Sie, dass ich bei der Suche nach dem Sweet Spot viele Berechnungen durchgeführt habe, während die weniger interessanten Teile fast überall eine niedrigere Auflösung haben.

 Heatmap of a SVC in high resolution

2
Anderas

Erstellen Sie ein zweidimensionales Array, das den Zellen in Ihrem endgültigen Bild entspricht, mit dem Namen say heatmap_cells, und instanziieren Sie es als alle Nullen.

Wählen Sie für jede Dimension zwei Skalierungsfaktoren aus, die den Unterschied zwischen den einzelnen Array-Elementen in reellen Einheiten definieren, z. B. x_scale und y_scale. Wählen Sie diese so, dass alle Ihre Datenpunkte innerhalb der Grenzen des Heatmap-Arrays liegen.

Für jeden Rohdatenpunkt mit x_value und y_value:

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

2
meepmeep

Ich fürchte, ich komme ein bisschen zu spät zur Party, aber ich hatte vor einiger Zeit eine ähnliche Frage. Die akzeptierte Antwort (von @ptomato) hat mir geholfen, aber ich möchte dies auch posten, falls es jemandem von Nutzen ist.


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


Hier ist das Ergebnis  enter image description here 

0
Abhishek

Sehr ähnlich zu @ Piti's Antwort , aber 1 Aufruf statt 2 zum Erzeugen der Punkte:

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

Ausgabe:

 2d_gaussian_heatmap

0
Alaa M.

enter image description here

Hier ist eine, die ich anhand eines 1-Millionen-Punkt-Sets mit 3 Kategorien (rot, grün und blau) erstellt habe. Hier ist ein Link zum Repository, wenn Sie die Funktion ausprobieren möchten. 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