web-dev-qa-db-fra.com

Python: Création d'un histogramme 2D à partir d'une matrice numpy

Je suis nouveau sur Python.

J'ai une matrice numpy, de dimensions 42x42, avec des valeurs comprises entre 0 et 996. Je veux créer un histogramme 2D en utilisant ces données. J'ai consulté des didacticiels, mais ils semblent tous montrer comment créer des histogrammes 2D à partir de données aléatoires et non d'une matrice numpy.

Jusqu'à présent, j'ai importé:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors

Je ne suis pas sûr que ces importations soient correctes, j'essaie simplement de tirer ce que je peux des tutoriels que je vois.

J'ai la matrice numpy M avec toutes les valeurs qu'elle contient (comme décrit ci-dessus). En fin de compte, je veux qu'il ressemble à ceci:

2D histogram

évidemment, mes données seront différentes, mon intrigue devrait donc être différente. Quelqu'un peut-il me donner un coup de main?

Edit: Pour les besoins de mon propos, l'exemple de Hooked ci-dessous, utilisant matshow, est exactement ce que je recherche.

12
Kestrel

Si vous avez les données brutes des décomptes, vous pouvez utiliser plt.hexbin pour créer les tracés pour vous (à mon humble avis, c'est mieux qu'un réseau carré): Adapté de l'exemple de hexbin :

import numpy as np
import matplotlib.pyplot as plt

n = 100000
x = np.random.standard_normal(n)
y = 2.0 + 3.0 * x + 4.0 * np.random.standard_normal(n)
plt.hexbin(x,y)

plt.show()

enter image description here

Si vous avez déjà les valeurs Z dans une matrice que vous mentionnez, utilisez simplement plt.imshow ou plt.matshow:

XB = np.linspace(-1,1,20)
YB = np.linspace(-1,1,20)
X,Y = np.meshgrid(XB,YB)
Z = np.exp(-(X**2+Y**2))
plt.imshow(Z,interpolation='none')

enter image description here

20
Hooked

Si vous disposez non seulement de la matrice d'histogramme 2D, mais également des données (x, y) sous-jacentes, vous pouvez créer un diagramme de dispersion des points (x, y) et colorier chaque point en fonction de sa valeur de comptage par lot dans la matrice d'histogramme 2D:

import numpy as np
import matplotlib.pyplot as plt

n = 10000
x = np.random.standard_normal(n)
y = 2.0 + 3.0 * x + 4.0 * np.random.standard_normal(n)
xedges, yedges = np.linspace(-4, 4, 42), np.linspace(-25, 25, 42)
hist, xedges, yedges = np.histogram2d(x, y, (xedges, yedges))
xidx = np.clip(np.digitize(x, xedges), 0, hist.shape[0]-1)
yidx = np.clip(np.digitize(y, yedges), 0, hist.shape[1]-1)
c = hist[xidx, yidx]
plt.scatter(x, y, c=c)

plt.show()

Example scatter plot of 2D histogram

12
unutbu

La réponse de @ unutbu contient une erreur: xidx et yidx sont calculés de manière incorrecte (du moins sur mon échantillon de données). La manière correcte devrait être:

xidx = np.clip(np.digitize(x, xedges) - 1, 0, hist.shape[0] - 1)
yidx = np.clip(np.digitize(y, yedges) - 1, 0, hist.shape[1] - 1)

En tant que dimension de retour de np.digitize qui nous intéresse, elle est comprise entre 1 et len(xedges) - 1, mais le c = hist[xidx, yidx] a besoin d'indices compris entre 0 et hist.shape - 1.


Vous trouverez ci-dessous la comparaison des résultats. Comme vous pouvez le constater, vous obtenez un résultat similaire mais différent.

import numpy as np
import matplotlib.pyplot as plt

fig = plt.figure()
ax1 = fig.add_subplot(211)
ax2 = fig.add_subplot(212)

n = 10000
x = np.random.standard_normal(n)
y = 2.0 + 3.0 * x + 4.0 * np.random.standard_normal(n)
xedges, yedges = np.linspace(-4, 4, 42), np.linspace(-25, 25, 42)
hist, xedges, yedges = np.histogram2d(x, y, (xedges, yedges))

xidx = np.clip(np.digitize(x, xedges), 0, hist.shape[0] - 1)
yidx = np.clip(np.digitize(y, yedges), 0, hist.shape[1] - 1)
c = hist[xidx, yidx]
old = ax1.scatter(x, y, c=c, cmap='jet')

xidx = np.clip(np.digitize(x, xedges) - 1, 0, hist.shape[0] - 1)
yidx = np.clip(np.digitize(y, yedges) - 1, 0, hist.shape[1] - 1)

c = hist[xidx, yidx]
new = ax2.scatter(x, y, c=c, cmap='jet')


plt.show()

 Same Same but Different

1
TheoryX

Je suis un grand fan de l'histogramme «scatter», mais je ne pense pas que les autres solutions leur rendent pleinement justice. Voici une fonction qui les implémente. L'avantage majeur de cette fonction par rapport aux autres solutions est qu'elle trie les points en fonction des données d'hist (voir l'argument mode). Cela signifie que le résultat ressemble davantage à un histogramme traditionnel (c’est-à-dire que vous n’obtenez pas le chevauchement chaotique des marqueurs dans différentes cases) .  The scatter-histogram

MCVE pour cette figure (en utilisant ma fonction ):

import numpy as np
import matplotlib.pyplot as plt
from hist_scatter import scatter_hist2d

fig = plt.figure(figsize=[5, 4])
ax = plt.gca()

x = randgen.randn(npoint)
y = 2 + 3 * x + 4 * randgen.randn(npoint)

scat = scatter_hist2d(x, y,
                      bins=[np.linspace(-4, 4, 42),
                            np.linspace(-25, 25, 42)],
                      s=5,
                      cmap=plt.get_cmap('viridis'))
ax.axhline(0, color='k', linestyle='--', zorder=3, linewidth=0.5)
ax.axvline(0, color='k', linestyle='--', zorder=3, linewidth=0.5)
plt.colorbar(scat)

Marge d'amélioration?

Le principal inconvénient de cette approche est que les points situés dans les zones les plus denses chevauchent les points situés dans les zones de densité inférieure, ce qui entraîne une représentation quelque peu fausse des zones de chaque groupe. J'ai passé pas mal de temps à explorer deux approches pour résoudre ce problème:

1) utiliser des marqueurs plus petits pour les bacs de densité supérieure

2) appliquer un masque de "découpage" à chaque bac

Le premier donne des résultats qui sont bien trop fous. Le second a l’air agréable - surtout si vous ne découpez que des bacs contenant> ~ 20 points - mais c’est extrêmement lent ( ce chiffre a pris environ une minute).

Donc, finalement, j'ai décidé qu'en choisissant soigneusement la taille du marqueur et la taille de la corbeille (s et bins), vous pouvez obtenir des résultats agréables visuellement et pas trop mauvais en termes de représentation erronée des données. Après tout, ces histogrammes 2D sont généralement destinés à être des aides visuelles aux données sous-jacentes, et non des représentations strictement quantitatives. Par conséquent, je pense que cette approche est de loin supérieure aux "histogrammes 2D traditionnels" (par exemple, plt.hist2d ou plt.hexbin) et je suppose que si vous avez trouvé cette page, vous n'êtes pas non plus un fan des diagrammes de dispersion traditionnels (monochromes).

Si j'étais le roi de la science, je m'assurerais que tous les histogrammes 2D fassent quelque chose de ce genre pour toujours.

0
farenorth