熊猫如何以向量化的方式获得满足特定条件的行索引?

时间:2019-07-06 02:13:12

标签: pandas

我有一个时间序列数据帧,其中包含市场价格和订单信息。对于每个条目,都有相应的止损。我想在每个输入订单的数据框中找到止损触发的柱线索引。如果市场价格> =止损,则触发止损,我要记录该止损属于哪个挂单。每个条目均根据其条目栏索引进行记录。例如,在栏1的输入价格为99的订单记录为输入订单1。在栏2的输入价格98为输入订单2,在栏5的输入价格103为输入订单5,等等。

原始数据框如下:

    entry  price  index  entryprice  stoploss  
0       0    100      0         NaN       NaN   
1       1     99      1        99.0     102.0    
2       1     98      2        98.0     101.0    
3       0    100      3         NaN       NaN    
4       0    101      4         NaN       NaN   
5       1    103      5       103.0     106.0   
6       0    105      6         NaN       NaN    
7       0    104      7         NaN       NaN   
8       0    106      8         NaN       NaN   
9       1    103      9       103.0     106.0   
10      0    100     10         NaN       NaN    
11      0    104     11         NaN       NaN    
12      0    108     12         NaN       NaN    
13      0    110     13         NaN       NaN     

代码是:

import pandas as pd

df = pd.DataFrame(
    {'price':[100,99,98,100,101,103,105,104,106,103,100,104,108,110],
     'entry': [0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],})
df['index'] = df.index
df['entryprice'] = df['price'].where(df.entry==1)
df['stoploss'] = df['entryprice'] + 3

为了找出每个订单触发了止损的地方,我以一种适用的方式进行。我定义了一个外部参数 stoplist ,该参数记录了尚未触发的所有止损订单及其对应的进场订单索引。然后,我将df的每一行传递给该函数,并将市场价格与止损单中的止损进行比较,只要满足条件,就将入场订单索引分配给该行并将其从 stoplist 变量。 代码如下:

def Stop(row, stoplist):
    output = None
    for i in range(len(stoplist)-1, -1, -1):
        (ix, stop) = stoplist[i]
        if row['price'] >= stop:
            output = ix
            stoplist.pop(i)

    if row['stoploss'] != None:
        stoplist.append( (row['index'], row['stoploss']) )

    return output

import pandas as pd

df = pd.DataFrame(
    {'price':[100,99,98,100,101,103,105,104,106,103,100,104,108,110],
     'entry': [0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0],})
df['index'] = df.index
df['entryprice'] = df['price'].where(df.entry==1)
df['stoploss'] = df['entryprice'] + 3
stoplist = []
df['stopix'] = df.apply(lambda row: Stop(row, stoplist), axis=1)
print(df)

最终输出是:

    entry  price  index  entryprice  stoploss  stopix
0       0    100      0         NaN       NaN     NaN
1       1     99      1        99.0     102.0     NaN
2       1     98      2        98.0     101.0     NaN
3       0    100      3         NaN       NaN     NaN
4       0    101      4         NaN       NaN     2.0
5       1    103      5       103.0     106.0     1.0
6       0    105      6         NaN       NaN     NaN
7       0    104      7         NaN       NaN     NaN
8       0    106      8         NaN       NaN     5.0
9       1    103      9       103.0     106.0     NaN
10      0    100     10         NaN       NaN     NaN
11      0    104     11         NaN       NaN     NaN
12      0    108     12         NaN       NaN     9.0
13      0    110     13         NaN       NaN     NaN

最后一列stopix是我想要的。但是此解决方案的唯一问题是套用效率不是很高,我想知道是否有 vectorized方式来做到这一点?或者,如果有更好的解决方案来提高性能,将很有帮助。因为效率对我至关重要。

谢谢

1 个答案:

答案 0 :(得分:0)

这是我的看法:

# mark the block starting by entry
blocks = df.stoploss.notna().cumsum()

# mark where the prices are higher than or equal to entry price
higher = df['stoploss'].ffill().le(df.price)

# group higher by entries
g = higher.groupby(blocks)

# where the entry occurs in each group
idx = g.transform('idxmin')

# transform the idx to where the first higher occurs
df['stopix'] = np.where(g.cumsum().eq(1), idx, np.nan)

输出:

    entry  price  index  entryprice  stoploss  stopix
0       0    100      0         NaN       NaN     NaN
1       1     99      1        99.0     102.0     NaN
2       1     98      2        98.0     101.0     NaN
3       0    100      3         NaN       NaN     NaN
4       0    101      4         NaN       NaN     2.0
5       1    103      5       103.0     106.0     NaN
6       0    105      6         NaN       NaN     NaN
7       0    104      7         NaN       NaN     NaN
8       0    106      8         NaN       NaN     5.0
9       1    103      9       103.0     106.0     NaN
10      0    100     10         NaN       NaN     NaN
11      0    104     11         NaN       NaN     NaN
12      0    108     12         NaN       NaN     9.0
13      0    110     13         NaN       NaN     NaN