У меня есть данные временных рядов. Генерация данных
date_rng = pd.date_range('2019-01-01', freq='s', periods=400)
df = pd.DataFrame(np.random.lognormal(.005, .5,size=(len(date_rng), 3)),
columns=['data1', 'data2', 'data3'],
index= date_rng)
s = df['data1']
Я хочу создать зигзагообразную линию, соединяющую локальные максимумы и локальные минимумы, которая удовлетворяет условию, что на оси y |highest - lowest value|
каждой зигзагообразной линии должно превышать процент (скажем, 20%) расстояния предыдущего зигзагообразная линия И предварительно установленное значение k (скажем, 1.2)
Я могу найти локальные экстремумы, используя этот код:
# Find peaks(max).
peak_indexes = signal.argrelextrema(s.values, np.greater)
peak_indexes = peak_indexes[0]
# Find valleys(min).
valley_indexes = signal.argrelextrema(s.values, np.less)
valley_indexes = valley_indexes[0]
# Merge peaks and valleys data points using pandas.
df_peaks = pd.DataFrame({'date': s.index[peak_indexes], 'zigzag_y': s[peak_indexes]})
df_valleys = pd.DataFrame({'date': s.index[valley_indexes], 'zigzag_y': s[valley_indexes]})
df_peaks_valleys = pd.concat([df_peaks, df_valleys], axis=0, ignore_index=True, sort=True)
# Sort peak and valley datapoints by date.
df_peaks_valleys = df_peaks_valleys.sort_values(by=['date'])
но я не знаю, как применить к нему пороговое условие. Посоветуйте, пожалуйста, как применять такое условие.
Поскольку данные могут содержать миллионы временных отметок, настоятельно рекомендуется эффективный расчет
Пример вывода, из моих данных:
# Instantiate axes.
(fig, ax) = plt.subplots()
# Plot zigzag trendline.
ax.plot(df_peaks_valleys['date'].values, df_peaks_valleys['zigzag_y'].values,
color='red', label="Zigzag")
# Plot original line.
ax.plot(s.index, s, linestyle='dashed', color='black', label="Org. line", linewidth=1)
# Format time.
ax.xaxis_date()
ax.xaxis.set_major_formatter(mdates.DateFormatter("%Y-%m-%d"))
plt.gcf().autofmt_xdate() # Beautify the x-labels
plt.autoscale(tight=True)
plt.legend(loc='best')
plt.grid(True, linestyle='dashed')
Мой желаемый вывод (что-то похожее на это, зигзаг соединяет только значимые сегменты)
источник
Вы можете использовать функциональность Pandas Rolling для создания локальных экстремумов. Это немного упрощает код по сравнению с вашим подходом Scipy.
Функции поиска экстремумов:
Функция для создания зигзага, она может быть применена к Dataframe сразу (по каждому столбцу), но это представит NaN, поскольку возвращенные метки времени будут отличаться для каждого столбца. Вы можете легко удалить их позже, как показано в примере ниже, или просто применить функцию к одному столбцу в вашем Dataframe.
Обратите внимание, что я раскомментировал тест с порогом
k
, я не уверен, правильно ли понял эту часть. Вы можете включить его, если абсолютная разница между предыдущим и текущим экстремумом должна быть больше чемk
:& (ext_val.diff().abs() > k)
Я также не уверен, должен ли окончательный зигзаг всегда двигаться от исходного максимума к минимуму или наоборот. Я предположил, что это должно, в противном случае вы можете удалить второй поиск экстремальных в конце функции.
Создайте несколько примеров данных:
Примените функцию и извлеките результат для столбца «data1»:
Визуализируйте результат:
источник
(ext_val.diff().abs() > (ext_val.shift(-1).abs() * p))
, как я понимаю, вы сравниваете расстояние между двумя точками сp%
последней точкой, я прав? Потому что я хочу сравнить каждый зигзагообразный сегмент с предыдущим сегментом и повторять, пока условие не будет выполнено.