Eu tenho dados de séries temporais. Gerando dados
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']
Quero criar uma linha em zigue-zague conectando-se entre o máximo local e o mínimo local, que satisfaça a condição de que, no eixo y, |highest - lowest value|
de cada linha em zigue-zague deve exceder uma porcentagem (digamos 20%) da distância da anterior linha zig-zag, E um valor pré-estabelecido k (diga 1.2)
Eu posso encontrar os extremos locais 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'])
mas não sei como aplicar a condição de limite a ela. Por favor, informe-me sobre como aplicar essa condição.
Como os dados podem conter milhões de registros de data e hora, é altamente recomendável um cálculo eficiente
Para uma descrição mais clara:
Exemplo de saída, dos meus dados:
# 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')
Minha saída desejada (algo semelhante a isso, o zigue-zague apenas conecta os segmentos significativos)
fonte
Você pode usar a funcionalidade de rolagem do Pandas para criar os extremos locais. Isso simplifica um pouco o código comparado à sua abordagem Scipy.
Funções para encontrar os extremos:
A função para criar o ziguezague, pode ser aplicada no Dataframe de uma só vez (em cada coluna), mas isso apresentará os NaNs, pois os carimbos de data e hora retornados serão diferentes para cada coluna. Você pode descartá-las facilmente mais tarde, como mostrado no exemplo abaixo, ou simplesmente aplicar a função em uma única coluna no Dataframe.
Observe que descomentei o teste em um limite
k
, não tenho certeza se entendi completamente essa parte corretamente. Você pode incluí-lo se a diferença absoluta entre o extremo anterior e o atual precisar ser maior quek
:& (ext_val.diff().abs() > k)
Também não tenho certeza se o zigue-zague final sempre deve passar de uma alta original para uma baixa ou vice-versa. Eu presumi que deveria, caso contrário, você pode remover a segunda pesquisa por extrema no final da função.
Gere alguns dados de amostra:
Aplique a função e extraia o resultado para a coluna 'data1':
Visualize o resultado:
fonte
(ext_val.diff().abs() > (ext_val.shift(-1).abs() * p))
, como eu entendo, você está comparando a distância entre dois pontos ep%
o último ponto, estou certo? Porque eu quero comparar cada segmento em zigue-zague com o segmento anterior e repita até que a condição seja satisfeita.