Pandas 如何标记开始(1)和多个结束(2 或 3)之间的行?

Fli*_*int 4 python loops signals dataframe pandas

我有以下数据框:

import numpy as np
import pandas as pd

df = pd.DataFrame([])
df['Date'] = ['2020-01-01','2020-01-02','2020-01-03','2020-01-04','2020-01-05',
              '2020-01-06','2020-01-07','2020-01-08','2020-01-09','2020-01-10',
              '2020-01-11','2020-01-12','2020-01-13','2020-01-14','2020-01-15',
              '2020-01-16','2020-01-17','2020-01-18','2020-01-19','2020-01-20']
df['Machine'] = ['A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','A']
df['Signal'] = [0,1,2,0,1,3,0,0,0,3,0,1,0,0,3,0,1,0,0,1]
df['Status'] =  0
Run Code Online (Sandbox Code Playgroud)

下面的函数为机器 A 生成“状态”列。在信号列中,1 打开机器(状态列 1),该列保持为 1,直到机器收到 2 或 3(这是切换机器状态的信号)至 0(关闭),直到机器再次收到信号 1。

我已经使用以下函数解决了维持先前状态行值为 1 或 0 的问题:

def s_gen(dataset, Signal):
    _status = 0
    status0 = []
    for (i) in Signal:
        if _status == 0:
            if i == 1:
                _status = 1 
        elif _status == 1:
            if (i == 2 or i==3):
                _status = 0
        status0.append(_status)
        
    dataset['status0'] = status0

    return dataset['status0']

df['Status'] = s_gen(df,df['Signal'])
df.drop('status0',axis=1,inplace = True)
df
Run Code Online (Sandbox Code Playgroud)

这会将新创建的列附加到数据框。然而,我有一个更大的数据框,机器列中有许多不同的值(分组为系列;A、A、A、B、B、B 等),并且函数的结果不能重叠。使用 groupby 不起作用。因此,我认为下一步是将每个“状态”序列生成为单独的列表,并将它们连接起来,然后将整个系列附加到更大的数据帧作为更大的外循环的一部分。

这是期望的结果:

df = pd.DataFrame([])
df['Date'] = ['2020-01-01','2020-01-02','2020-01-03','2020-01-04','2020-01-05',
              '2020-01-06','2020-01-07','2020-01-08','2020-01-09','2020-01-10',
              '2020-01-11','2020-01-12','2020-01-13','2020-01-14','2020-01-15',
              '2020-01-16','2020-01-17','2020-01-18','2020-01-19','2020-01-20',
              '2020-01-01','2020-01-02','2020-01-03','2020-01-04','2020-01-05',
              '2020-01-06','2020-01-07','2020-01-08','2020-01-09','2020-01-10',
              '2020-01-11','2020-01-12','2020-01-13','2020-01-14','2020-01-15',
              '2020-01-16','2020-01-17','2020-01-18','2020-01-19','2020-01-20']

df['Machine'] = ['A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','A','A',
                'B','B','B','B','B','B','B','B','B','B','B','B','B','B','B','B','B','B','B','B',]
df['Signal'] = [0,1,2,0,1,3,0,0,0,3,0,1,0,0,3,0,1,0,0,1,0,1,2,0,1,3,0,0,0,3,0,1,0,0,3,0,1,0,0,1]
df['Status'] = [0,1,0,0,1,0,0,0,0,0,0,1,1,1,0,0,1,1,1,1,0,1,0,0,1,0,0,0,0,0,0,1,1,1,0,0,1,1,1,1]
df
Run Code Online (Sandbox Code Playgroud)

我正在努力解决的是,如果该函数单独处理每台机器的数据然后将其附加到数据帧,则它必须循环遍历每台机器,然后连接生成的所有状态系列,然后将较大的系列附加到数据帧。

这是我到目前为止所尝试过的:

dfList = df[df['Machine']]
dfListU = pd.DataFrame([])
dfListU = dfList['Machine'].unique()
dfListU.flatten()

def s_gen2(item, dataset, Signal):
   
    data = df[df.Machine==m]
    for m in dfListU:
        _status = 0
        status0 = []

        for (i) in Signal:
            if _status == 0:
                if i == 1:
                    _status = 1 
            elif _status == 1:
                if (i == 2 or i==3):
                    _status = 0
            #status0.append(_status)

        dataset['status0'] = status0

        return dataset['status0']
    for i in dfListU:
        df1 = pd.concat(i)
    status0.append(_status)
