看过来
《pandas 教程》 持续更新中,提供建议、纠错、催更等加作者微信: gairuo123(备注:pandas教程)和关注公众号「盖若」ID: gairuo。跟作者学习,请进入 Python学习课程。欢迎关注作者出版的书籍:《深入浅出Pandas》 和 《Python之光》。
今天的需求有点复杂,需要我们涉及到对指定两个状态作为一个周期,并计算出周期内的差值,写到周期结束所在的行上。pandas 非常适合实现此类有着较为复杂逻辑的问题。
以我们构造的数据是一份精密机器运行状态的日志文件样本:
import pandas as pd
df=pd.DataFrame(columns=['Status','t'])
df['Status']=['A','T', 'A','C','C','A','T','T','C','A']
df['t']= pd.to_datetime(['2021-06-12 08:39:24.813000',
'2021-06-12 08:39:24.820000',
'2021-06-12 08:39:25.210000',
'2021-06-12 08:39:25.217000',
'2021-06-12 08:44:28.830000',
'2021-06-12 10:48:10.293000',
'2021-06-12 10:48:10.300000',
'2021-06-12 10:48:10.680000',
'2021-06-12 10:48:10.693000',
'2021-06-12 10:48:11.223000'])
df
'''
Status t
0 A 2021-06-12 08:39:24.813
1 T 2021-06-12 08:39:24.820
2 A 2021-06-12 08:39:25.210
3 C 2021-06-12 08:39:25.217
4 C 2021-06-12 08:44:28.830
5 A 2021-06-12 10:48:10.293
6 T 2021-06-12 10:48:10.300
7 T 2021-06-12 10:48:10.680
8 C 2021-06-12 10:48:10.693
9 A 2021-06-12 10:48:11.223
'''
其中,状态列我们关注的是状态 T 和 C,T 表示机器触发开始运行,C 表示出现故障。需求期望增加 Delta 列来计算显示 T(触发)和 C(结束)状态之间的时间差,并将这个时间差显示在 C 对应的列上。
需要注意的是,状态 C 之后不经过 T 还可能出现状态 C,我们视 T 之后到第一个 C 之间为一个正常运行周期。
这个问题的难点是状态的不规律性,如何才能准确找出所有 T 和 C 的周期。
我们可以先将状态 T 和 C 筛选出来,然后剔除连续的 T 或者 C 中的无效的内容,最终形成一个规范的 T-C 列表。
在规范的列表中求位差,最终保留 C 位置的值并增加 Delta 列。
先筛选出状态为 T 和 C 的数据:
df.loc[df.Status.isin(['T', 'C'])]
'''
Status t
1 T 2021-06-12 08:39:24.820
3 C 2021-06-12 08:39:25.217
4 C 2021-06-12 08:44:28.830
6 T 2021-06-12 10:48:10.300
7 T 2021-06-12 10:48:10.680
8 C 2021-06-12 10:48:10.693
'''
增加两个辅助列,是不当前行的当是 T 下行是 C,是否当前行是 C 上行是 T:
(
df.loc[df.Status.isin(['T', 'C'])]
.assign(nxt_C=lambda x: (x.Status == 'T') | (x.Status.shift() == 'C'))
.assign(pre_T=lambda x: (x.Status == 'C') | (x.Status.shift(-1) == 'T'))
)
'''
Status t nxt_C pre_T
1 T 2021-06-12 08:39:24.820 True False
3 C 2021-06-12 08:39:25.217 False True
4 C 2021-06-12 08:44:28.830 True True
6 T 2021-06-12 10:48:10.300 True True
7 T 2021-06-12 10:48:10.680 True False
8 C 2021-06-12 10:48:10.693 False True
'''
我们筛除两个辅助列分别为 True 之外的数据:
(
df.loc[df.Status.isin(['T', 'C'])]
.assign(nxt_C=lambda x: (x.Status == 'T') | (x.Status.shift() == 'C'))
.assign(pre_T=lambda x: (x.Status == 'C') | (x.Status.shift(-1) == 'T'))
.loc[lambda x: ~((x.nxt_C==True) & (x.pre_T==True))]
)
'''
Status t nxt_C pre_T
1 T 2021-06-12 08:39:24.820 True False
3 C 2021-06-12 08:39:25.217 False True
7 T 2021-06-12 10:48:10.680 True False
8 C 2021-06-12 10:48:10.693 False True
'''
最后求时间列的位差:
temp = (
df.loc[df.Status.isin(['T', 'C'])]
.assign(nxt_C=lambda x: (x.Status == 'T') | (x.Status.shift() == 'C'))
.assign(pre_T=lambda x: (x.Status == 'C') | (x.Status.shift(-1) == 'T'))
.loc[lambda x: ~((x.nxt_C== True) & (x.pre_T==True))]
.assign(Delta=lambda x: x.t - x.t.shift())
)
temp
'''
Status t nxt_C pre_T Delta
1 T 2021-06-12 08:39:24.820 True False NaT
3 C 2021-06-12 08:39:25.217 False True 0 days 00:00:00.397000
7 T 2021-06-12 10:48:10.680 True False 0 days 02:08:45.463000
8 C 2021-06-12 10:48:10.693 False True 0 days 00:00:00.013000
'''
这样就得到的数据状态 C 对应的 Delta 值就是我们要的数据。
接下来,将这些数据写入原 df,我们采用了迭代的方式:
for index, row in temp.query('Status=="C"').iterrows():
df.loc[index, 'Delta'] = row.Delta
df
'''
Status t Delta
0 A 2021-06-12 08:39:24.813 NaT
1 T 2021-06-12 08:39:24.820 NaT
2 A 2021-06-12 08:39:25.210 NaT
3 C 2021-06-12 08:39:25.217 0 days 00:00:00.397000
4 C 2021-06-12 08:44:28.830 NaT
5 A 2021-06-12 10:48:10.293 NaT
6 T 2021-06-12 10:48:10.300 NaT
7 T 2021-06-12 10:48:10.680 NaT
8 C 2021-06-12 10:48:10.693 0 days 00:00:00.013000
9 A 2021-06-12 10:48:11.223 NaT
'''
这样就得到了最终的需求。
(完)
更新时间:Aug. 18, 2024, 4 p.m. 标签:pandas python 周期 时长