J'ai des données de série temporelle. Génération de données
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']
Je veux créer une ligne en zig-zag reliant entre les maxima locaux et les minima locaux, qui satisfait la condition selon laquelle sur l'axe des y, |highest - lowest value|
chaque ligne en zig-zag doit dépasser un pourcentage (disons 20%) de la distance de la précédente ligne en zig-zag ET une valeur prédéterminée k (disons 1.2)
Je peux trouver les extrema locaux en utilisant ce code:
# 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'])
mais je ne sais pas comment lui appliquer la condition de seuil. Veuillez me conseiller sur la façon d'appliquer une telle condition.
Étant donné que les données peuvent contenir des millions d'horodatages, un calcul efficace est fortement recommandé
Pour une description plus claire:
Exemple de sortie, à partir de mes données:
# 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')
Ma sortie souhaitée (quelque chose de similaire à cela, le zigzag ne connecte que les segments significatifs)
la source
Vous pouvez utiliser la fonctionnalité de roulement Pandas pour créer les extrema locaux. Cela simplifie un peu le code par rapport à votre approche Scipy.
Fonctions pour trouver les extrema:
La fonction pour créer le zigzag, elle peut être appliquée sur le Dataframe à la fois (sur chaque colonne), mais cela introduira des NaN puisque les horodatages retournés seront différents pour chaque colonne. Vous pouvez facilement les supprimer plus tard, comme indiqué dans l'exemple ci-dessous, ou simplement appliquer la fonction sur une seule colonne de votre Dataframe.
Notez que j'ai décommenté le test par rapport à un seuil
k
, je ne suis pas sûr de bien comprendre cette partie correctement. Vous pouvez l'inclure si la différence absolue entre l'extrême précédent et l'extrême actuel doit être supérieure àk
:& (ext_val.diff().abs() > k)
Je ne sais pas non plus si le zigzag final devrait toujours passer d'un niveau haut original à un niveau bas ou vice versa. Je l'ai supposé, sinon vous pouvez supprimer la deuxième recherche d'extrême à la fin de la fonction.
Générez quelques exemples de données:
Appliquez la fonction et extrayez le résultat pour la colonne 'data1':
Visualisez le résultat:
la source
(ext_val.diff().abs() > (ext_val.shift(-1).abs() * p))
, si je comprends bien, vous comparez la distance entre deux points avecp%
le dernier point, ai-je raison? Parce que je veux comparer chaque segment en zigzag avec le segment précédent et répéter jusqu'à ce que la condition soit satisfaite.