Поиск локальных максимумов графика точек данных xy с помощью numpy?

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

Рассмотрим этот простой пример:

xval = [-0.15, -0.02, 0.1, 0.22, 0.36, 0.43, 0.58, 0.67, 0.79, 0.86, 0.96 ]

yval = [-0.09, 0.13, -0.01, -0.1, -0.05, 0.2, 0.56, 0.47, 0.35, 0.43, 0.69]

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

Желаемый результат — список с индексами пиковых точек, здесь locMaxId =[1,6,10]. Сравнение ближайших соседей - это решение, но для 10 тыс. значений?


person ptaeck    schedule 28.07.2013    source источник


Ответы (1)


Вы можете позволить numpy обрабатывать итерацию, т.е. векторизовать ее:

def local_maxima(xval, yval):
    xval = np.asarray(xval)
    yval = np.asarray(yval)

    sort_idx = np.argsort(xval)
    yval = yval[sort_idx]
    gradient = np.diff(yval)
    maxima = np.diff((gradient > 0).view(np.int8))
    return np.concatenate((([0],) if gradient[0] < 0 else ()) +
                          (np.where(maxima == -1)[0] + 1,) +
                          (([len(yval)-1],) if gradient[-1] > 0 else ()))

EDIT Таким образом, код сначала вычисляет изменение от каждой точки до следующей (gradient). Следующий шаг немного сложен... Если вы сделаете np.diff((gradient > 0), результирующий логический массив будет True, где есть изменение от растущего (> 0) до нерастущего (<= 0). Сделав его целым числом со знаком того же размера, что и логический массив, вы можете различать переходы от растущего к нерастущему (-1) и обратному (+1). Принимая .view(np.int8) целочисленного типа со знаком того же размера dtype, что и логический массив, мы избегаем копирования данных, как это произошло бы, если бы мы сделали менее хакерский .astype(int). Осталось только позаботиться о первой и последней точках и объединить все точки в один массив. Одна вещь, которую я обнаружил сегодня, заключается в том, что если вы включите пустой список в кортеж, который вы отправляете в np.concatenate, он выйдет как пустой массив dtype np.float, и в конечном итоге это будет dtype результата, следовательно, более сложная конкатенация пустые кортежи в приведенном выше коде.

Оно работает:

In [2]: local_maxima(xval, yval)
Out[2]: array([ 1,  6, 10], dtype=int64)

И достаточно быстро:

In [3]: xval = np.random.rand(10000)

In [4]: yval = np.random.rand(10000)

In [5]: local_maxima(xval, yval)
Out[5]: array([   0,    2,    4, ..., 9991, 9995, 9998], dtype=int64)

In [6]: %timeit local_maxima(xval, yval)
1000 loops, best of 3: 1.16 ms per loop

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

person Jaime    schedule 28.07.2013
comment
Да, это именно то, что я искал! Возможно, вы могли бы добавить некоторые комментарии для неопытных пользователей :) - person ptaeck; 28.07.2013
comment
@ptaeck Посмотрите, есть ли в этом смысл, я часто лучше объясняюсь на Python, чем на английском ... - person Jaime; 28.07.2013
comment
Да, сейчас намного лучше! Если хотите, этот вопрос немного сложнее. - person ptaeck; 29.07.2013