Как сделать интерполяцию в питоне

Обновлено: 30.06.2024

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

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

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

Операторы

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

Одна из наиболее распространенных проблем в области компьютерных наук – это поиск в коллекции и определение того, присутствует ли данный объект в коллекции или нет.

Почти каждый язык программирования имеет свою собственную реализацию базового алгоритма поиска, обычно в виде функции, которая возвращает логическое значение True или False, когда элемент обнаружен в заданном наборе элементов.

В Python самый простой способ поиска объекта – использовать операторы членства, названные так, потому что они позволяют нам определить, является ли данный объект членом коллекции.

  • in – возвращает True, если данный элемент является частью структуры.
  • not in – возвращает True, если данный элемент не является частью структуры.

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

В большинстве случаев нам нужна позиция элемента в последовательности, помимо определения того, существует он или нет. Операторы членства не соответствуют этому требованию.

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

Линейный поиск

Линейный поиск (LinearSearch) – один из простейших и самых простых для понимания алгоритмов поиска. Мы можем думать об этом, как о расширенной версии нашей собственной реализации оператора в Python.

Алгоритм состоит из перебора массива и возврата индекса первого вхождения элемента после его обнаружения:

Итак, если мы используем функцию для вычисления:

После выполнения кода:

Это индекс первого появления искомого элемента, учитывая, что индексы Python начинаются с 0.

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

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

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

Бинарный поиск

  • Если mid – это элемент, который мы ищем (в лучшем случае), мы возвращаем его индекс.
  • Если нет, мы определяем, какая сторона mid val с большей вероятностью будет лежать в основе, в зависимости от того, больше или меньше val, чем mid, и отбрасываем другую сторону массива.
  • Затем мы рекурсивно или итеративно выполняем jump, выбирая новое значение для mid, сравнивая его с val и отбрасывая половину возможных совпадений на каждой итерации алгоритма.

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

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

Если мы используем функцию для вычисления:

Это индекс значения, которое мы ищем.

  • Возврат индекса текущего элемента.
  • Поиск в левой половине массива.
  • Поиск по правой половине массива.

Мы можем выбрать только одну возможность на итерацию, и наш пул возможных совпадений делится на два на каждой итерации. Это делает временную сложность двоичного поиска равной O (log n).

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

Запуск этого фрагмента кода приведет к получению индекса среднего элемента:

Для сравнения выполнение линейного поиска в том же массиве вернет:

Это индекс первого элемента. Однако мы не можем категорически сказать, что двоичный поиск не работает, если массив содержит один и тот же элемент дважды – он может работать так же, как линейный поиск и в некоторых случаях возвращать первое вхождение элемента.

Если мы выполним двоичный поиск, например, в массиве [1,2,3,4,4,5] и найдем 4, в результате мы получим 3.

Как перейти к поиску?

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

Для отсортированного массива вместо постепенного поиска по элементам массива мы ищем скачкообразно. Итак, в нашем входном списке lys, если у нас есть размер jump, наш алгоритм будет рассматривать элементы в порядке lys [0], lys [0 + jump], lys [0 + 2jump], lys [0 + 3jump] и т.д.

При каждом переходе мы сохраняем предыдущее значение и его индекс. Когда мы находим набор значений, где lys [i]

Python — отличный язык для анализа данных, в первую очередь благодаря фантастической экосистеме пакетов Python, ориентированных на данные. Pandas является одним из таких пакетов и значительно упрощает импорт и анализ данных.

Функция Pandas dataframe.interpolate() в основном используется для заполнения значений NA в кадре данных или серии. Но это очень мощная функция для заполнения пропущенных значений. Он использует различные методы интерполяции, чтобы заполнить пропущенные значения, а не жестко кодировать значение.

Syntax: DataFrame.interpolate(method=’linear’, axis=0, limit=None, inplace=False, limit_direction=’forward’, limit_area=None, downcast=None, **kwargs)

Parameters :
method :

axis : 0 fill column-by-column and 1 fill row-by-row.
limit : Maximum number of consecutive NaNs to fill. Must be greater than 0.
limit_direction : , default ‘forward’
limit_area : None (default) no fill restriction. inside Only fill NaNs surrounded by valid values (interpolate). outside Only fill NaNs outside valid values (extrapolate). If limit is specified, consecutive NaNs will be filled in this direction.
inplace : Update the NDFrame in place if possible.
downcast : Downcast dtypes if possible.
kwargs : keyword arguments to pass on to the interpolating function.

