python pandas 试图减少对循环的依赖
python pandas trying to reduce reliance on loops
这是一个关于矢量化的一般问题,但我会用一个例子来帮助提问。我有一个数据框 df
和 df[col_1]
bool (True/False)。
在 df[col_2]
中,我想 return 另一个 True/False 基于第 1 列 df[col_1][i-6:i-1]
的前五行是否包含 df[col_1][i]
的匹配项。
这是我现在使用的循环,但它只是众多循环之一,所以我认为随着数据变大,它们一定会放慢速度:
for i in df.index:
if i < 6:
df[col_2][i] = 0.
else:
df[col_2][i] = df[col_1][i] not in tuple(df[col_1].ix[i-6:i-1,col_1)
...输出如下所示:
. col_1 col_2
0 TRUE
1 TRUE
2 TRUE
3 TRUE
4 FALSE
5 FALSE FALSE
6 FALSE FALSE
7 FALSE FALSE
8 FALSE FALSE
9 TRUE TRUE
10 FALSE FALSE
11 FALSE FALSE
12 FALSE FALSE
13 FALSE FALSE
14 TRUE FALSE
15 TRUE FALSE
16 TRUE FALSE
17 TRUE FALSE
18 TRUE FALSE
19 TRUE FALSE
20 FALSE TRUE
我如何在 pandas 中使用矢量化执行此操作 - 也许使用 shift()
或偏移函数?
这是一个简单的矢量化解决方案,应该非常快,尽管可能有更优雅的编写方式。如果愿意,您可以忽略前 5 行或将它们覆盖为 NaN。
df = pd.DataFrame({ 'col_1':[True,True,True,True,False,False,False,False,
False,True,False,False,False,False,True,True,
True,True,True,True,False] })
df['col_2'] = ((df!=df.shift(1)) & (df!=df.shift(2)) & (df!=df.shift(3)) &
(df!=df.shift(4)) & (df!=df.shift(5)))
如果速度真的很重要,您可以执行以下操作。它比上面的速度快 3 倍以上,并且可能与您在此处执行的操作一样高效。这只是利用 rolling_sum()
将布尔值解释为 0/1 的事实,您只需要知道总和是 0 还是 5。
df['rollsum'] = pd.rolling_sum(df.col_1,6) - df.col_1
df['col_3'] = ( ((df.col_1==True ) & (df.rollsum==0))
| ((df.col_1==False) & (df.rollsum==5)) )
col_1 col_2 rollsum col_3
0 True True NaN False
1 True False NaN False
2 True False NaN False
3 True False NaN False
4 False True NaN False
5 False False 4 False
6 False False 3 False
7 False False 2 False
8 False False 1 False
9 True True 0 True
10 False False 1 False
11 False False 1 False
12 False False 1 False
13 False False 1 False
14 True False 1 False
15 True False 1 False
16 True False 2 False
17 True False 3 False
18 True False 4 False
19 True False 5 False
20 False True 5 True
这是一个关于矢量化的一般问题,但我会用一个例子来帮助提问。我有一个数据框 df
和 df[col_1]
bool (True/False)。
在 df[col_2]
中,我想 return 另一个 True/False 基于第 1 列 df[col_1][i-6:i-1]
的前五行是否包含 df[col_1][i]
的匹配项。
这是我现在使用的循环,但它只是众多循环之一,所以我认为随着数据变大,它们一定会放慢速度:
for i in df.index:
if i < 6:
df[col_2][i] = 0.
else:
df[col_2][i] = df[col_1][i] not in tuple(df[col_1].ix[i-6:i-1,col_1)
...输出如下所示:
. col_1 col_2
0 TRUE
1 TRUE
2 TRUE
3 TRUE
4 FALSE
5 FALSE FALSE
6 FALSE FALSE
7 FALSE FALSE
8 FALSE FALSE
9 TRUE TRUE
10 FALSE FALSE
11 FALSE FALSE
12 FALSE FALSE
13 FALSE FALSE
14 TRUE FALSE
15 TRUE FALSE
16 TRUE FALSE
17 TRUE FALSE
18 TRUE FALSE
19 TRUE FALSE
20 FALSE TRUE
我如何在 pandas 中使用矢量化执行此操作 - 也许使用 shift()
或偏移函数?
这是一个简单的矢量化解决方案,应该非常快,尽管可能有更优雅的编写方式。如果愿意,您可以忽略前 5 行或将它们覆盖为 NaN。
df = pd.DataFrame({ 'col_1':[True,True,True,True,False,False,False,False,
False,True,False,False,False,False,True,True,
True,True,True,True,False] })
df['col_2'] = ((df!=df.shift(1)) & (df!=df.shift(2)) & (df!=df.shift(3)) &
(df!=df.shift(4)) & (df!=df.shift(5)))
如果速度真的很重要,您可以执行以下操作。它比上面的速度快 3 倍以上,并且可能与您在此处执行的操作一样高效。这只是利用 rolling_sum()
将布尔值解释为 0/1 的事实,您只需要知道总和是 0 还是 5。
df['rollsum'] = pd.rolling_sum(df.col_1,6) - df.col_1
df['col_3'] = ( ((df.col_1==True ) & (df.rollsum==0))
| ((df.col_1==False) & (df.rollsum==5)) )
col_1 col_2 rollsum col_3
0 True True NaN False
1 True False NaN False
2 True False NaN False
3 True False NaN False
4 False True NaN False
5 False False 4 False
6 False False 3 False
7 False False 2 False
8 False False 1 False
9 True True 0 True
10 False False 1 False
11 False False 1 False
12 False False 1 False
13 False False 1 False
14 True False 1 False
15 True False 1 False
16 True False 2 False
17 True False 3 False
18 True False 4 False
19 True False 5 False
20 False True 5 True