使用Scipy FindPeaks函数进行熊猫滚动应用:TypeError:只能转换大小为1的数组

时间:2018-11-30 15:33:36

标签: python arrays pandas numpy scipy

我正在使用pandas.Series.Rolling.apply功能中的scipy函数Find_Peaks寻求帮助。我抛出TypeError:每次尝试都只能将size-1数组转换为Python标量,我不明白1.)为什么2.)如何正确编写

我的最终目标:从透视日期开始,找到信号中的历史峰值。

find_peaks函数根据峰值属性识别信号内的峰值。我正在使用Mathworks-> prominence methodology

中的突出方法有用的示例

该函数本身采用一维数组,并返回一个元组(peaks:ndarray,properties:dict)。

所需的输出:

x = np.ones((12,))
x[3] = 10
x[7] = 10
x[11] = 10
x = pd.Series(x)
x.rolling(4).apply(lambda x: find_peaks(x,prominence=.2)[0])

0      []
1      []
2      []
3      [3]
4      [3]
5      [3]
6      [3]
7      [3,7]
8      [3,7]
9      [3,7]
10     [3,7]
11     [3,7]
dtype: float64

尝试/错误消息:

x.rolling(4).apply(lambda x: find_peaks(x,prominence=.2)[0])

TypeError:只有大小为1的数组可以转换为Python标量

from SO36680402发生此错误当函数期望单个值但您传递一个数组时,引发错误“仅将length-1数组可以转换为Python标量”。

但是,SO45254174在以下示例中似乎与此TypeError相矛盾:

import numpy as np
import pandas as pd

n = 3
a = np.arange(5)
df = pd.DataFrame(a, columns=['a'])

def keep(window, windows):
    windows.append(window.copy())
    return window[-1]

windows = list()
df['a'].rolling(n).apply(keep, args=(windows,))
df = df.tail(n)
df['a_window'] = windows

它将数组/向量添加到每个滚动块,从而产生:

   a         a_window
2  2  [0.0, 1.0, 2.0]
3  3  [1.0, 2.0, 3.0]
4  4  [2.0, 3.0, 4.0]

第一次尝试:

x.rolling(4).apply(lambda x: find_peaks(x,prominence=.2)[0])

错误:TypeError:只能将大小为1的数组转换为Python标量

第二次尝试:

def _find_peaks(array,prominence=.2):
   peaks,_ = find_peaks(array,prominence=prominence)
   return np.empty((0,0)) if peaks.shape[0]==0 else peaks

x.rolling(4).apply(_find_peaks)

TypeError:只有大小为1的数组可以转换为Python标量

任何有关如何编写以及为什么抛出错误的想法都将不胜感激!

2 个答案:

答案 0 :(得分:1)

您可以做的是改为使用数组,并在find_peaks中使用wlen参数来设置窗口长度,而不要使用pd.rolling

来自documentation

  

wlen:整数或浮点数,可选:样本中的窗口长度,可以选择将每个峰的评估面积限制为x的子集。峰值始终位于窗口的中间,因此给定的长度将四舍五入到下一个奇数整数。此参数可以加快计算速度

因此,您可以执行以下操作:

find_peaks(x.values, prominence=0.2, wlen=4)

(array([3, 7], dtype=int64),
 {'left_bases': array([2, 6], dtype=int64),
  'prominences': array([9., 9.]),
  'right_bases': array([4, 8], dtype=int64)})

答案 1 :(得分:0)

谢谢尼克松的想法。实际上,我真的很喜欢使用“突出显示”选项来定义峰。我找到了两个解决问题的方法,它们的格式与我期望的输出格式不完全相同。

这是我想出的。解决方案1的显示速度更快,但是对我来说,解决方案2的阅读起来更容易。

enter image description here

enter image description here

解决方案1 ​​As_Strided Rolling Window

numpy的as_strided函数可以非常快速地创建滚动窗口。

import numpy as np
import pandas as pd
from numpy.lib import stride_tricks
from scipy.signal import find_peaks

x = np.ones((12,))
x[3] = 10
x[7] = 10
x[11] = 10

frames_example_1 = pd.DataFrame(stride_tricks.as_strided(x,shape=(len(x)-7+1,7),strides=(8,8)))

peaks = frames_example_1.apply(find_peaks,axis=1,prominence=0.2).apply(lambda x: x[0])

aligned_peaks = np.arange(1,7).reshape(6,1) + \ 
pd.DataFrame(peaks.tolist(),index=np.arange(6,12),columns=['first_peak','second_peak'])
aligned_peaks.index.name = 'perspective date'

Output Date

解决方案2 :Numpy Fancy Indexing Fancy Indexing Explained 仍然利用numpy,因为我无法找到一种通过熊猫完成原始代码的方法。

window = 7 
frames, frame_length = len(x) - window +1, window 

indexer = np.tile(np.arange(frame_length),frames).reshape(frames,frame_length) + \
 np.arange(frames).reshape(frames,1)


 peaks = pd.DataFrame(x[indexer],index=np.arange(6,12)).apply(find_peaks,axis=1,prominence=0.2).apply(lambda x: x[0])

然后执行与之前相同的步骤:

aligned_peaks = np.arange(1,7).reshape(6,1) + pd.DataFrame(peaks.tolist(),index=np.arange(6,12),columns=['first_peak','second_peak'])
aligned_peaks.index.name = 'perspective date'

enter image description here