Returns : Series or DataFrame of same shape interpolated at the NaNs

import pandas as pd

"B" :[ None , 2 , 54 , 3 , None ],

"C" :[ 20 , 16 , None , 3 , 8 ],

"D" :[ 14 , 3 , None , None , 6 ]>)


Давайте интерполируем пропущенные значения, используя линейный метод. Обратите внимание, что линейный метод игнорирует индекс и обрабатывает значения как одинаково расположенные.

df.interpolate(method = 'linear' , limit_direction = 'forward' )


Выход :

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

import pandas as pd

"B" :[ None , 2 , 54 , 3 , None ],

"C" :[ 20 , 16 , None , 3 , 8 ],

"D" :[ 14 , 3 , None , None , 6 ]>)

df.interpolate(method = 'linear' , limit_direction = 'backward' , limit = 1 )


Выход :

Обратите внимание, что в четвертом столбце было заполнено только одно пропущенное значение, так как мы установили ограничение в 1. Пропущенное значение в последней строке не может быть заполнено, так как после этого не существует строки, из которой значение можно интерполировать.

В Python 3.6 добавился новый вариант форматирования строк - f-строки или интерполяция строк. F-строки позволяют не только подставлять какие-то значения в шаблон, но и позволяют выполнять вызовы функций, методов и т.п.

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

Синтаксис¶

F-строки - это литерал строки с буквой f перед ним. Внутри f-строки в паре фигурных скобок указываются имена переменных, которые надо подставить:

Аналогичный результат с format можно получить так: "IP: , mask: ".format(ip=ip, mask=mask) .

Очень важное отличие f-строк от format: f-строки это выражение, которое выполняется, а не просто строка. То есть, в случае с ipython, как только мы написали выражение и нажали Enter, оно выполнилось и вместо выражений и подставились значения переменных.

Поэтому, например, нельзя сначала написать шаблон, а затем определить переменные, которые используются в шаблоне:

Кроме подстановки значений переменных, в фигурных скобках можно писать выражения:

После двоеточия в f-строках можно указывать те же значения, что и при использовании format:

Особенности использования f-строк¶

При использовании f-строк нельзя сначала создать шаблон, а затем его использовать, как при использовании format.

F-строка сразу выполняется и в нее подставляются значения переменных, которые должны быть определены ранее:

Если необходимо подставить другие значения, надо создать новые переменные (с теми же именами) и снова написать f-строку:

Примеры использования f-строк¶

Базовая подстановка переменных:

Ширина столбцов может быть указана через переменную:

Работа со словарями

Вызов функции len внутри f-строки:

Вызов метода upper внутри f-строки:

Конвертация чисел в двоичный формат:

Что использовать format или f-строки¶

Во многих случаях f-строки удобней использовать, так как шаблон выглядит понятней и компактней. Однако бывают случаи, когда метод format удобней. Например:

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

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

этот Q & A предназначен как канонический (- ish) относительно двумерной (и многомерной) интерполяции с использованием scipy. Часто возникают вопросы, касающиеся основного синтаксиса различных многомерных методов интерполяции, я надеюсь, что они тоже будут установлены прямо.

у меня есть набор рассеянных двумерных точек данных, и я хотел бы построить их как красивую поверхность, предпочтительно используя что-то вроде contourf или plot_surface in matplotlib.pyplot . Как я могу интерполировать свои двумерные или многомерные данные в сетку с помощью scipy?

Я нашел scipy.interpolate подпакет, но я продолжаю получать ошибки при использовании interp2d или bisplrep или griddata или rbf . Каков правильный синтаксис этих методов?

1 ответ:

  1. upsampling (входные данные на прямоугольнике сетка, выходные данные находятся на более плотной сетке)
  2. интерполяция рассеянных данных на регулярную сетку
  1. гладкая и дружественная функция: cos(pi*x)*sin(pi*y) ; в диапазоне [-1, 1]
  2. злая (и в частности, не непрерывная) функция: x*y/(x^2+y^2) со значением 0,5 вблизи начала координат; диапазон в [-0.5, 0.5]

