找到一个数组中的峰值、另一个数组中的谷值,然后以交替方式将它们连接起来的有效方法是什么?

2024-05-29 00:04:12 发布

您现在位置:Python中文网/ 问答频道 /正文

我有两个数组,高点和低点,我想找到高点上的峰,低点上的谷,然后交替连接它们,这样就不会连续出现两个或多个峰/谷,例如:

Highs = [107165., 107095., 107000., 107045., 106980., 106940., 106890.,
         106740., 106565., 106615., 106375., 106440., 106445., 106290.,
         106245., 106270., 106075., 105990., 106260., 106395., 106315.,
         106080., 106180., 106305., 106265., 106180., 106360., 106260.,
         106110., 106070.]

Lows = [106815., 106845., 106905., 106920., 106850., 106850., 106565.,
        106460., 106380., 106305., 106250., 106280., 106155., 106035.,
        106055., 105960., 105915., 105875., 105760., 106110., 105945.,
        105820., 105865., 106095., 106020., 105925., 106045., 106020.,
        105925., 105880.]

应用高斯滤波器平滑数据

from scipy.ndimage.filters import gaussian_filter1d
gauH = gaussian_filter1d(Highs, 1)
gauL = gaussian_filter1d(Lows, 1)

在高处找到高峰,在低处找到低谷

iCeils  = (np.diff(np.sign(np.diff(gauH))) < 0).nonzero()[0] + 1 # Ceils index 
iFloors = (np.diff(np.sign(np.diff(gauL))) > 0).nonzero()[0] + 1 # Floors index

现在我需要对它们进行连接和排序,找出一行中是否有多个峰/谷,并从数组中删除较低/较高的峰/谷。 目前我正在这样做:

将1指定给波峰,将0指定给波谷

iCeils = np.concatenate([np.reshape(iCeils, (len(iCeils), 1)), np.reshape(np.ones(len(iCeils)), 
(len(iCeils), 1))], axis = 1).astype(int)

iFloors = np.concatenate([np.reshape(iFloors, (len(iFloors), 1)), np.reshape(np.zeros(len(iFloors)), 
(len(iFloors), 1))], axis = 1).astype(int)

在数据帧中连接和排序

iCF = pd.DataFrame(np.concatenate([iCeils, iFloors], axis = 0), columns = ['Val', 'kind']).sort_values(['Val'])

从上下文来看,这给了我类似的东西:

iCF = pd.DataFrame([[17,  0],
                    [19,  1],
                    [21,  0],
                    [25,  0],
                    [26,  1]], columns = ['Val', 'kind'])

一行中有两个谷值分别为21和25,低[21]=105820<;低[25]=105925,因此从数据帧中删除25并获得以下结果:

iCF = pd.DataFrame([[17,  0],
                    [19,  1],
                    [21,  0],
                    [26,  1]], columns = ['Val', 'kind'])

现在我可以循环遍历整个数据帧,检查列“kind”是否连续具有相同的值,并删除最高谷值和最低峰值,但我不确定循环是否更有效,有什么建议吗

Edit1,为了更清楚,我想删除蓝色圆圈中的点 Points in blue circles have to be removed from iCF

Edit2,我打错了应该删除的Val


Tags: 数据lennpdiffvalgaussianpdkind
1条回答
网友
1楼 · 发布于 2024-05-29 00:04:12

下面是一个使用scipy.signal后跟列表理解的替代方法

编辑:用整个数据集(25466行)替换测试数据。在我的机器上运行整个代码需要3.67 s±364 ms/循环(7次运行的平均值±标准偏差,每个循环1次),包括所有导入和绘图

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.signal import find_peaks
from collections import Counter

dataset=pd.read_csv('/path/to/WIN205.csv', delimiter = ':', header = 0)

Highs=np.array(dataset.High)

Lows=np.array(dataset.Low)

#find peaks by simple comparison of neighboring value
peaks, _ = find_peaks(Highs)
plt.plot(Highs,color='gray', zorder=1)

#invert Lows to transform
Lows_inv=Lows*(-1)
valleys, _ = find_peaks(Lows_inv)

#plot the results
plt.figure()
plt.subplot(121)
plt.plot(Highs,color='gray', zorder=1)
plt.scatter(peaks, Highs[peaks], color='red', marker='x', s=100, zorder=2)
plt.title('Peaks',fontsize=18)
plt.xlabel('Values (n°)',fontsize=15)
plt.ylabel('Highs (?)',fontsize=15)
plt.subplot(122)
plt.plot(Lows,color='gray', zorder=1)
plt.scatter(valleys, Lows[valleys], color='red', marker='x', s=100, zorder=2)
plt.title('Valleys',fontsize=18)
plt.xlabel('Values (n°)',fontsize=15)
plt.ylabel('Lows (?)',fontsize=15)

#combine peaks and valleys
df_peaks=pd.DataFrame({'category':np.ones(len(peaks)), 'height': Highs[peaks]},index=peaks)
df_valleys=pd.DataFrame({'category':np.zeros(len(valleys)), 'height': Lows[valleys]}, index=valleys)
df_pv=pd.concat([df_peaks,df_valleys], sort=True).sort_index()

#split df by blocks of consecutive same categories 
df_pv['block'] = (df_pv.category.shift(1) != df_pv.category).astype(int).cumsum()

#count number of values in each block
blocks=Counter(df_pv['block'])

def filter_outputs(block_id, nb_vals):
    
    #work on blocks with consecutive same categories
    if nb_vals>1:
        
        df_block=df_pv[df_pv.block==block_id]
        
        #check if peak or valley block and keep max if peak, min if valley
        if df_block.category.iloc[0]==1:
            indxs_to_keep=[np.where(df_pv.height==np.nanmax(df_block.height))[0][0]]
        else:
            indxs_to_keep=[np.where(df_pv.height==np.nanmin(df_block.height))[0][0]]
    
    #keep other blocks unchanged
    else:
        
        indxs_to_keep=[np.where(df_pv.block==block_id)[0]]
            
    return indxs_to_keep

#find all indexes to keep
indxs_to_keep=[filter_outputs(block_id, nb_vals) for block_id, nb_vals in blocks.items()]

#and keep them!
df_pv_filtered=df_pv.reset_index().iloc[[val for sublist in indxs_to_keep for val in sublist]]

相关问题 更多 >

    热门问题