Frage

Ich habe eine Reihe von X-, Y -Datenpunkten (ca. 10K), die als Streudiagramm leicht zu zeichnen sind, aber ich möchte als Heatmap darstellen.

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

Gibt es eine Methode, die eine Menge X, Y, alle unterschiedlich, in eine Heatmap umwandelt (wo Zonen mit höherer Häufigkeit von x, y "wärmer" wären)?

War es hilfreich?

Lösung

Wenn Sie keine Sechsecke wollen, können Sie Numpy's verwenden histogram2d Funktion:

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 macht eine 50x50 Heatmap. Wenn Sie beispielsweise 512x384 möchten, können Sie einsetzen bins=(512, 384) im Anruf an histogram2d.

Beispiel: Matplotlib heat map example

Andere Tipps

Im Matplotlib Lexikon, ich denke du willst a Hexbin Handlung.

Wenn Sie mit dieser Art von Handlung nicht vertraut sind, ist es nur ein Bivariate Histogramm in dem die XY-Ebene von einem regelmäßigen Gitter von Sechseckern tesselliert wird.

Aus einem Histogramm können Sie also nur die Anzahl der Punkte zählen, die in jedem Sechseck fallen, und diskretiisieren Sie die Auftrittsregion als eine Menge von von Fenster, Weisen Sie jedem dieser Fenster jeden Punkt zu. Zum Schluss die Fenster auf a Farbarray, Und du hast ein Hexbin -Diagramm.

Obwohl weniger häufig verwendet als z.

  • Sechsecke haben Symmetrie der nächsten Nachbarn (z. B. Quadratmächer nicht, z. B. die Entfernung aus Ein Punkt an der Grenze eines Quadrats zu Ein Punkt in diesem Quadrat ist nicht überall gleich) und

  • Hexagon ist das höchste N-Polygon, das gibt Regelmäßige Flugzeug -Tessellation (dh Sie können Ihren Küchenboden sicher mit hexagonalförmigen Fliesen neu modellieren, da Sie bei Bearbeitung keinen Hohlraum zwischen den Kacheln haben-nicht für alle anderen höheren, n> = 7, Polygone ).

(Matplotlib Verwendet den Begriff Hexbin Handlung; So auch (afaik) alle von Bibliotheken aufweisen zum R; Trotzdem weiß ich nicht, ob dies der allgemein anerkannte Begriff für Diagramme dieser Art ist, obwohl ich vermute, dass dies wahrscheinlich das gegeben ist Hexbin ist kurz für Hexagonal Binning, was den wesentlichen Schritt bei der Vorbereitung der Daten für die Anzeige beschreibt.)


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

Anstatt NP.hist2d zu verwenden, was im Allgemeinen recht hässliche Histogramme erzeugt, möchte ich gerne recyceln Py-SPHVIEWER, ein Python -Paket zum Rendern von Partikelsimulationen mit einem adaptiven Glättungskernel und der problemlos aus PIP installiert werden kann (siehe Webseitendokumentation). 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()

Das erzeugt das folgende Bild:

enter image description here

Wie Sie sehen, sehen die Bilder ziemlich gut aus und wir können verschiedene Unterstrukturen identifizieren. Diese Bilder sind konstruiert und verbreiten ein bestimmtes Gewicht für jeden Punkt in einer bestimmten Domäne, definiert durch die Glättungslänge, die sich abwechselnd durch den Abstand zum näheren Abstand ergeben NB Nachbar (ich habe 16, 32 und 64 für die Beispiele ausgewählt). Daher werden Regionen mit höherer Dichte typischerweise über kleinere Regionen im Vergleich zu Regionen mit niedrigerer Dichte verteilt.

Die Funktion myplot ist nur eine sehr einfache Funktion, die ich geschrieben habe, um Py-SPHViewer-Daten zu den X- und Y-Daten zu geben, um die Magie zu machen.

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

Bearbeiten: Für eine bessere Annäherung an Alejandros Antwort siehe unten.

