Tengo datos de una serie temporal. Generando datos
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']
Quiero crear una línea en zig-zag que conecte entre los máximos locales y los mínimos locales, que satisfaga la condición de que en el eje y, |highest - lowest value|
de cada línea en zig-zag debe exceder un porcentaje (digamos 20%) de la distancia de la anterior línea en zig-zag, Y un valor preestablecido k (digamos 1.2)
Puedo encontrar los extremos locales usando este código:
# 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'])
pero no sé cómo aplicarle la condición de umbral. Por favor, avísenme sobre cómo aplicar tal condición.
Como los datos pueden contener millones de marcas de tiempo, se recomienda un cálculo eficiente
Para una descripción más clara:
Ejemplo de salida, de mis datos:
# 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')
Mi salida deseada (algo similar a esto, el zigzag solo conecta los segmentos significativos)
fuente
Puede usar la funcionalidad de balanceo de Pandas para crear los extremos locales. Eso simplifica un poco el código en comparación con su enfoque Scipy.
Funciones para encontrar los extremos:
La función para crear el zigzag, se puede aplicar en el Marco de datos a la vez (sobre cada columna), pero esto introducirá NaN ya que las marcas de tiempo devueltas serán diferentes para cada columna. Puede soltarlos fácilmente más tarde como se muestra en el siguiente ejemplo, o simplemente aplicar la función en una sola columna en su Marco de datos.
Tenga en cuenta que descomenté la prueba contra un umbral
k
, no estoy seguro si entiendo completamente esa parte correctamente. Puede incluirlo si la diferencia absoluta entre el extremo anterior y el actual necesita ser mayor quek
:& (ext_val.diff().abs() > k)
Tampoco estoy seguro de si el zigzag final siempre debe moverse de un máximo original a un mínimo o viceversa. Supuse que debería, de lo contrario, puede eliminar la segunda búsqueda de extrema al final de la función.
Genere algunos datos de muestra:
Aplique la función y extraiga el resultado para la columna 'data1':
Visualiza el resultado:
fuente
(ext_val.diff().abs() > (ext_val.shift(-1).abs() * p))
, según tengo entendido, está comparando la distancia entre dos puntos conp%
el último punto, ¿estoy en lo cierto? Porque quiero comparar cada segmento en zigzag con el segmento anterior y repetir hasta que se cumpla la condición.