передискретизация, интерполирующая матрица

Я пытаюсь интерполировать некоторые данные для построения графика. Например, учитывая N точек данных, я хотел бы иметь возможность генерировать «гладкий» график, состоящий из 10 * N или около того интерполированных точек данных.

Мой подход заключается в создании матрицы N на 10 * N и вычислении внутреннего произведения исходного вектора и матрицы, которую я сгенерировал, что дает вектор 1 на 10 * N. Я уже разработал математику, которую хотел бы использовать для интерполяции, но мой код довольно медленный. Я новичок в Python, поэтому я надеюсь, что некоторые из экспертов здесь могут дать мне некоторые идеи о том, как я могу попытаться ускорить свой код.

Я думаю, что часть проблемы заключается в том, что для создания матрицы требуется 10 * N ^ 2 вызовов следующей функции:

def sinc(x):
    import math
    try:
        return math.sin(math.pi * x) / (math.pi * x)
    except ZeroDivisionError:
        return 1.0

(Это вытекает из теории выборки. По сути, я пытаюсь воссоздать сигнал из его сэмплов и повысить его частоту до более высокой частоты.)

Матрица создается следующим образом:

def resampleMatrix(Tso, Tsf, o, f):
    from numpy import array as npar
    retval = []

    for i in range(f):
        retval.append([sinc((Tsf*i - Tso*j)/Tso) for j in range(o)])

    return npar(retval)

Я рассматриваю возможность разбить задачу на более мелкие части, потому что мне не нравится идея матрицы N ^ 2, находящейся в памяти. Вероятно, я мог бы превратить 'resampleMatrix' в функцию генератора и выполнять внутренний продукт построчно, но я не думаю, что это сильно ускорит мой код, пока я не начну подкачивать данные в память и из нее.

Заранее спасибо за ваши предложения!


person Phil    schedule 05.12.2009    source источник
comment
совершенно независимо от того, что вы пытаетесь сделать со своим кодом, идея о том, что вы можете просто интерполировать дополнительные точки без генеративной модели данных, неверна. если вы хотите сделать это каким-либо статистически принципиальным способом, вам нужно выполнить какую-то регрессию. см. en.wikipedia.org/wiki/Generative_model   -  person twolfe18    schedule 05.12.2009
comment
Похоже, Фил хочет использовать интерполяцию только для построения графиков. Пока интерполированные точки не используются для другой цели, я не понимаю, зачем нужна генеративная модель.   -  person Jitse Niesen    schedule 07.12.2009
comment
@Phil: Любая конкретная причина, по которой вы хотите использовать интерполяцию sinc, учитывая, что это алгоритм O (N ^ 2), а другие методы, такие как кубический сплайн, имеют только O (N)?   -  person Jitse Niesen    schedule 07.12.2009
comment
@twole18: Модель данных такова, что они были выбраны в соответствии с en. wikipedia.org/wiki/Nyquist%E2%80%93Shannon_sampling_theorem. Вы можете точно восстановить оригинал, используя функции sinc.   -  person endolith    schedule 21.09.2011
comment
Кстати, в numpy уже есть функция sinc(). docs.scipy.org/doc/numpy/reference/generated/ numpy.sinc.html   -  person endolith    schedule 21.09.2011


Ответы (8)


Это повышение частоты дискретизации. См. Помощь с передискретизацией/апсемплингом для некоторых примеров решений.

Быстрый способ сделать это (для автономных данных, таких как ваше графическое приложение) — использовать БПФ. Это то, что делает нативная функция SciPy resample(). . Однако он предполагает периодический сигнал, , так что это не совсем то же самое. См. эту ссылку:

Вот вторая проблема, связанная с интерполяцией реального сигнала во временной области, и это действительно большое дело. Этот алгоритм точной интерполяции дает правильные результаты только в том случае, если исходная последовательность x(n) является периодической в ​​пределах своего полного временного интервала.

Ваша функция предполагает, что все выборки сигнала равны 0 за пределами определенного диапазона, поэтому два метода будут расходиться от центральной точки. Если вы сначала дополните сигнал большим количеством нулей, это даст очень близкий результат. Есть еще несколько нулей за краем графика, не показанного здесь:

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

Кубическая интерполяция не подходит для повторной выборки. Этот пример — крайний случай (около частоты дискретизации), но, как видите, кубическая интерполяция и близко не подходит. Для более низких частот это должно быть довольно точным.

person endolith    schedule 21.09.2011
comment
Спасибо за ответ! @endolith Я заметил ваш комментарий ниже. Вы правы, я должен был сформулировать свой вопрос яснее с самого начала. - person Phil; 23.09.2011

