Python 在数据帧中,根据特定条件更快地计算滚动发生次数

Python 在数据帧中,根据特定条件更快地计算滚动发生次数,python,pandas,performance,dataframe,Python,Pandas,Performance,Dataframe,我有这样一个熊猫数据框: name number A 0.8466 A 0.8463 A 0.8482 A 0.8455 A 0.8423 A 0.8405 A 0.842 A 0.8453 A 0.8419 A 0.8394 A 0.8376 A 0.8368 A 0.8388 A 0.8392 A 0.8409 A 0.8415 A 0.8424 A 0.8425 A 0.8433 A 0.8412 A 0.

我有这样一个熊猫数据框:

name number
A   0.8466
A   0.8463
A   0.8482
A   0.8455
A   0.8423
A   0.8405
A   0.842
A   0.8453
A   0.8419
A   0.8394
A   0.8376
A   0.8368
A   0.8388
A   0.8392
A   0.8409
A   0.8415
A   0.8424
A   0.8425
A   0.8433
A   0.8412
A   0.8397
我想计算在3个
number
值的滑动窗口中满足
上限
下限
条件的次数。我有一个工作代码:

df['Upper'] = 0
df['Lower'] = 0
for i in range(len(df)):
    if i < 1:
        df['Upper'].iloc[i] = 0
        df['Lower'].iloc[i] = 0
    else:
        chip_sum_r = 0
        chip_sum_s = 0
        for j in range(3):
            if df['name'].iloc[i-j] == df['name'].iloc[i]:
                if df['number'].iloc[i-j] <= df['number'].iloc[i]*1.003 and df['number'].iloc[i-j] >   df['number'].iloc[i]:
                    chip_sum_r += 1
                if df['number'].iloc[i-j] >= df['number'].iloc[i]*0.997 and df['number'].iloc[i-j] < df['number'].iloc[i]:
                   chip_sum_s += 1
        df['Upper'].iloc[i] = chip_sum_r
        df['Lower'].iloc[i] = chip_sum_s

但是,对于大量数据点来说,这是非常缓慢的。有没有办法加快速度,我不确定矢量化方法在这里是否有效?

您可以通过对大小为3的滚动窗口应用自定义函数来实现矢量化

即使你的函数没有矢量化,如果你使用Numba引擎(需要安装一个包),你可能会看到比原始python有显著的性能提升


编辑:如果该应用程序与正常pd或np应用程序类似,则它可能没有矢量化。相反,可以使用.shift()生成一组中间列,并以这种方式构建计算-然后将每个中间计算矢量化。

当然我们可以通过
numpy
广播来完成

n=3
s=df.number.values
s=s[:,None]/s
df['Lower']=np.sum(np.tril(np.triu(np.tril((s<=1.003) & (s>1)), -n+1)), 1)
df['Upper']=np.sum(np.tril(np.triu(np.tril((s<1) & (s>=0.997)), -n+1)), 1)
df
Out[52]: 
   name  number  Lower  Upper
0     A  0.8466      0      0
1     A  0.8463      0      1
2     A  0.8482      2      0
3     A  0.8455      0      1
4     A  0.8423      0      0
5     A  0.8405      0      1
6     A  0.8420      1      1
7     A  0.8453      0      0
8     A  0.8419      0      1
9     A  0.8394      0      1
10    A  0.8376      0      1
11    A  0.8368      0      1
12    A  0.8388      2      0
13    A  0.8392      2      0
14    A  0.8409      2      0
15    A  0.8415      2      0
16    A  0.8424      2      0
17    A  0.8425      2      0
18    A  0.8433      2      0
19    A  0.8412      0      2
20    A  0.8397      0      1
n=3
s=df.number.values
s=s[:,无]/s
df['Lower']=np.sum(np.tril(np.triu(np.tril((s1)),-n+1)),1)
df['Upper']=np.sum(np.tril(np.triu)(np.tril((s=0.997)),-n+1)),1)
df
出[52]:
姓名号码上下
0 A 0.8466 0 0
1 A 0.8463 0 1
2 A 0.8482 2 0
3 A 0.8455 0 1
4 A 0.8423 0 0
5 A 0.8405 0 1
6 A 0.8420 1 1
7 A 0.8453 0 0
8 A 0.8419 0 1
9 A 0.8394 0 1
10 A 0.8376 0 1
11 A 0.8368 0 1
12 A 0.8388 2 0
13 A 0.8392 2 0
14 A 0.8409 2 0
15 A 0.8415 2 0
16 A 0.8424 2 0
17 A 0.8425 2 0
18 A 0.8433 2 0
19 A 0.8412 0 2
20 A 0.8397 0 1

尽管我不确定在测试(10毫秒)后,这是否会大大提高性能,但您可以使用
.shift()
作为循环行和
np.where()
的替代方法,以满足您的条件。在下面的解决方案中,您总共只需要循环3次,但我测试了Yorben的循环,每次循环0.3毫秒,速度快了30倍

df['Upper'] = 0
df['Lower'] = 0
for j in range(3):
    df['Upper'] = np.where((df['name'] == df.shift(j)['name'])
                                & (df.shift(j)['number'] <= df['number']*1.003)
                                & (df.shift(j)['number'] >  df['number']),
                                df['Upper'] + 1, df['Upper'])
    df['Lower'] = np.where((df['name'] == df.shift(j)['name'])
                                & (df.shift(j)['number'] >= df['number']*0.997)
                                & (df.shift(j)['number'] <  df['number']),
                                df['Lower'] + 1, df['Lower'])

此解决方案比我的解决方案快30倍左右。不过我真的不太明白+1的性能。哇,这是超快速的,如此快速的编码,令人印象深刻。然而,这并不是我所需要的——检查结果df。此外,没有检查滚动范围内的
名称
是否与@YOBEN_S相同
df['Upper'] = 0
df['Lower'] = 0
for j in range(3):
    df['Upper'] = np.where((df['name'] == df.shift(j)['name'])
                                & (df.shift(j)['number'] <= df['number']*1.003)
                                & (df.shift(j)['number'] >  df['number']),
                                df['Upper'] + 1, df['Upper'])
    df['Lower'] = np.where((df['name'] == df.shift(j)['name'])
                                & (df.shift(j)['number'] >= df['number']*0.997)
                                & (df.shift(j)['number'] <  df['number']),
                                df['Lower'] + 1, df['Lower'])
  name  number  Upper   Lower
0   A   0.8466  0   0
1   A   0.8463  1   0
2   A   0.8482  0   2
3   A   0.8455  1   0
4   A   0.8423  0   0
5   A   0.8405  1   0
6   A   0.8420  1   1
7   A   0.8453  0   0
8   A   0.8419  1   0
9   A   0.8394  1   0
10  A   0.8376  1   0
11  A   0.8368  1   0
12  A   0.8388  0   2
13  A   0.8392  0   2
14  A   0.8409  0   2
15  A   0.8415  0   2
16  A   0.8424  0   2
17  A   0.8425  0   2
18  A   0.8433  0   2
19  A   0.8412  2   0
20  A   0.8397  1   0