df['Status'] = s_gen(df,df['Signal'])
df.drop('status0',axis=1,inplace = True)
df
Run Code Online (Sandbox Code Playgroud)

这会导致错误 - KeyError: "None of [Index(['A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A' , 'A', 'A', 'A', 'A', 'A',\n 'A', 'A', 'A', 'A', 'A', 'A', 'B' , 'B', 'B', 'B', 'B', 'B', 'B', 'B',\n 'B', 'B', 'B', 'B', 'B' , 'B', 'B', 'B', 'B', 'B', 'B', 'B'],\n dtype='object')] 在 [列] 中”

通过 dfListU(唯一机器列表)循环该函数然后连接结果是否更好?我试图避免使用循环,但找不到任何其他方法来将先前的状态行与信号列中的同一行进行比较。

真诚感谢任何帮助。

moz*_*way 7

一个简单的方法是先获取map已知状态,然后再获取groupby.ffill它们:

df['Status'] = (df['Signal']
 .map({1:1, 2:0, 3:0})
 .groupby(df['Machine']).ffill()
 .fillna(0, downcast='infer')
 )
Run Code Online (Sandbox Code Playgroud)

输出:

          Date Machine  Signal  Status
0   2020-01-01       A       0       0
1   2020-01-02       A       1       1
2   2020-01-03       A       2       0
3   2020-01-04       A       0       0
4   2020-01-05       A       1       1
5   2020-01-06       A       3       0
6   2020-01-07       A       0       0
7   2020-01-08       A       0       0
8   2020-01-09       A       0       0
9   2020-01-10       A       3       0
10  2020-01-11       A       0       0
11  2020-01-12       A       1       1
12  2020-01-13       A       0       1
13  2020-01-14       A       0       1
14  2020-01-15       A       3       0
15  2020-01-16       A       0       0
16  2020-01-17       A       1       1
17  2020-01-18       A       0       1
18  2020-01-19       A       0       1
19  2020-01-20       A       1       1
20  2020-01-01       B       0       0
21  2020-01-02       B       1       1
22  2020-01-03       B       2       0
23  2020-01-04       B       0       0
24  2020-01-05       B       1       1
25  2020-01-06       B       3       0
26  2020-01-07       B       0       0
27  2020-01-08       B       0       0
28  2020-01-09       B       0       0
29  2020-01-10       B       3       0
30  2020-01-11       B       0       0
31  2020-01-12       B       1       1
32  2020-01-13       B       0       1
33  2020-01-14       B       0       1
34  2020-01-15       B       3       0
35  2020-01-16       B       0       0
36  2020-01-17       B       1       1
37  2020-01-18       B       0       1
38  2020-01-19       B       0       1
39  2020-01-20       B       1       1
Run Code Online (Sandbox Code Playgroud)


Shu*_*rma 6

有一个更好的办法。使用 cumsum 函数标记以 2/3 条件开头的不同行/块集。然后屏蔽不为 1 的信号值,然后按机器和块对屏蔽列进行分组,并向前填充值。

m1 = df['Signal'].ne(1)
m2 = df['Signal'].isin([2, 3])

df['Status'] = df['Signal'].mask(m1).groupby([df['Machine'], m2.cumsum()]).ffill().fillna(0)
Run Code Online (Sandbox Code Playgroud)
          Date Machine  Signal  Status
0   2020-01-01       A       0     0.0
1   2020-01-02       A       1     1.0
2   2020-01-03       A       2     0.0
3   2020-01-04       A       0     0.0
4   2020-01-05       A       1     1.0
5   2020-01-06       A       3     0.0
6   2020-01-07       A       0     0.0
7   2020-01-08       A       0     0.0
8   2020-01-09       A       0     0.0
9   2020-01-10       A       3     0.0
10  2020-01-11       A       0     0.0
11  2020-01-12       A       1     1.0
12  2020-01-13       A       0     1.0
13  2020-01-14       A       0     1.0
14  2020-01-15       A       3     0.0
15  2020-01-16       A       0     0.0
16  2020-01-17       A       1     1.0
17  2020-01-18       A       0     1.0
18  2020-01-19       A       0     1.0
19  2020-01-20       A       1     1.0
Run Code Online (Sandbox Code Playgroud)