numpy Pandas Dataframe:替换或重新对齐重叠的时间表项

8ehkhllq  于 2023-06-29  发布在  其他
关注(0)|答案(2)|浏览(104)

因此,我正在处理从一个系统进入另一个系统的时间表数据。源系统中的时间表数据有重叠项,而目标系统中没有。业务规则是:“在重叠条目的情况下,中间事件将取代当前事件,直到中间事件完成,然后当前事件将重新开始”。用数据可以更好地解释这一点。
源系统(有重叠):
| 移位|类型|开始|结束| End |
| --|--|--|--| ------------ |
| SHFT-1|睡觉|19/06/2023 10:00PM| 20/06/2023 07:00AM| 20/06/2023 07:00AM |
| ACT-1|干扰|19/06/2023 11:00PM| 2023年6月19日11:30 PM| 19/06/2023 11:30PM |
| ACT-2|干扰|20/06/2023 02:00AM| 20/06/2023 03:00AM| 20/06/2023 03:00AM |
| ACT-3|干扰|2023年6月20日06:30 AM| 20/06/2023 07:00AM| 20/06/2023 07:00AM |
实际上,工人被安排从晚上10点到早上7点睡觉,在该班次期间有3次干扰。当干扰结束时,他们“计时”回到睡眠任务,除了最后一个,因为它是睡眠任务的结束。
目标系统需要如下数据:
| 移位|类型|开始|结束| End |
| --|--|--|--| ------------ |
| SHFT-1|睡觉|19/06/2023 10:00PM| 19/06/2023 11:00PM| 19/06/2023 11:00PM |
| ACT-1|干扰|19/06/2023 11:00PM| 20/06/2023 11:30PM| 20/06/2023 11:30PM |
| SHFT-1|睡觉|2023年6月19日11:30 PM| 20/06/2023 02:00AM| 20/06/2023 02:00AM |
| ACT-2|干扰|20/06/2023 02:00AM| 20/06/2023 03:00AM| 20/06/2023 03:00AM |
| SHFT-1|睡觉|20/06/2023 03:00AM| 2023年6月20日06:30 AM| 20/06/2023 06:30AM |
| ACT-3|干扰|2023年6月20日06:30 AM| 20/06/2023 07:00AM| 20/06/2023 07:00AM |
我有几个关于如何强制执行此逻辑的想法,但我正在寻找比运行以下内容更复杂的东西:while not_finished iterate_all not_finished = changes_occured
有没有一种矢量化的方法可以做到这一点?

3xiyfsfu

3xiyfsfu1#

我想我会张贴“蛮力”的方法,因为也许这可能会刺激别人对我如何能做到这一点,在一个非新手的方式。

import pandas as pd
pd.options.mode.chained_assignment = None

# Sample DataFrame
data = {
    'Employee ID': [1,1,1,1],
    'Shift':['SFT-1','ACT-1','ACT-2','ACT-3'],
    'Type':['Sleep','Disturbance','Disturbance','Disturbance'],
    'Start': ['19/06/2023 10:00PM', '19/06/2023 10:00PM', '20/06/2023 02:00AM', '20/06/2023 06:30AM' ],
    'Finish': ['20/06/2023 07:00AM', '19/06/2023 11:30PM', '20/06/2023 03:00AM', '20/06/2023 07:00AM'],
    'Duration':[540,30,60,30]
}

df = pd.DataFrame(data)

# Convert columns to datetime type
df['Start'] = pd.to_datetime(df['Start'])
df['Finish'] = pd.to_datetime(df['Finish'])

# Sort DataFrame by Start time
df = df.sort_values('Start').reset_index(drop=True)

complete = False
row = 0

while (not complete):

    this_row = df.iloc[row]
    next_row = df.iloc[row + 1]
    
    # If next row starts before this one finishes, then it is overlapping.
    if this_row['Finish'] > next_row['Start']:
             
        # Create new line, set start to next_row finish, finish to this_row finish,
        # calculate duration
        new_row = this_row.copy()
        new_row['Shift'] = new_row['Shift'] + ' O/L'
        new_row['Start'] = next_row['Finish']
        # Finish already set by copy.
        new_row['Duration'] = (new_row['Finish'] - new_row['Start']).seconds / 60.0
        
        # Set df's this_row finish to next_row start, calculate duration.
        df.at[row, 'Finish'] = next_row['Start']
        df.at[row, 'Duration'] = (df.at[row, 'Finish'] - df.at[row, 'Start']).seconds / 60.0
        
        # Insert new_row after next_row
        df = pd.concat([df.iloc[:row+2], new_row.to_frame(1).T, df.iloc[row+2:]]).reset_index(drop=True)

    row += 1
    
    # Check for completeness.    
    complete = (row == df.shape[0] - 1)
    
# Drop all rows with 0 duration.
df = df[df['Duration'] > 0]
pgvzfuti

pgvzfuti2#

我试图找到一个Pandas解决方案,但我没有机会进一步微调它,我将在这里发布它以供参考,请随时修改它或提出一些更改。
注意:我使用了与您在问题中共享的相同的数据框结构和数据(员工,班次,类型,开始,结束)。
样本代码:

st_df = (
    testing
    .assign(
        Start= lambda df_: pd.to_datetime(df_.Start),
        End= lambda df_: pd.to_datetime(df_.End),
        dates= lambda df_: df_.apply(lambda row: pd.date_range(row["Start"], row["End"], freq="30T"), axis=1)
    )
    .explode("dates")
    .drop_duplicates(subset=["dates"], keep="last")
    .sort_values(by="dates")
    .reset_index(drop=True)
    .set_index(["Employee", "Shift", "Type", "Start", "End"])
    .apply(lambda x: x.diff())
    .reset_index()
    .loc[ lambda df_: (df_.Start != df_.shift(-1).Start)]
    .drop(columns=["dates"])
    .reset_index(drop=True)
)
display(st_df)

预期输出:

我创建了一个名为dates的新列,在其中创建了数据范围(每30分钟是一个新行),然后将它们分解。然后,我使用它们来应用差分函数series.diff()来计算每个真实的移位之间的差。
最后,我在最终的df(在series.diff()之后)和它的掩码的一行移位版本之间进行比较,以便删除不需要的多次出现。
例如,如果我从晚上10点到晚上11点睡了一个小时,我将在我的最终 Dataframe 中有两行30分钟的时间范围,一个从10点到10点30分,另一个从10点30分到11点,所以我删除了其中一个。
我希望这对你有帮助!

相关问题