для ясности, вот код, с помощью которого я сгенерировал входные данные. Хотя в данном конкретном случае Я, очевидно, знаю о функции, лежащей в основе данных, я буду использовать это только для генерации входных данных для методов интерполяции. Я использую numpy для удобства (и в основном для генерации данных), но одного scipy тоже будет достаточно.

fig2: smooth upsampling

давайте начнем с самой простой задачи. Вот как апсэмплинг из сетки формы [6,7] в одном из [20,21] работает вне для ровного теста функция:

несмотря на то, что это простая задача, уже есть тонкие различия между выходами. На первый взгляд все три выхода разумны. Есть две особенности, которые следует отметить, основываясь на наших предварительных знаниях о базовой функции: средний случай griddata искажает данные больше всего. Обратите внимание на y==-1 граница участка (ближайшая к x label): функция должна быть строго нулевой (так как y==-1 - это Узловая линия для гладкой функции), но это не так для griddata . Также обратите внимание на x==-1 граница участков (сзади, слева): базовая функция имеет локальный максимум (подразумевающий нулевой градиент вблизи границы) при [-1, -0.5] , но griddata выход показывает явно ненулевой градиент в этой области. Эффект неуловимый, но тем не менее это предубеждение. (Верность Rbf еще лучше с выбором по умолчанию радиальных функций, дублированных multiquadratic .)

fig3: evil upsampling

немного сложнее задача выполнить апсэмплинг на нашей злой функции:

четкие различия начинают проявляться среди трех методов. Глядя на поверхностные графики, есть четкие паразитные экстремумы, появляющиеся на выходе из interp2d (обратите внимание на два горба на правой стороне нанесенной поверхности). В то время как griddata и Rbf производят похожие результаты на первый взгляд, последний, кажется, производит более глубокий минимум вблизи [0.4, -0.4] это отсутствует в основной функции.

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

fig4: smooth scattered interpolation

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

выход для гладкой функция:

теперь там уже немного Шоу ужасов происходит. Я обрезал выход из interp2d до [-1, 1] исключительно для построения графиков, чтобы сохранить хотя бы минимальный объем информации. Ясно, что в то время как некоторые из основных форм присутствуют, есть огромные шумные области, где метод полностью ломается. Второй случай griddata воспроизводит форму довольно красиво, но обратите внимание на белый области на границе контурного участка. Это связано с тем, что griddata работает только внутри выпуклой оболочки исходных точек данных (другими словами, он не выполняет каких-либо экстраполяция). Я сохранил значение NaN по умолчанию для выходных точек, лежащих вне выпуклой оболочки. 2 учитывая эти особенности, Rbf кажется, работает лучше всего.

fig5: evil scattered interpolation

и момент, которого мы все ждали ибо:

неудивительно, что interp2d сдается. На самом деле, во время звонка в interp2d вы должны ожидать некоторые дружественные RuntimeWarning жалуются на невозможность построить сплайн. Что касается двух других методов, Rbf кажется, что производит лучший результат, даже вблизи границ домена, где результат экстраполируется.

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


Существует один очень хороший метод линейной интерполяции. Он выполняет линейную интерполяцию, требующую не более одного умножения на выходной образец. Я нашел его описание в третьем издании Understanding DSP от Lyons. В этом методе используется специальный буфер хранения. Учитывая количество отсчетов, которые нужно вставить между любыми двумя входными отсчетами, он создает выходные точки с использованием линейной интерполяции. Здесь я переписал этот алгоритм, используя Python:

где x содержит входные выборки, L - количество точек, которые нужно вставить, y будет содержать выходные выборки.

У меня вопрос как наиболее эффективно реализовать такой алгоритм в ANSI C, например можно ли избежать второго цикла?

ПРИМЕЧАНИЕ: представленный код Python предназначен только для понимания того, как работает этот алгоритм.

ОБНОВЛЕНИЕ: вот пример того, как это работает в Python:

Скажем, x = [. 10, 20, 30 . ]. Затем, если L = 1, он будет производить [. 10, 15, 20, 25, 30 . ]

  • 2 Если вы просто хотите реализовать его на C для повышения производительности, но по-прежнему используете его из Python, я рекомендую Cython.
  • представленный код Python просто для понимания, как работает этот алгоритм
  • 2 Легче понять, как работает алгоритм, если использовать значимые имена переменных.
  • @Lennart: Обновились. Теперь должно быть легко понять
  • 2 Есть ли онлайн-ссылка на алгоритм? Кроме того, отказ от умножения звучит как оптимизация 80-х годов .

