Нахождение локальных максимумов /минимумов с помощью Numpy в одномерном массиве Numpy

Можете ли вы предложить функцию модуля из numpy /scipy, которая может находить локальные максимумы /минимумы в одномерном массиве numpy? Очевидно, что самый простой подход - взглянуть на ближайших соседей, но я бы хотел, чтобы у нас было приемлемое решение, являющееся частью большого дистрибутива.

90 голосов | спросил Navi 7 Jpm1000000pmFri, 07 Jan 2011 14:22:09 +030011 2011, 14:22:09

9 ответов


0

Если вы ищете все записи в массиве 1d a меньше, чем их соседи, вы можете попробовать

numpy.r_[True, a[1:] < a[:-1]] & numpy.r_[a[:-1] < a[1:], True]

Вы также можете сгладить свой массив перед этим шагом, используя numpy.convolve()

Я не думаю, что есть специальная функция для этого.

ответил Sven Marnach 7 Jpm1000000pmFri, 07 Jan 2011 14:41:35 +030011 2011, 14:41:35
0

В SciPy> = 0,11

import numpy as np
from scipy.signal import argrelextrema

x = np.random.random(12)

# for local maxima
argrelextrema(x, np.greater)

# for local minima
argrelextrema(x, np.less)

Производит

>>> x
array([ 0.56660112,  0.76309473,  0.69597908,  0.38260156,  0.24346445,
    0.56021785,  0.24109326,  0.41884061,  0.35461957,  0.54398472,
    0.59572658,  0.92377974])
>>> argrelextrema(x, np.greater)
(array([1, 5, 7]),)
>>> argrelextrema(x, np.less)
(array([4, 6, 8]),)

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

>>> x[argrelextrema(x, np.greater)[0]]

scipy.signal также предоставляет argrelmax и argrelmin для поиска максимумов и минимумов соответственно.

ответил danodonovan 21 32012vEurope/Moscow11bEurope/MoscowWed, 21 Nov 2012 15:03:50 +0400 2012, 15:03:50
0

Для кривых с не слишком большим шумом я рекомендую следующий небольшой фрагмент кода:

from numpy import *

# example data with some peaks:
x = linspace(0,4,1e3)
data = .2*sin(10*x)+ exp(-abs(2-x)**2)

# that's the line, you need:
a = diff(sign(diff(data))).nonzero()[0] + 1 # local min+max
b = (diff(sign(diff(data))) > 0).nonzero()[0] + 1 # local min
c = (diff(sign(diff(data))) < 0).nonzero()[0] + 1 # local max


# graphical output...
from pylab import *
plot(x,data)
plot(x[b], data[b], "o", label="min")
plot(x[c], data[c], "o", label="max")
legend()
show()

+1 важен, потому что diff уменьшает исходный номер индекса.

ответил R. C. 12 MarpmMon, 12 Mar 2012 16:35:51 +04002012-03-12T16:35:51+04:0004 2012, 16:35:51
0

Другой подход (больше слов, меньше кода), который может помочь:

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

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

Поскольку сглаживание - это, в простейшем смысле, фильтр нижних частот, сглаживание часто лучше (ну, проще всего) выполнить с помощью ядра свертки, а "формирование" этого ядра может обеспечить удивительное количество функций, сохраняющих /Расширение возможностей. Процесс поиска оптимального ядра может быть автоматизирован с использованием различных средств, но лучшим может быть простой метод грубой силы (достаточно быстрый для поиска небольших ядер). Хорошее ядро ​​(как и предполагалось) будет сильно искажать исходные данные, но это НЕ повлияет на расположение интересующих пиков /долин.