Если вы хотите интерполировать данные довольно общим и быстрым способом, сплайны или полиномы очень полезны. В Scipy есть очень полезный модуль scipy.interpolate. Вы можете найти множество примеров на официальных страницах.

person Eric O Lebigot    schedule 05.12.2009

Ваш вопрос не совсем ясен; вы пытаетесь оптимизировать код, который вы разместили, верно?

Переписывание sinc должно значительно ускорить его. Эта реализация избегает проверки того, что математический модуль импортируется при каждом вызове, не выполняет доступ к атрибуту три раза и заменяет обработку исключений условным выражением:

from math import sin, pi
def sinc(x):
    return (sin(pi * x) / (pi * x)) if x != 0 else 1.0

Вы также можете попробовать избежать создания матрицы дважды (и дважды удерживать ее параллельно в памяти), создав numpy.array напрямую (а не из списка списков):

def resampleMatrix(Tso, Tsf, o, f):
    retval = numpy.zeros((f, o))
    for i in xrange(f):
        for j in xrange(o):
            retval[i][j] = sinc((Tsf*i - Tso*j)/Tso)
    return retval

(замените xrange на range в Python 3.0 и выше)

Наконец, вы можете создавать строки с помощью numpy.arange, а также вызывать numpy.sinc для каждой строки или даже для всей матрицы:

def resampleMatrix(Tso, Tsf, o, f):
    retval = numpy.zeros((f, o))
    for i in xrange(f):
        retval[i] = numpy.arange(Tsf*i / Tso, Tsf*i / Tso - o, -1.0)
    return numpy.sinc(retval)

Это должно быть значительно быстрее, чем ваша первоначальная реализация. Попробуйте разные комбинации этих идей и проверьте их эффективность, посмотрите, какая из них работает лучше всего!

person taleinat    schedule 05.12.2009
comment
заменяет обработку исключений условным выражением, но исключения работают быстрее, чем условные выражения в python. также было бы быстрее сделать pi*x один раз и использовать его дважды, верно? - person endolith; 19.02.2014
comment
@endolith Это неправда, что исключения быстрее, чем условные выражения в Python, это действительно зависит от того, как часто возникает исключительное условие. В любом случае, это должно быть совершенно незначительным здесь по сравнению с избеганием импорта и поиска атрибутов при каждом вызове функции. Не использовать здесь try/except — вопрос стиля и ясности кода. - person taleinat; 12.03.2014
comment
@endolith Что касается pi * x, я не уверен, что создание новой локальной переменной, чтобы избежать одиночного умножения с плавающей запятой, было бы полезно. Это одна из тех вещей, которые вам просто нужно проверить. Опять же, это действительно незначительно по сравнению с другими изменениями, которые я предложил, которые могли бы иметь большое влияние. - person taleinat; 12.03.2014
comment
Да, исключения быстрее, чем условные, поэтому, если они случаются редко, код, использующий их, тоже будет быстрее. В этом случае условное выражение сработает только в том случае, если на входе ровно 0, что бывает очень редко, поэтому быстрее использовать исключение. В быстром тесте версия исключения примерно на 30% быстрее для случайного ввода, а использование pix = pi*x также ускоряет ее примерно на 40%. - person endolith; 13.03.2014

Я не совсем уверен, что вы пытаетесь сделать, но есть некоторые ускорения, которые вы можете сделать для создания матрицы. Предложение Braincore использовать numpy.sinc — это первый шаг, но второй — осознать, что numpy функции хотят работать с массивами numpy, где они могут выполнять циклы на скорости C и могут делать это быстрее, чем с отдельными элементами.

def resampleMatrix(Tso, Tsf, o, f):
    retval = numpy.sinc((Tsi*numpy.arange(i)[:,numpy.newaxis]
                         -Tso*numpy.arange(j)[numpy.newaxis,:])/Tso)
    return retval

Хитрость заключается в том, что, индексируя диапазоны с помощью numpy.newaxis, numpy преобразует массив с формой i в массив с формой i x 1, а массив с формой j — в форму 1 x j. На этапе вычитания numpy будет «транслировать» каждый вход, чтобы он действовал как массив в форме i x j, и выполнял вычитание. ("Broadcast" - это термин numpy, отражающий тот факт, что не делается никакой дополнительной копии, чтобы растянуть i x 1 до i x j.)

Теперь numpy.sinc может перебирать все элементы в скомпилированном коде намного быстрее, чем любой цикл for, который вы могли бы написать.

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

