根据Pandas中另一列中满足条件的出现次数按顺序创建列

vngu2lb8  于 2023-03-16  发布在  其他
关注(0)|答案(2)|浏览(103)

我有一个类似于以下内容的数据集:

import pandas as pd

date0 = ["2020-01-31"] * 3
date1 = ["2020-02-28"] * 3
date2 = ["2020-03-31"] * 3

date = date0 + date1 + date2
acc = ["A", "B", "C"] * 3
val = [1, 0, 0, 1, 1, 0, 1, 1, 1]

df = pd.DataFrame({"date": date, "account": acc, "value": val})

目标是创建一个名为“output”的新列,该列基于“account”对给定时间序列中二进制列“value”的出现次数进行计数。如果列“val”在一行中显示三个(或N个)1,则输出列为1,否则为0。
对于上述数据集,这将产生:

output = [0, 0, 0, 0, 0, 0, 1, 0, 0]
df["output"] = output

此外,如果时间序列中的第4次迭代是0,那么序列重新开始。当然,如果第4次迭代是1,那么输出是1。
下面的数据集显示,帐户“A”在前3个月就满足了标准,然后帐户恢复,再次开始循环。帐户“B”在第2-4个月满足标准,依此类推。这是大型模拟的一部分,因此矢量化解决方案将是理想的。

date3 = ["2020-04-30"] * 3
val = [0, 1, 1]
acc = ["A", "B", "C"]
output = [0, 1, 0]

df2 = pd.DataFrame({"date": date3, "account": acc, "value": val, "output": output})

df3 = pd.concat([df,  df2])
bq9c1y66

bq9c1y661#

假设每个帐户每月有一个值,并且没有缺失日期,则可以使用groupby_rolling

df['output'] = (df.groupby('account').rolling(3)['value']
                  .sum().eq(3).astype(int).droplevel(0))
print(df)

# Output
         date account  value  output
0  2020-01-31       A      1       0
1  2020-01-31       B      0       0
2  2020-01-31       C      0       0
3  2020-02-28       A      1       0
4  2020-02-28       B      1       0
5  2020-02-28       C      0       0
6  2020-03-31       A      1       1
7  2020-03-31       B      1       0
8  2020-03-31       C      1       0

对于第二个示例:

df3 = pd.concat([df,  df2], ignore_index=True)  # <- ignore_index=True
df3['output2'] = (df3.groupby('account').rolling(3)['value']
                     .sum().eq(3).astype(int).droplevel(0))
print(df3)

# Output
          date account  value  output  output2
0   2020-01-31       A      1       0        0
1   2020-01-31       B      0       0        0
2   2020-01-31       C      0       0        0
3   2020-02-28       A      1       0        0
4   2020-02-28       B      1       0        0
5   2020-02-28       C      0       0        0
6   2020-03-31       A      1       1        1
7   2020-03-31       B      1       0        0
8   2020-03-31       C      1       0        0
9   2020-04-30       A      0       0        0
10  2020-04-30       B      1       1        1
11  2020-04-30       C      1       0        0
hmae6n7t

hmae6n7t2#

我用了一个更大的数据集进行测试,它运行得很快,效果很好。
我保留了我创建的中间列,以便您看到正在发生的事情。

import time
import pandas as pd
import numpy as np
from numpy.lib.stride_tricks import sliding_window_view as swv

date0 = ["2020-01-31"] * 6
date1 = ["2020-02-28"] * 6
date2 = ["2020-03-31"] * 6

date = date0 + date1 + date2
acc = ["A", "B", "C"] * 6
val = [1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1]

df = pd.DataFrame({"date": date, "account": acc, "value": val})

start = time.time()

w=[]
N = df['account'].nunique() # N=3 in your example
for i in range(N):
    S = df["value"][i::N]
    w.append(swv(S, N).sum(axis=1))
df['w'] = np.pad(np.array(w).flatten('F'), (N*2, 0), mode='constant')
# [0 0 0 0 0 0 3 2 1 3 2 1 3 2 1 3 2 1]

df['mod'] = (df.groupby('account')
               .cumsum(numeric_only=True)
               .mod(N))["value"]

df["output"] = df['w'] + df['mod']
df["output"] = (df["output"].where((df['w'] + df['mod']).eq(df['w']) & df['w'].eq(N), 0)
                            .astype(bool)
                            .astype(int))

end = time.time()
print("time_speed : ", end - start)
print("df : ", df)

结果

time_speed :  0.002397298812866211
df :  
    date       account  value  w  mod  output
0   2020-01-31       A      1  0    1       0
1   2020-01-31       B      0  0    0       0
2   2020-01-31       C      0  0    0       0
3   2020-01-31       A      1  0    2       0
4   2020-01-31       B      1  0    1       0
5   2020-01-31       C      0  0    0       0
6   2020-02-28       A      1  3    0       1
7   2020-02-28       B      1  2    2       0
8   2020-02-28       C      1  1    1       0
9   2020-02-28       A      1  3    1       0
10  2020-02-28       B      0  2    2       0
11  2020-02-28       C      0  1    1       0
12  2020-03-31       A      1  3    2       0
13  2020-03-31       B      1  2    0       0
14  2020-03-31       C      0  1    1       0
15  2020-03-31       A      1  3    0       1
16  2020-03-31       B      1  2    1       0
17  2020-03-31       C      1  1    2       0

说明

  • w列:3-在示例中,从第6行开始使用numpy进行窗口化(不需要之前的窗口,因为您需要3个连续的1)。当窗口和为3时,您有3个连续的1
  • 色谱柱modcumsum模值(引入循环性)

对于cumsum模值,0%33%3mod列中有0
当您添加wmod列时,您实际上过滤掉了包含0%3的行,例如将w列中的0添加到mod列中的0,给予0
另一方面,将w列中的3mod列中的0相加,得到3(示例中为N值)

相关问题