К счастью, довольно часто подходящее ядро ​​может быть создано с помощью простого SWAG («обоснованное предположение»). Ширина сглаживающего ядра должна быть немного шире, чем самый ожидаемый «интересный» пик в исходных данных, и его форма будет напоминать этот пик (одномасштабный вейвлет). Для ядер со средним сохранением (каким должен быть любой хороший сглаживающий фильтр) сумма элементов ядра должна быть точно равна 1,00, а ядро ​​должно быть симметричным относительно его центра (то есть иметь нечетное количество элементов.

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

Определение «правильной» (оптимальной) степени сглаживания (усиления ядра свертки) можно даже автоматизировать: сравните стандартное отклонение первых производных данных со стандартным отклонением сглаженных данных. То, как соотношение двух стандартных отклонений изменяется с изменением степени сглаживания кулачка, можно использовать для прогнозирования эффективных значений сглаживания. Несколько ручных прогонов данных (которые действительно являются репрезентативными) должны быть всем, что нужно.

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

Наконец, плохие новости: поиск «настоящих» пиков становится настоящей болью, когда шум также имеет функции, похожие на реальные пики (перекрывающиеся полосы пропускания). Следующее более сложное решение, как правило, заключается в использовании более длинного сверточного ядра («более широкой апертуры ядра»), которое учитывает взаимосвязь между соседними «реальными» пиками (например, минимальные или максимальные скорости для появления пиков), или использования нескольких свертка проходит с использованием ядер, имеющих разную ширину (но только если она быстрее: фундаментальная математическая истина заключается в том, что линейные свертки, выполняемые последовательно, всегда могут быть сведены вместе в одну свертку). Но зачастую гораздо проще сначала найти последовательность полезных ядер (различной ширины) и собрать их вместе, чем непосредственно найти конечное ядро ​​за один шаг.

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

ответил BobC 7 42013vEurope/Moscow11bEurope/MoscowThu, 07 Nov 2013 03:49:55 +0400 2013, 03:49:55
0

Почему бы не использовать встроенную функцию Scipy? noreferrer "> signal.find_peaks_cwt , чтобы выполнить работу?

from scipy import signal
import numpy as np

#generate junk data (numpy 1D arr)
xs = np.arange(0, np.pi, 0.05)
data = np.sin(xs)

# maxima : use builtin function to find (max) peaks
max_peakind = signal.find_peaks_cwt(data, np.arange(1,10))

# inverse  (in order to find minima)
inv_data = 1/data
# minima : use builtin function fo find (min) peaks (use inversed data)
min_peakind = signal.find_peaks_cwt(inv_data, np.arange(1,10))

#show results
print "maxima",  data[max_peakind]
print "minima",  data[min_peakind]

результаты:

maxima [ 0.9995736]
minima [ 0.09146464]

Привет

ответил A STEFANI 23 32016vEurope/Moscow11bEurope/MoscowWed, 23 Nov 2016 16:24:50 +0300 2016, 16:24:50
0

Update: Я не был доволен градиентом, поэтому я нашел более надежным использовать numpy.diff. Пожалуйста, дайте мне знать, если он делает то, что вы хотите.

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

import numpy as np
from matplotlib import pyplot

a=np.array([10.3,2,0.9,4,5,6,7,34,2,5,25,3,-26,-20,-29],dtype=np.float)

gradients=np.diff(a)
print gradients


maxima_num=0
minima_num=0
max_locations=[]
min_locations=[]
count=0
for i in gradients[:-1]:
        count+=1

    if ((cmp(i,0)>0) & (cmp(gradients[count],0)<0) & (i != gradients[count])):
        maxima_num+=1
        max_locations.append(count)     

    if ((cmp(i,0)<0) & (cmp(gradients[count],0)>0) & (i != gradients[count])):
        minima_num+=1
        min_locations.append(count)


turning_points = {'maxima_number':maxima_num,'minima_number':minima_num,'maxima_locations':max_locations,'minima_locations':min_locations}  

print turning_points

pyplot.plot(a)
pyplot.show()
ответил Mike Vella 27 Jpm1000000pmThu, 27 Jan 2011 16:20:04 +030011 2011, 16:20:04
0

Ни одно из этих решений не помогло мне, так как я также хотел найти пики в центре повторяющихся значений. например, в

ar = np.array([0,1,2,2,2,1,3,3,3,2,5,0])

ответ должен быть

array([ 3,  7, 10], dtype=int64)

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

def findLocalMaxima(ar):
# find local maxima of array, including centers of repeating elements    
maxInd = np.zeros_like(ar)
peakVar = -np.inf
i = -1
while i < len(ar)-1:
#for i in range(len(ar)):
    i += 1
    if peakVar < ar[i]:
        peakVar = ar[i]
        for j in range(i,len(ar)):
            if peakVar < ar[j]:
                break
            elif peakVar == ar[j]:
                continue
            elif peakVar > ar[j]:
                peakInd = i + np.floor(abs(i-j)/2)
                maxInd[peakInd.astype(int)] = 1
                i = j
                break
    peakVar = ar[i]
maxInd = np.where(maxInd)[0]
return maxInd 
ответил Misha Smirnov 9 J0000006Europe/Moscow 2017, 21:15:00
0
import numpy as np
x=np.array([6,3,5,2,1,4,9,7,8])
y=np.array([2,1,3,5,3,9,8,10,7])
sortId=np.argsort(x)
x=x[sortId]
y=y[sortId]
minm = np.array([])
maxm = np.array([])
i = 0
while i < length-1:
    if i < length - 1:
        while i < length-1 and y[i+1] >= y[i]:
            i+=1

        if i != 0 and i < length-1:
            maxm = np.append(maxm,i)

        i+=1

    if i < length - 1:
        while i < length-1 and y[i+1] <= y[i]:
            i+=1

        if i < length-1:
            minm = np.append(minm,i)
        i+=1


print minm
print maxm

minm и maxm содержат индексы минимумов и максимумов соответственно. Для огромного набора данных он даст много максимумов /минимумов, поэтому в этом случае сначала сгладьте кривую, а затем примените этот алгоритм.

ответил prtkp 29 J0000006Europe/Moscow 2015, 10:02:48
0

Начиная с версии SciPy 1.1, вы также можете использовать find_peaks . Ниже приведены два примера из самой документации.

Используя аргумент height, можно выбрать все максимумы выше определенного порога (в этом примере все неотрицательные максимумы; это может быть очень полезным, если нужно иметь дело с шумным базовым уровнем):

import matplotlib.pyplot as plt
from scipy.misc import electrocardiogram
from scipy.signal import find_peaks
import numpy as np

x = electrocardiogram()[2000:4000]
peaks, _ = find_peaks(x, height=0)
plt.plot(x)
plt.plot(peaks, x[peaks], "x")
plt.plot(np.zeros_like(x), "--", color="gray")
plt.show()

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

Еще один чрезвычайно полезный аргумент - это distance, который определяет минимальное расстояние между двумя пиками:

peaks, _ = find_peaks(x, distance=150)
# difference between peaks is >= 150
print(np.diff(peaks))
# prints [186 180 177 171 177 169 167 164 158 162 172]

plt.plot(x)
plt.plot(peaks, x[peaks], "x")
plt.show()

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

ответил Cleb 22 42018vEurope/Moscow11bEurope/MoscowThu, 22 Nov 2018 00:07:07 +0300 2018, 00:07:07

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132