循环访问数据帧并跟踪特定的序列持续时间



我想弄清楚负值出现的频率以及负价格发生的时间。

示例 DF

d = {'value': [1,2,-3,-4,-5,6,7,8,-9,-10], 'period':[1,2,3,4,5,6,7,8,10]}
df = pd.DataFrame(data=d)

我检查了哪些行具有负值。df['value'] < 0

我想我可以遍历每一行,在出现负值时保留一个计数器,也许将该行移动到另一个 df,因为我想保存开始period和结束period

我目前正在尝试什么

def count_negatives(df):
df_negatives = pd.DataFrame(columns=['start','end', 'counter'])
for index, row in df.iterrows():
counter = 0
df_negative_index = 0
while(row['value'] < 0):
# if its the first one add it to df as start ?
# grab the last one and add it as end
#constantly overwrite the counter?
counter += 1
#add counter to df row
df_negatives['counter'] = counter
return df_negatives

除了我认为这给了我一个无限循环。如果我用if替换while,我就会想出一种方法来跟踪多长时间。

我认为更好的是避免循环:

#compare by < 
a = df['value'].lt(0)
#running sum
b = a.cumsum()
#counter only for negative consecutive values
df['counter'] = b-b.mask(a).ffill().fillna(0).astype(int)
print (df)
value  period  counter
0      1       1        0
1      2       2        0
2     -3       3        1
3     -4       4        2
4     -5       5        3
5      6       6        0
6      7       7        0
7      8       8        0
8     -9       9        1
9    -10      10        2

或者如果不需要重置计数器:

a = df['value'].lt(0)
#repalce values per mask a to 0
df['counter'] = a.cumsum().where(a, 0)
print (df)
value  period  counter
0      1       1        0
1      2       2        0
2     -3       3        1
3     -4       4        2
4     -5       5        3
5      6       6        0
6      7       7        0
7      8       8        0
8     -9       9        4
9    -10      10        5

如果需要开始和结束期间:

#comapre for negative mask
a = df['value'].lt(0)
#inverted mask
b = (~a).cumsum()
#filter only negative rows
c = b[a].reset_index()
#aggregate first and last value per groups
df = (c.groupby('value')['index']
.agg([('start', 'first'),('end', 'last')])
.reset_index(drop=True))
print (df)
start  end
0      2    4
1      8    9

我想保存开始期和结束期。

如果这是您的要求,您可以使用itertools.groupby.另请注意,period系列不是必需的,因为如果未明确提供,Pandas 会提供一个自然整数索引(从 0 开始(。

from itertools import groupby
from operator import itemgetter
d = {'value': [1,2,-3,-4,-5,6,7,8,-9,-10]}
df = pd.DataFrame(data=d)
ranges = []
for k, g in groupby(enumerate(df['value'][df['value'] < 0].index), lambda x: x[0]-x[1]):
group = list(map(itemgetter(1), g))
ranges.append((group[0], group[-1]))
print(ranges)
[(2, 4), (8, 9)]

然后,要转换为数据帧,请执行以下操作:

df = pd.DataFrame(ranges, columns=['start', 'end'])
print(df)
start  end
0      2    4
1      8    9

最新更新