【问题标题】:Group by column value and within 3 days of each other按列值分组并在 3 天内分组
【发布时间】:2022-12-09 01:40:26
【问题描述】:

我有一个非常复杂的问题,找不到我可以应用的答案。在包含工作、任务和日期(字符串、字符串、日期)的数据框中,我想根据工作进行分组,但只有当日期彼此相隔 3 天以内时:

Work Task Date
Work 1 Task 1 08-12-2021
Work 1 Task 2 09-12-2021
Work 1 Task 3 10-12-2021
Work 2 Task 1 20-12-2021
Work 2 Task 2 20-12-2021
Work 2 Task 3 21-12-2021
Work 1 Task 1 10-12-2021
Work 1 Task 2 11-12-2021

数据集:

In [1]: df = pd.DataFrame([['Work1','Task1','08-12-2021'], ['Work1','Task2','09-12-2021'], ['Work1','Task3','10-12-2021'],['Work2','Task1','20-12-2021'],['Work2','Task2','20-12-2021'],['Work2','Task3','21-12-2021'],['Work1','Task1','10-12-2022'],['Work1','Task2','11-12-2022']], columns=['Work', 'Task','Date'])

我尝试了什么:我计算了一个包含分组工作、工作开始日期(聚合最小日期)、工作完成日期(聚合最大日期)、第一项任务、最新任务的数据框。

df2 = df.groupby(by=('Work')).agg(Max=('Date','max'), Min=('Date','min')).reset_index()

df2["Finish_Date"] = df2.merge(df, left_on=["Work", "Max"], right_on=["Work", "Date"])['Task']
df2["Start_Date"] = df2.merge(df, left_on=["Work", "Min"], right_on=["Work", "Date"])['Task']

我得到什么:

Work Start Date Finish Date First Task Last Task
Work 1 08-12-2021 11-12-2022 Task 1 Task 2
Work 2 20-12-2021 21-12-2021 Task 1 task 3

问题是工作不能持续超过 3 天,我想根据工作分组,但每个分组不超过 3 天。我尝试用作品+开始日期来创建一个新的列来区分不同的作品,但没有成功。我想要的是:

Work Start Date Finish Date First Task Last Task
Work 1 - 08-12-2021 08-12-2021 10-12-2022 Task 1 Task 3
Work 2 - 20-12-2021 20-12-2021 21-12-2021 Task 1 task 3
Work 1 - 10-12-2022 10-12-2022 11-12-2022 Task 1 task 3

谢谢

【问题讨论】:

  • 为什么 Last Task task_3 而不是 task_2 因为这是 11-12-2021 上的最后一个任务?

标签: python pandas dataframe


【解决方案1】:

groupby中使用shiftcumsum

output =  ( df.groupby([(df['Work'] != df['Work'].shift()).cumsum()])
                   .agg(('first', 'last')).drop('Work', axis=1))

输出:

    Task            Date
   first    last    first         last
Work                
1   Task1   Task3   08-12-2021  10-12-2021
2   Task1   Task3   20-12-2021  21-12-2021
3   Task1   Task2   10-12-2022  11-12-2022

【讨论】:

    【解决方案2】:

    这是我将如何做这样的事情:

    # Assuming the data is sorted by date:
    # First, convent the date to a datetime.
    df['Date'] = pd.to_datetime(df['Date'], format="%d-%m-%Y")
    
    # then find the gaps of 3 or more days and label the consecutive runs
    groupings = (df['Date'].diff().dt.days >= 3).cumsum()
    
    # we can now group by these labels and find the min/max
    final = df.groupby(groupings).agg({'Date': ['min','max'], 'Task': ['first','last']})
    
    # Then, to flatten the multiindex, we can use:
    final.columns = ['Start Date', 'End Date', 'First Task', 'Last Task']
    
    ===================== 
    Result:
         Start Date   End Date First Task Last Task
    Date
    0    2021-12-08 2021-12-10      Task1     Task3
    1    2021-12-20 2021-12-21      Task1     Task3
    2    2022-12-10 2022-12-11      Task1     Task2
    

    希望有帮助!

    编辑澄清:

    如果不同的工作项在日期上很接近但需要留在不同的组中,您可以改用groupby(['Work',groupings])

    此外,如果您有以下内容:

    Work Date
    T1 2022-01-03
    T2 2022-01-05
    T2 2022-01-07
    T1 2022-01-09

    您可能想使用groupings = (df.groupby('Work').Date.diff().dt.days >= 3).cumsum(),以便两个T1出现在不同的组中。

    【讨论】:

    • 谢谢你的回答。我是否应该添加最终的 df.groupby(by=['Work',groupings] 以使结果也按 Work 分组?
    • 是的,我忘了那部分。这将解决一些重叠组的问题。您可能还想在计算连续日期之前对 Work 进行分组:grouping = (df.groupby('Work').Date.diff().dt.days >=3).cumsum(),例如[W1,W2,W2,W2,W1] 连续几天有两个 W1 在不同的组中。
    • 我编辑了我的答案以帮助澄清这一点。
    猜你喜欢
    • 2014-05-16
    • 2021-07-23
    • 2021-04-07
    • 2021-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-08
    相关资源
    最近更新 更多