Единственным недостатком является то, что теперь вы платите за дополнительный массив Nx10*N для хранения разницы. Это может нарушить сделку, если N велико, а память является проблемой.

В противном случае вы сможете написать это, используя numpy.convolve. Из того немногого, что я только что узнал о sinc-интерполяции, я бы сказал, что вам нужно что-то вроде numpy.convolve(orig,numpy.sinc(numpy.arange(j)),mode="same"). Но я, наверное, ошибаюсь в деталях.

person AFoglia    schedule 06.12.2009
comment
Я пытаюсь свернуться, поэтому я думаю, что numpy.convolve может быть правильным направлением. - person Phil; 08.12.2009

Если вас интересует только «создание «гладкого» графика», я бы просто выбрал простую полиномиальную сплайновую кривую:

Для любых двух соседних точек данных коэффициенты полиномиальной функции третьей степени могут быть вычислены из координат этих точек данных и двух дополнительных точек слева и справа от них (без учета граничных точек). Это создаст точки на красивой гладкой кривой с непрерывная первая дивизна. Существует прямая формула для преобразования 4 координат в 4 полиномиальных коэффициента, но я не хочу лишать вас удовольствия от ее поиска ;o).

person Don O'Donnell    schedule 07.12.2009

Вот минимальный пример одномерной интерполяции с помощью scipy — не так весело, как заново изобретать, но.
Сюжет выглядит как sinc, что не случайно: попробуйте сплайн-сплайн гугл передискретизировать "приблизительно sinc".
(Предположительно, менее локальный / больше нажатий, лучшее приближение, но я понятия не имею, насколько локальны UnivariateSplines.)

""" interpolate with scipy.interpolate.UnivariateSpline """
from __future__ import division
import numpy as np
from scipy.interpolate import UnivariateSpline
import pylab as pl

N = 10 
H = 8
x = np.arange(N+1)
xup = np.arange( 0, N, 1/H )
y = np.zeros(N+1);  y[N//2] = 100

interpolator = UnivariateSpline( x, y, k=3, s=0 )  # s=0 interpolates
yup = interpolator( xup )
np.set_printoptions( 1, threshold=100, suppress=True )  # .1f
print "yup:", yup

pl.plot( x, y, "green",  xup, yup, "blue" )
pl.show()

Добавлено в феврале 2010 г.: см. также базовая сплайн-интерполяция в нескольких строках numpy

person denis    schedule 05.12.2009

Небольшое улучшение. Используйте встроенную функцию numpy.sinc(x), которая запускается в скомпилированном коде C.

Возможное большее улучшение: можете ли вы выполнять интерполяцию на лету (по мере построения графика)? Или вы привязаны к библиотеке графиков, которая принимает только матрицу?

person BrainCore    schedule 05.12.2009
comment
Спасибо за комментарий. Как ни странно, код работает примерно в 10 раз медленнее, когда я использовал numpy.sinc(x). Я удивлен! - person Phil; 05.12.2009
comment
Сюжетная часть описания предназначена только для иллюстративных целей. Я не очень беспокоюсь о рисовании сюжета, просто ускоряю вычисления. В конце концов, это будет скорее задача типа «на лету», потому что я буду обрабатывать срезы большого набора данных. Однако в нынешнем виде прохождение того, что я бы назвал наименьшим полезным фрагментом данных, требует больше времени, чем требуется для получения следующего набора данных... - person Phil; 05.12.2009
comment
Tso = Начальный шаг расчета, Tsf = Окончательный шаг расчета. Итак, если я начну с сигнала, дискретизированного на частоте 1 кГц, и я хочу создать 10 интерполированных точек для каждого образца (новая частота дискретизации будет 10 кГц), Tso = 0,001, Tsf = 0,0001. - person Phil; 05.12.2009

Я рекомендую вам проверить ваш алгоритм, так как это нетривиальная задача. В частности, я предлагаю вам ознакомиться со статьей Hu and Pavlidis (1991) «Построение графиков функций с использованием конических сплайнов» (Компьютерная графика и приложения IEEE). Реализация их алгоритма позволяет выполнять адаптивную выборку функции, так что время рендеринга меньше, чем при подходах с регулярными интервалами.

Резюме следует:

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

person Escualo    schedule 07.12.2009
comment
Мой алгоритм исходит из теории выборки. По сути, я пытаюсь воссоздать сигнал из его сэмплов и передискретизировать его на более высокой частоте. Для построения графика я уверен, что мое решение не лучший метод... - person Phil; 08.12.2009
comment
@Phil: Ты должен был сказать это в вопросе - person endolith; 21.09.2011