Pregunta ¿Cómo visualizar datos 2D escalares con Matplotlib?


Entonces tengo una malla de malla (matrices X e Y) junto con datos escalares (matriz Z), y necesito visualizar esto. Preferentemente, una imagen 2D con colores en los puntos que muestran el valor de Z allí. Investigué pero no encontré nada que haga exactamente lo que quiero.

pyplot.imshow (Z) tiene una buena apariencia, pero no toma mis matrices X e Y, por lo que los ejes son incorrectos y no es capaz de manejar los puntos espaciados no linealmente dados por X e Y.

pyplot.pcolor (X, Y, Z) hace cuadrados de colores con colores correspondientes a los datos en una de sus esquinas, por lo que tergiversa los datos (debe mostrar los datos en su centro o algo así). Además, ignora dos de los bordes de la matriz de datos.

Estoy bastante seguro de que debe existir una forma mejor en algún lugar en Matplotlib, pero la documentación hace que sea difícil obtener una visión general. Entonces, estoy preguntando si alguien más sabe de una mejor manera. Bonificación si me permite actualizar la matriz Z para hacer una animación.


10
2018-02-26 15:10


origen


Respuestas:


Esto se ve bien, pero es ineficiente:

from pylab import *
origin = 'lower'

delta = 0.025

x = y = arange(-3.0, 3.01, delta)
X, Y = meshgrid(x, y)
Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = 10 * (Z1 - Z2)

nr, nc = Z.shape

CS = contourf(
    X, Y, Z,
    levels = linspace(Z.min(), Z.max(), len(x)),
    ls = '-',
    cmap=cm.bone,
    origin=origin)

CS1 = contour(
    CS,
    levels = linspace(Z.min(), Z.max(), len(x)),
    ls = '-',
    cmap=cm.bone,
    origin=origin)

show()

Si fuera yo, volvería a interpolar (usando scipy.interpolate) los datos a una grilla normal y usar imshow (), estableciendo las extensiones para arreglar los ejes.

fine contour

Editar (por comentario):

Animar una trama de contorno se puede lograr así, pero, como dije, lo anterior es ineficiente, simplemente abuso de la función de trazado de curvas de nivel. La forma más eficiente de hacer lo que desea es emplear SciPy. ¿Lo tienes instalado?

import matplotlib
matplotlib.use('TkAgg') # do this before importing pylab
import time
import matplotlib.pyplot as plt

fig = plt.figure()
ax = fig.add_subplot(111)

def animate():
    origin = 'lower'
    delta = 0.025

    x = y = arange(-3.0, 3.01, delta)
    X, Y = meshgrid(x, y)
    Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
    Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
    Z = 10 * (Z1 - Z2)

    CS1 = ax.contourf(
        X, Y, Z,
        levels = linspace(Z.min(), Z.max(), 10),
        cmap=cm.bone,
        origin=origin)

    for i in range(10):
        tempCS1 = contourf(
            X, Y, Z,
            levels = linspace(Z.min(), Z.max(), 10),
            cmap=cm.bone,
            origin=origin)
        del tempCS1
        fig.canvas.draw()
        time.sleep(0.1)
        Z += x/10

win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate)
plt.show()

9
2018-02-26 16:08



Si tu malla de malla tiene un espaciado uniforme, podrías continuar usando pcolor, pero simplemente cambie X e Y a los efectos de centrar los datos en los valores particulares en lugar de en las esquinas.

También podría usar un diagrama de dispersión para colocar explícitamente puntos de algún tamaño en los puntos X e Y exactos y luego establecer el color en Z:

x = numpy.arange(10)
y = numpy.arange(10)
X,Y = numpy.meshgrid(x,y)
Z = numpy.arange(100).reshape((10,10))
scatter(X,Y,c=Z,marker='s',s=1500) 
#I picked a marker size that basically overlapped the symbols at the edges
axis('equal')

o:

pcolor(X+0.5,Y+0.5,Z)
axis('equal')

o como sugirió Paul, usando una de las funciones de contorno


2
2018-02-26 15:56



En caso de que alguien se encuentre con este artículo buscando lo que estaba buscando, tomé el ejemplo anterior y lo modifiqué para usar imshow con una pila de marcos de entrada, en lugar de generar y usar contornos sobre la marcha. Comenzando con una matriz 3D de imágenes de forma (nBins, nBins, nBins), llamadas frames.

def animate_frames(frames):
    nBins   = frames.shape[0]
    frame   = frames[0]
    tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
    for k in range(nBins):
        frame   = frames[k]
        tempCS1 = plt.imshow(frame, cmap=plt.cm.gray)
        del tempCS1
        fig.canvas.draw()
        #time.sleep(1e-2) #unnecessary, but useful
        fig.clf()

fig = plt.figure()
ax  = fig.add_subplot(111)

win = fig.canvas.manager.window
fig.canvas.manager.window.after(100, animate_frames, frames)

También encontré una manera mucho más simple de llevar a cabo todo este proceso, aunque menos robusto:

fig = plt.figure()

for k in range(nBins):
    plt.clf()
    plt.imshow(frames[k],cmap=plt.cm.gray)
    fig.canvas.draw()
    time.sleep(1e-6) #unnecessary, but useful

Tenga en cuenta que ambos parecen funcionar solo con ipython --pylab=tk, a.k.a.backend = TkAgg

Gracias por la ayuda con todo.


1
2018-04-20 19:21



La siguiente función crea cuadros de la mitad del tamaño en el límite (como se muestra en la imagen adjunta).

import matplotlib.pyplot as plt
import numpy as np
from scipy.ndimage.filters import convolve

def pcolor_all(X, Y, C, **kwargs):
    X = np.concatenate([X[0:1,:], X], axis=0)
    X = np.concatenate([X[:,0:1], X], axis=1)

    Y = np.concatenate([Y[0:1,:], Y], axis=0)
    Y = np.concatenate([Y[:,0:1], Y], axis=1)

    X = convolve(X, [[1,1],[1,1]])/4
    Y = convolve(Y, [[1,1],[1,1]])/4

    plt.pcolor(X, Y, C, **kwargs)

X, Y = np.meshgrid(
    [-1,-0.5,0,0.5,1],
    [-2,-1,0,1,2])

C = X**2-Y**2

plt.figure(figsize=(4,4))

pcolor_all(X, Y, C, cmap='gray')

plt.savefig('plot.png')

plot.png


0
2017-07-31 09:35