python - graficos - Suavizado de datos en diagrama de contorno con Matplotlib
graficos en python (2)
Estoy trabajando en la creación de una gráfica de contorno usando Matplotlib. Tengo todos los datos en una matriz que es multidimensional. Tiene 12 de largo y 2000 de ancho. Entonces, básicamente es una lista de 12 listas de 2000 de longitud. Tengo la trama de contorno funcionando bien, pero necesito suavizar los datos. He leído muchos ejemplos. Desafortunadamente, no tengo los conocimientos de matemática para entender qué está pasando con ellos.
Entonces, ¿cómo puedo suavizar esta información? Tengo un ejemplo de cómo se ve mi gráfico y cómo quiero que se vea más.
Este es mi gráfico:
Lo que quiero que se vea más similar también:
¿Qué significa que tengo que suavizar la trama del contorno como en el segundo gráfico?
Los datos que estoy utilizando se extraen de un archivo XML. Pero, mostraré la salida de parte de la matriz. Como cada elemento de la matriz tiene alrededor de 2000 elementos, solo mostraré un fragmento.
Aquí hay una muestra:
[27.899999999999999, 27.899999999999999, 27.899999999999999, 27.899999999999999,
28.0, 27.899999999999999, 27.899999999999999, 28.100000000000001, 28.100000000000001,
28.100000000000001, 28.100000000000001, 28.100000000000001, 28.100000000000001,
28.100000000000001, 28.100000000000001, 28.0, 28.100000000000001, 28.100000000000001,
28.0, 28.100000000000001, 28.100000000000001, 28.100000000000001, 28.100000000000001,
28.100000000000001, 28.100000000000001, 28.100000000000001, 28.100000000000001,
28.100000000000001, 28.100000000000001, 28.100000000000001, 28.100000000000001,
28.100000000000001, 28.100000000000001, 28.100000000000001, 28.100000000000001,
28.100000000000001, 28.100000000000001, 28.0, 27.899999999999999, 28.0,
27.899999999999999, 27.800000000000001, 27.899999999999999, 27.800000000000001,
27.800000000000001, 27.800000000000001, 27.899999999999999, 27.899999999999999, 28.0,
27.800000000000001, 27.800000000000001, 27.800000000000001, 27.899999999999999,
27.899999999999999, 27.899999999999999, 27.899999999999999, 28.0, 28.0, 28.0, 28.0,
28.0, 28.0, 28.0, 28.0, 27.899999999999999, 28.0, 28.0, 28.0, 28.0, 28.0,
28.100000000000001, 28.0, 28.0, 28.100000000000001, 28.199999999999999,
28.300000000000001, 28.300000000000001, 28.300000000000001, 28.300000000000001,
28.300000000000001, 28.399999999999999, 28.300000000000001, 28.300000000000001,
28.300000000000001, 28.300000000000001, 28.300000000000001, 28.300000000000001,
28.399999999999999, 28.399999999999999, 28.399999999999999, 28.399999999999999,
28.399999999999999, 28.300000000000001, 28.399999999999999, 28.5, 28.399999999999999,
28.399999999999999, 28.399999999999999, 28.399999999999999]
Tenga en cuenta que esto es solo un extracto. La dimensión de los datos es de 12 filas por columnas de 1959. Las columnas cambian según los datos importados del archivo XML. Puedo ver los valores después de usar el Gaussian_filter y cambian. Pero los cambios no son lo suficientemente grandes como para afectar la trama del contorno.
Puede suavizar sus datos con un gaussian_filter :
import numpy as np
import matplotlib.pyplot as plt
import scipy.ndimage as ndimage
X, Y = np.mgrid[-70:70, -70:70]
Z = np.cos((X**2+Y**2)/200.)+ np.random.normal(size=X.shape)
# Increase the value of sigma to increase the amount of blurring.
# order=0 means gaussian kernel
Z2 = ndimage.gaussian_filter(Z, sigma=1.0, order=0)
fig=plt.figure()
ax=fig.add_subplot(1,2,1)
ax.imshow(Z)
ax=fig.add_subplot(1,2,2)
ax.imshow(Z2)
plt.show()
El lado izquierdo muestra los datos originales, el lado derecho después del filtrado gaussiano.
Gran parte del código anterior fue tomado del Scipy Cookbook , que demuestra el alisado gaussiano usando un kernel gauss hecho a mano. Como scipy viene con el mismo integrado, elegí usar gaussian_filter
.
Una manera fácil de suavizar los datos es usar un algoritmo de promedio móvil . Una forma simple de promedio móvil es calcular el promedio de las mediciones adyacentes en una posición determinada. En una serie unidimensional de mediciones a [1: N], por ejemplo, la media móvil en a [n] puede calcularse como a [n] = (a [n-1] + a [n] + a [ n + 1]) / 3, por ejemplo. Si revisas todas tus medidas, terminaste. En este ejemplo simple, nuestra ventana de promediación tiene el tamaño 3. También puede usar ventanas de diferentes tamaños, dependiendo de la cantidad de suavizado que desee.
Para hacer los cálculos más fáciles y rápidos para una gama más amplia de aplicaciones, también puede usar un algoritmo basado en convolución . La ventaja de usar convolución es que puede elegir diferentes tipos de promedios, como promedios ponderados, simplemente cambiando la ventana.
Hagamos algo de codificación para ilustrar. El siguiente extracto necesita Numpy, Matplotlib y Scipy instalados. Haga clic aquí para obtener el código de muestra completo
from __future__ import division
import numpy
import pylab
from scipy.signal import convolve2d
def moving_average_2d(data, window):
"""Moving average on two-dimensional data.
"""
# Makes sure that the window function is normalized.
window /= window.sum()
# Makes sure data array is a numpy array or masked array.
if type(data).__name__ not in [''ndarray'', ''MaskedArray'']:
data = numpy.asarray(data)
# The output array has the same dimensions as the input data
# (mode=''same'') and symmetrical boundary conditions are assumed
# (boundary=''symm'').
return convolve2d(data, window, mode=''same'', boundary=''symm'')
El siguiente código genera algunos datos arbitrarios y ruidosos y luego calcula el promedio móvil usando cuatro ventanas de caja de diferentes tamaños.
M, N = 20, 2000 # The shape of the data array
m, n = 3, 10 # The shape of the window array
y, x = numpy.mgrid[1:M+1, 0:N]
# The signal and lots of noise
signal = -10 * numpy.cos(x / 500 + y / 10) / y
noise = numpy.random.normal(size=(M, N))
z = signal + noise
# Calculating a couple of smoothed data.
win = numpy.ones((m, n))
z1 = moving_average_2d(z, win)
win = numpy.ones((2*m, 2*n))
z2 = moving_average_2d(z, win)
win = numpy.ones((2*m, 4*n))
z3 = moving_average_2d(z, win)
win = numpy.ones((2*m, 10*n))
z4 = moving_average_2d(z, win)
Y luego, para ver los diferentes resultados, aquí está el código para algunos gráficos.
# Initializing the plot
pylab.close(''all'')
pylab.ion()
fig = pylab.figure()
bbox = dict(edgecolor=''w'', facecolor=''w'', alpha=0.9)
crange = numpy.arange(-15, 16, 1.) # color scale data range
# The plots
ax = pylab.subplot(2, 2, 1)
pylab.contourf(x, y, z, crange)
pylab.contour(x, y, z1, crange, colors=''k'')
ax.text(0.05, 0.95, ''n=10, m=3'', ha=''left'', va=''top'', transform=ax.transAxes,
bbox=bbox)
bx = pylab.subplot(2, 2, 2, sharex=ax, sharey=ax)
pylab.contourf(x, y, z, crange)
pylab.contour(x, y, z2, crange, colors=''k'')
bx.text(0.05, 0.95, ''n=20, m=6'', ha=''left'', va=''top'', transform=bx.transAxes,
bbox=bbox)
bx = pylab.subplot(2, 2, 3, sharex=ax, sharey=ax)
pylab.contourf(x, y, z, crange)
pylab.contour(x, y, z3, crange, colors=''k'')
bx.text(0.05, 0.95, ''n=40, m=6'', ha=''left'', va=''top'', transform=bx.transAxes,
bbox=bbox)
bx = pylab.subplot(2, 2, 4, sharex=ax, sharey=ax)
pylab.contourf(x, y, z, crange)
pylab.contour(x, y, z4, crange, colors=''k'')
bx.text(0.05, 0.95, ''n=100, m=6'', ha=''left'', va=''top'', transform=bx.transAxes,
bbox=bbox)
ax.set_xlim([x.min(), x.max()])
ax.set_ylim([y.min(), y.max()])
fig.savefig(''movingavg_sample.png'')
# That''s all folks!
Y aquí están los resultados trazados para ventanas de diferentes tamaños:
El código de ejemplo proporcionado aquí utiliza una ventana simple de cuadro (o rectangular) en dos dimensiones. Hay varios tipos diferentes de ventanas disponibles y es posible que desee consultar Wikipedia para obtener más ejemplos.