在Pandas中查找事件发生后的日期时间

a64a0gku  于 2023-01-01  发布在  其他
关注(0)|答案(2)|浏览(126)

我有一个Pandas格式的数据框,它反映了员工的轮班情况(他们实际工作的时间),下面是它的一个片段:

df = pd.DataFrame({'Worker' : ['Alice','Alice','Alice', 'Bob','Bob','Bob'],
                          'Shift_start' : ['2022-01-01 10:00:00', '2022-01-01 13:10:00', '2022-01-01 15:45:00', '2022-01-01 11:30:00', '2022-01-01 13:40:00', '2022-01-01 15:20:00'],
                          'Shift_end' : ['2022-01-01 12:30:00', '2022-01-01 15:30:00', '2022-01-01 17:30:00', '2022-01-01 13:30:00', '2022-01-01 15:10:00', '2022-01-01 18:10:00']})

| 工人|移位_开始|班次_结束|
| - ------| - ------| - ------|
| 爱丽丝|2022年1月1日10时00分|2022年1月1日12时30分|
| 爱丽丝|2022年1月1日13时10分|2022年1月1日15时30分|
| 爱丽丝|2022年1月1日15时45分|2022年1月1日17时30分|
| 鲍勃|2022年1月1日11时30分|2022年1月1日13时30分|
| 鲍勃|2022年1月1日13时40分|2022年1月1日15时10分|
| 鲍勃|2022年1月1日15时20分|2022年1月1日18时10分|
现在,我需要在每一行中计算自上次部分中断以来的时间,定义为暂停〉20分钟,并根据每个班次的开始时间计算。即,如果暂停15分钟,则应认为暂停不存在,并计算自上次〉20分钟暂停以来的时间。如果不存在暂停,这个时间应该是从一天开始算起的时间。所以我需要这样的东西:
| 工人|移位_开始|班次_结束|休息后小时数|
| - ------| - ------| - ------| - ------|
| 爱丽丝|2022年1月1日10时00分|2022年1月1日12时30分|无|
| 爱丽丝|2022年1月1日13时10分|2022年1月1日15时30分|无|
| 爱丽丝|2022年1月1日15时45分|2022年1月1日17时30分|二点五八|
| 鲍勃|2022年1月1日11时30分|2022年1月1日13时30分|无|
| 鲍勃|2022年1月1日13时40分|2022年1月1日15时10分|二、十七|
| 鲍勃|2022年1月1日15时20分|2022年1月1日18时10分|三点八三|
对于Alice,第一行为0,因为没有先前的休息时间,所以将其作为自一天开始以来的值。由于这是她的第一个班次,因此结果为0小时。在第二行中,她刚刚暂停了40分钟,因此自休息以来再次为0小时。在第三行中,她刚刚休息了15分钟,但由于最小休息时间为20分钟,就好像她没有休息过一样,因此,自从她最后一次休息以来的时间是从她最后一次休息结束的13:10:00开始的,所以结果是2小时35分钟,即2.58小时。
在Bob的情况下,同样的逻辑也适用。第一行是0(是一天的第一班)。在第二排,他只休息了10分钟,这不算,所以从最后一次休息开始的时间应该是从他一天开始的时间,即2h10m在第三排,他又休息了10分钟,所以时间又是从一天开始的,所以3h50m(3.83小时)。
为了计算20分钟限制的休息时间,我做了以下操作:

shifted_end = df.groupby("Worker")["Shift_end"].shift()
df["Partial_break"] = (df["Shift_start"] - shifted_end)
df['Partial_break_hours'] = df["Partial_break"].dt.total_seconds() / 3600
df.loc[(df['Partial_break_hours']<0.33), 'Partial_break_hours'] = 0

但是我想不出一种方法来实现搜索逻辑以给出所需的输出。任何帮助都是非常感谢的!

ie3xauqp

ie3xauqp1#

您可以尝试(假设DataFrame已排序):

def fn(x):
    rv = []
    last_zero = 0

    for a, c in zip(
        x["Shift_start"],
        (x["Shift_start"] - x["Shift_end"].shift()) < "20 minutes",
    ):
        if c:
            rv.append(round((a - last_zero) / pd.to_timedelta(1, unit="hour"), 2))
        else:
            last_zero = a
            rv.append(0)

    return pd.Series(rv, index=x.index)

df["Hours_since_break"] = df.groupby("Worker").apply(fn).droplevel(0)
print(df)

图纸:

Worker         Shift_start           Shift_end  Hours_since_break
0  Alice 2022-01-01 10:00:00 2022-01-01 12:30:00               0.00
1  Alice 2022-01-01 13:10:00 2022-01-01 15:30:00               0.00
2  Alice 2022-01-01 15:45:00 2022-01-01 17:30:00               2.58
3    Bob 2022-01-01 11:30:00 2022-01-01 13:30:00               0.00
4    Bob 2022-01-01 13:40:00 2022-01-01 15:10:00               2.17
5    Bob 2022-01-01 15:20:00 2022-01-01 18:10:00               3.83
8tntrjer

8tntrjer2#

你可以计算一个“fullBreakAtStart”标志,并在此基础上设置一个“lastShiftStart”,如果没有“fullBreakAtStart”,则只需输入一个np.nan,然后使用fillna(method="ffill")函数,代码如下:

df["Shift_end_prev"] = df.groupby("Worker")["Shift_end"].shift(1)

df["timeDiff"] = pd.to_datetime(df["Shift_start"]) - pd.to_datetime(df["Shift_end_prev"])
df["fullBreakAtStart"] = (df["timeDiff"]> "20 minutes") | (df["timeDiff"].isna())

df["lastShiftStart"] = np.where(df["fullBreakAtStart"], df["Shift_start"], np.nan)
df["lastShiftStart"] = df["lastShiftStart"].fillna(method="ffill")

df["Hours_since_break"] = pd.to_datetime(df["Shift_start"]) - pd.to_datetime(df["lastShiftStart"])
df["Hours_since_break"] = df["Hours_since_break"]/np.timedelta64(1, 'h')
df["Hours_since_break"] = np.where(df["fullBreakAtStart"],0,df["Hours_since_break"])

相关问题