Pandas在有条件的2列之间滚动最大损失



有一个包含多列的panda数据帧,我想获得每行n观测值中列'high'和后续值列'low'之间的最大差值。


close   high    low     open
0   12.65   13.16   12.63   12.80
1   12.46   12.84   12.28   12.70
2   13.14   13.25   12.63   13.16
3   12.92   13.14   12.79   12.98
4   12.95   13.05   12.69   13.00
5   13.40   13.71   13.03   13.10

让我们举一个例子:假设我们有一个n=3。在这种情况下,df.max_loss[0]将是:

max([df.high[0] - df.low[0],
df.high[0] - df.low[1], <---
df.high[0] - df.low[2],
df.high[1] - df.low[1],
df.high[1] - df.low[2],
df.high[2] - df.low[2]]
0.88

可以计算给定子集的值,但我想将其应用于整个df,为每个观测计算接下来三行的max_loss。

def max_loss(high, low, window=3):
lst = []
for ih in np.arange(0,window):
for il in np.arange(ih,window):
dd = round(high[ih] - low[il], 2)
lst.append(dd)
return max(lst)
max_loss(df.high, df.low, 3)

我可以再做一个for loop,但我觉得一定有更优雅的方式。我尝试过使用rolling().apply(),但没有成功。是否可以将涉及多行和多列的函数应用于滚动窗口?知道吗?

可能不是最快的解决方案,但您可以定义一个自定义函数来交叉合并索引-high的索引是leq,而不是low:

def greater_cartesian(df, n):
idx_frame = df.index[:n].to_frame()
product = idx_frame.merge(idx_frame, how='cross')
product.columns = ['high', 'low']
# filter out the indexes where high has greater index
product = product[product['high'] <= product['low']]
return product

然后在实际数据帧上应用highlow上的新索引,得到最大值:

greater_cartesian(df, 3).
apply(lambda row: df.loc[row.high, 'high'] - df.loc[row.low, 'low'], axis=1)

它给出

0    0.53
1    0.88
2    0.53
4    0.56
5    0.21
8    0.62

当然,您可以在apply之后使用max获得最大值。

一个非精细化的解决方案是"即兴"创建一个滚动窗口,以子集形式设置初始df。

def max_loss(df, n):
#initialize a max_loss column
df['max_loss'] = np.NaN
#roll through df with an improvised n-sized window
for i in np.arange(0, len(df)-n+1):
df_roll=df[i:i+n].copy()
lst=[]
#compute each loss in the roll (considering ih<=il) & append to the list
for ih in np.arange(0,n):
for il in np.arange(ih,n):
dd = round(df_roll.loc[df_roll.index[ih], 'high'] - df_roll.loc[df_roll.index[il], 'low'], 2)
lst.append(dd)

#get the max   
df.loc[df.index[i], 'max_loss'] = max(lst)
return df
df = max_loss(df, n=3)

所需输出。我去掉了不必要的栏以提高可见性。

high    low     max_loss
0   13.16   12.63   0.88
1   12.84   12.28   0.62
2   13.25   12.63   0.62
3   13.14   12.79   0.68
4   13.05   12.69   NaN
5   13.71   13.03   NaN

然而,通过3 for循环不是很有效,尤其是如果我们想将其扩展到多个大型数据帧。

好的,所以我找到了另一种方法,快了约30倍(尽管门槛很低(

首先,我们生成元组(ih, il)的列表,其中ih是high列索引,il是low列索引,考虑到ih<il<n

然后,我们遍历元组,并根据元组移动highlow列。我们将获得一系列计算两个移位列之间差异的列。然后,我们简单地获取伪"丢失"数据帧上各列的最大值。

def max_loss2(df, n):
#this is optional, just to avoid warnings when n is to big
from warnings import simplefilter
simplefilter(action="ignore", category=pd.errors.PerformanceWarning)
tuples = [(ih, il) for ih in np.arange(0,n) for il in np.arange(ih,n)]
loss = pd.DataFrame(index = df.index)
for ih, il in tuples:
loss[f'{ih}_{il}'] = round(df.high.shift(-ih) - df.low.shift(-il), 2)
df[f'max_loss{n}'] = loss.max(axis=1)
return df

考虑约400行的df:

%time max_loss2(df, n=5)
CPU times: user 21 ms, sys: 54 µs, total: 21.1 ms
Wall time: 19.7 ms

与之前的";循环通过":

%time df = max_loss(df, n=5)
CPU times: user 672 ms, sys: 14 ms, total: 686 ms
Wall time: 672 ms

有没有更快的想法?:(

最新更新