Как визуализировать скалярные 2D-данные с помощью Matplotlib?

Итак, у меня есть meshgrid (матрицы X и Y) вместе со скалярными данными (матрица Z), и мне нужно визуализировать это. Предпочтительно некоторое двумерное изображение с цветами в точках, показывающих значение Z. Я провел некоторое исследование, но не нашел ничего, что делает именно то, что я хочу.

pyplot.imshow(Z) имеет хороший внешний вид, но он не принимает мои X и Y-матрицы, поэтому оси неправильны и не могут обрабатывать нелиноразмерные точки, заданные X и Y.

pyplot.pcolor(X, Y, Z) создает цветные квадраты с цветами, соответствующими данным в одном из его углов, поэтому он искажает данные (он должен показывать данные в своем центре или что-то еще). Кроме того, он игнорирует два ребра из матрицы данных.

Я уверен, что в Matplotlib должен быть какой-то лучший способ, но документация затрудняет получение обзора. Поэтому я спрашиваю, знает ли кто-то другой лучший способ. Бонус, если он позволяет мне обновить матрицу Z, чтобы сделать анимацию.

Ответ 1

Это выглядит хорошо, но неэффективно:

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

Это был я, я бы повторно интерполировал (используя scipy.interpolate) данные в обычную сетку и использовал imshow(), установив экстенты для фиксации осей.

fine contour

Изменить (за комментарий):

Анимация контурного сюжета может быть выполнена подобным образом, но, как я уже сказал, вышеизложенное является неэффективным, просто злоупотреблением функцией контурного графика. Самый эффективный способ сделать то, что вы хотите, - это использовать SciPy. У вас установлено это?

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

Ответ 2

Если ваш meshgrid имеет равномерный интервал, вы можете продолжать использовать pcolor, а просто сдвигать X и Y для центрирования данных по конкретным значениям, а не по углам.

Вы также можете использовать график рассеяния, чтобы явно разместить точки определенного размера в точках X и Y, а затем установить цвет в 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')

или

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

или, как предложил Павел, используя одну из контурных функций

Ответ 3

В случае, если кто-то попадает в эту статью в поисках того, что я искал, я взял приведенный выше пример и изменил его, чтобы использовать imshow с входным стеком фреймов вместо того, чтобы генерировать и использовать контуры на лету. Начиная с 3D-массива изображений формы (nBins, nBins, nBins), называется 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)

Я также нашел гораздо более простой способ обойти весь этот процесс, хотя и менее надежный:

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

Обратите внимание, что обе эти функции работают только с ipython --pylab=tk, a.k.a. backend = TkAgg

Спасибо за помощь во всем.

Ответ 4

Следующая функция создает поля с половиной размера на границе (как показано на прилагаемом рисунке).

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