Ich weiß, dass dies eine alte Frage ist, wollte aber Alejandros Anwser etwas hinzufügen np.histogram2d und einen Gaußschen Filter anwenden (von scipy.ndimage.filters) zur Heatmap:

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, das für Agape Gal'lo aufeinander aufgetragen wurde (klicken Sie für eine bessere Ansicht):

On top of eachother


Ein Unterschied, den ich mit meinem Gaußschen Filteransatz und dem Ansatz meines Alejandro bemerkte, war, dass seine Methode lokale Strukturen viel besser zeigt als meine. Daher habe ich eine einfache Methode der nächsten Nachbarn auf Pixelebene implementiert. Diese Methode berechnet für jedes Pixel die inverse Summe der Entfernungen der n Spendest Punkte in den Daten. Diese Methode befindet sich bei einer hohen Auflösung ziemlich rechenintensiv und ich denke, es gibt einen schnelleren Weg. Lassen Sie mich also wissen, ob 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

Seeborn hat jetzt das JointPlot -Funktion was 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

Und die erste Frage war ... wie man Streuwerte in Gitterwerte konvertieren, oder?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 Arbeiten.

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-Resulten für X- und Y-Koordinaten. Ich berechnete jedoch nur wenige Punkte außerhalb des Interessesbereichs (große Lücken) und Haufen von Punkten in einem kleinen Interessenbereich.

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 mein Grafikmotor, CM ist eine Reihe von Farbkarten mit einer gewissen Auswahl. Numpy für die Berechnungen und Griddata zum Anbringen von Werten an einem festen Netz.

Das letzte ist vor allem wichtig, da die Häufigkeit von XY -Punkten in meinen Daten nicht gleich verteilt ist. Beginnen wir zunächst mit einigen Grenzen, die an meine Daten und eine willkürliche Gittergröße passen. Die ursprünglichen Daten haben auch Datenpunkte außerhalb dieser X- und Y -Grenzen.

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

Wir haben also ein Gitter mit 500 Pixel zwischen den Min- und maximalen Werten von x und y definiert.

In meinen Daten stehen im Bereich von großem Interesse viel mehr als die 500 Werte zur Verfügung. Während im niedrig-zinsgünstigen Bereich nicht einmal 200 Werte im Gesamtnetz enthalten sind; zwischen den grafischen Grenzen von x_min und x_max Es gibt noch weniger.

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

Ich definiere jetzt mein Netz. Für jedes XX-YJ-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 Gitter mit einer vordefinierten Methode. Ich wähle "nächstgelegen" - leere Netzpunkte werden mit Werten aus dem 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 sich dafür entscheiden, "linear" zu interpolieren, und Bereiche mit weniger Informationen sehen weniger scharf aus. Wirklich Geschmack.

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 hüpfen, wir geben an Matplotlib über, um das Diagramm 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, sehen Sie, dass ich während meiner Suche nach dem Sweet Spot viele Berechnungen durchgeführt habe, während die weniger interessanten Teile fast überall sonst eine geringere Auflösung haben.

Heatmap of a SVC in high resolution

Machen Sie ein zweidimensionales Array, das den Zellen in Ihrem endgültigen Bild entspricht, namens Say heatmap_cells und instanziieren Sie es als alle Nullen.

Wählen Sie zwei Skalierungsfaktoren, die den Unterschied zwischen jedem Array -Element in realen Einheiten für jede Dimension definieren, beispielsweise für jede Dimension x_scale und y_scale. Wählen Sie diese so, dass alle Ihre Datenpunkte in die Grenzen des Heatmap -Arrays fallen.

Für jeden rohen Datenpunkt mit x_value und y_value:

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

Sehr ähnlich zu @Pitis Antwort, aber mit 1 Anruf anstelle von 2, um die Punkte zu generieren:

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

Ich fürchte, ich bin ein bisschen 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 veröffentlichen, falls es für jemanden 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 Ergebnisenter image description here

enter image description here

Hier ist eine, die ich auf einem 1 -Million -Punkt -Set mit 3 Kategorien (farbig rot, grün und blau) gemacht 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)
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top