Mam dane z timeseries. Generowanie danych
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']
Chcę utworzyć linię zygzakowatą łączącą lokalne maksima i lokalne minima, które spełniają warunek, że na osi y |highest - lowest value|
każdej linii zygzakowatej musi przekraczać procent (powiedzmy 20%) odległości poprzedniej linia zygzakowata ORAZ z góry określona wartość k (powiedzmy 1.2)
Mogę znaleźć lokalną ekstrema za pomocą tego kodu:
# 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'])
ale nie wiem, jak zastosować do tego warunek progowy. Proszę mi doradzić, jak zastosować taki warunek.
Ponieważ dane mogą zawierać miliony znaczników czasu, zaleca się wydajne obliczenia
Przykładowe dane wyjściowe z moich danych:
# 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')
Moje pożądane wyjście (coś podobnego do tego, zygzak łączy tylko znaczące segmenty)
źródło
Możesz użyć funkcji Pandas rolling, aby stworzyć lokalną ekstrema. To trochę upraszcza kod w porównaniu do twojego podejścia Scipy.
Funkcje znajdowania ekstremów:
Funkcja tworzenia zygzaka, można ją zastosować jednocześnie do ramki danych (nad każdą kolumną), ale wprowadzi to NaN, ponieważ zwracane znaczniki czasu będą różne dla każdej kolumny. Możesz łatwo upuścić je później, jak pokazano w poniższym przykładzie, lub po prostu zastosować funkcję w jednej kolumnie w ramce danych.
Zauważ, że odkomentowałem test względem progu
k
, nie jestem pewien, czy w pełni rozumiem tę część poprawnie. Możesz to uwzględnić, jeśli absolutna różnica między poprzednim a obecnym ekstremum musi być większa niżk
:& (ext_val.diff().abs() > k)
Nie jestem również pewien, czy ostateczny zygzak powinien zawsze przejść z oryginalnego wysokiego do niskiego lub odwrotnie. Założyłem, że tak, w przeciwnym razie możesz usunąć drugie wyszukiwanie ekstremów na końcu funkcji.
Wygeneruj przykładowe dane:
Zastosuj funkcję i wyodrębnij wynik dla kolumny „data1”:
Wizualizuj wynik:
źródło
(ext_val.diff().abs() > (ext_val.shift(-1).abs() * p))
, jak rozumiem, porównujesz odległość między dwoma punktami ap%
ostatnim punktem, prawda? Ponieważ chcę porównać każdy zygzakowaty segment z poprzednim segmentem i powtarzać, aż warunek zostanie spełniony.