Сглаживание данных на контурном графике с помощью Matplotlib

Я работаю над созданием контурного графика с помощью Matplotlib. У меня есть все данные в многомерном массиве. Это 12 в длину и около 2000 в ширину. Таким образом, это в основном список из 12 списков длиной 2000. У меня контурный график работает нормально, но мне нужно сгладить данные. Я прочитал много примеров. К сожалению, у меня нет математического образования, чтобы понять, что с ними происходит.

Итак, как я могу сгладить эти данные? У меня есть пример того, как выглядит мой график и как я хочу, чтобы он выглядел больше.

Это мой график:

графическая иллюстрация

Что я хочу, чтобы это выглядело более похоже:

моя цель

Какими средствами я должен сгладить контурный график, как на втором графике?


Данные, которые я использую, извлекаются из XML-файла. Но я покажу вывод части массива. Поскольку каждый элемент в массиве имеет длину около 2000 элементов, я покажу только отрывок.

Вот пример:

[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]

Имейте в виду, что это только отрывок. Размер данных составляет 12 строк на 1959 столбцов. Столбцы изменяются в зависимости от данных, импортированных из XML-файла. Я могу посмотреть на значения после использования Gaussian_filter, и они действительно меняются. Но изменения не настолько велики, чтобы повлиять на контурный график.


person dman87    schedule 08.11.2011    source источник


Ответы (2)


Вы можете сгладить данные с помощью гауссовский_фильтр:

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

введите здесь описание изображения

Слева показаны исходные данные, справа — после гауссовой фильтрации.

Большая часть приведенного выше кода была взята из поваренной книги Scipy, в которой демонстрируется сглаживание по Гауссу с использованием сделанного вручную ядро Гаусса. Поскольку scipy поставляется с тем же встроенным, я решил использовать gaussian_filter.

person unutbu    schedule 08.11.2011
comment
Я смотрел на этот пример раньше. Однако я не могу заставить это работать на моем массиве. Я должен отметить, что мой массив представляет собой список python, а не массив numpy. Это вызовет проблемы? Если да, то как проще всего преобразовать список python в массив numpy? - person dman87; 09.11.2011
comment
Хм, на самом деле ndimage.gaussian_filter может прекрасно работать со списками списков. (Например, ndimage.gaussian_filter(Z.tolist()) работает.) Проблема должна быть в другом месте. Трудно сказать, не видя данных. Что значит не работает? Возникает ли исключение? или результат просто не выглядит правильно? - person unutbu; 09.11.2011
comment
Извините, я должен был быть более конкретным. Я считаю, что это была проблема со строкой данных в списках. Хотя функция контур() на это не жаловалась. У меня заработало без ошибок. Но это вообще не меняет вывод контура(). - person dman87; 09.11.2011
comment
Можете ли вы отредактировать свой вопрос с кратким примером того, как выглядят ваши данные? Является ли это списком строк или списком строк, и являются ли строки строковыми представлениями чисел, например. 1.5, или это бинарные данные, которые нужно struct.unpacked? - person unutbu; 09.11.2011
comment
Я привел краткий отрывок выше. Это после того, как я конвертирую строку в числа с плавающей запятой. - person dman87; 09.11.2011
comment
Да, я пробовал от 0,25 до 1000 с теми же результатами, когда смотрю на контур. Порядок kwarg, похоже, тоже не имеет большого значения. - person dman87; 09.11.2011
comment
Подождите, в моем коде была ошибка. (Ударяется головой...) Я думаю, что пока это работает. Я не буду, хотя, это не очень сглаживает неровные края контура. Когда я увеличиваю значения сигмы, это в конечном итоге просто уменьшает уровни контура, создаваемого из данных. - person dman87; 09.11.2011

Одним из простых способов сглаживания данных является использование алгоритма скользящего среднего. Одной из простых форм скользящего среднего является вычисление среднего значения соседних измерений в определенной позиции. Например, в одномерном ряду измерений a[1:N] скользящее среднее в точке a[n] может быть рассчитано как a[n] = (a[n-1] + a[n] + a[ n+1])/3, например. Если вы пройдете все свои измерения, все готово. В этом простом примере наше окно усреднения имеет размер 3. Вы также можете использовать окна разных размеров, в зависимости от того, насколько сильное сглаживание вы хотите.

Чтобы сделать вычисления проще и быстрее для более широкого круга приложений, вы также можете использовать алгоритм, основанный на свертке. Преимущество использования свертки заключается в том, что вы можете выбирать различные виды средних значений, например средневзвешенные значения, просто меняя окно.

Давайте сделаем код для иллюстрации. Для следующего фрагмента требуются установленные Numpy, Matplotlib и Scipy. Нажмите здесь, чтобы просмотреть полный пример кода

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

Следующий код генерирует некоторые произвольные и зашумленные данные, а затем вычисляет скользящее среднее с использованием четырех окон разного размера.

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)

А затем, чтобы увидеть разные результаты, вот код для построения графика.

# 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!

А вот результаты на графике для окон разного размера: Results

В приведенном здесь примере кода используется простое рамочное (или прямоугольное) окно в двух измерениях. Доступно несколько различных типов окон, и вы можете проверить Википедию. для большего количества примеров.

person regeirk    schedule 13.11.2011