а) это неэффективно - два цикла - это нормально, но он выполняет умножение для каждого отдельного элемента вывода; также он использует промежуточные списки ( hold ), генерирует результат с append (мелкое пиво)

б) неправильно интерполирует первый интервал; он генерирует поддельные данные перед первым элементом. Скажем, у нас есть multiplier = 5 и seq = [20,30] - он будет генерировать [0,4,8,12,16,20,22,24,28,30] вместо [20,22,24,26, 28,30].

Итак, вот алгоритм в виде генератора:

Хорошо, а теперь несколько тестов:

А вот мой перевод на C, вписанный в шаблон Fn Краца:

  • о, облом, срок награды истек, и была присуждена только половина. остальные 25пт - уф! - исчез из области stackoverflow.

В этом случае, я думаю, вы можете избежать второго цикла:

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

  • Нет, вы скопировали неправильное предложение. Я уже говорил о том, что делает буфер удержания. Полный алгоритм производит линейную интерполяцию, а не только повторяющиеся элементы. См. Мой обновленный вопрос.
  • Да, количество вычислений таким образом не меньше. Само по себе зацикливание не занимает много времени, поэтому нет необходимости его избегать. Тем не менее, это должно работать. +1 за показ альтернативных (если они бесполезны) решений. ;)

Ну, во-первых, ваш код сломан. L не определен, как и y или x.

Как только это исправлено, я запускаю cython для полученного кода:

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

"например, можно ли избежать второго цикла?"

Если да, то это возможно и в Python. И я не понимаю, как это сделать, хотя и не понимаю, почему вы так делаете. Сначала создание списка i-temp длиной L совершенно бессмысленно. Просто зациклите L раз:

Все это кажется слишком сложным для того, что вы получаете. На самом деле, что вы пытаетесь сделать? Что-нибудь интерполировать? (Да, так сказано в названии. Извините за это.)

Конечно, есть более простые способы интерполяции.

Обновление, значительно упрощенная функция интерполяции:

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

  • @psihodelia: Но i-temp1 не составляет список, поэтому его не будет [10, 20, 10].[i-temp1] всегда будет списком только из одного значения.
  • @Lennart, * L вызовет повторение списка. [1]*2 является [1,1] . Но да, я заметил то же самое, повторяющийся список не очень помогает выразить алгоритм.
  • @Lennart - Я неправильно понял ваш предыдущий комментарий, я не понял, что вы и я указывали на то же самое. Сожалею.
  • Ваша новая упрощенная интерполяция работает намного медленнее! У вас есть операция деления в цикле. Целью представленного алгоритма является только одно умножение на выборку. Количество операций на выборку очень важно при обработке сигналов.
  • @psihodelia - это деление на константу. Компилятор должен это увидеть и превратить в умножение. Если профилирование или проверка сборки показывает, что это не так, просто объявите временный интервал и предварительно вычислите его. В любом случае этот алгоритм дает правильный результат, что кажется более важным.

Думаю, вам понадобятся две петли. Вы должны перешагнуть образцы в x для инициализации интерполятора, не говоря уже о копировании их значений в y , и вам нужно перешагнуть через выходные образцы, чтобы заполнить их значения. Я полагаю, вы могли бы сделать один цикл, чтобы скопировать x в соответствующие места в y , за которым следует еще один цикл для использования всех значений из y , но это все равно потребует некоторой пошаговой логики. Лучше использовать подход вложенного цикла.

(И, как указывает Леннарт Регебро) В качестве примечания, я не понимаю, почему вы делаете hold = [i-temp1] * L . Вместо этого, почему бы не сделать hold = i-temp , а затем цикл for j in xrange(L): а также temp2 += hold ? Это будет использовать меньше памяти, но в остальном будет вести себя точно так же.

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

Вот моя попытка реализации C для вашего алгоритма. Прежде чем пытаться продолжить оптимизацию, id предлагает вам профилировать его производительность со всеми включенными оптимизациями компилятора.

Читайте также: