【问题标题】:Pandas: Group start/end events into intervalsPandas:将开始/结束事件分组为间隔
【发布时间】:2021-11-30 18:40:51
【问题描述】:

我有以下数据:

import pandas as pd

df = pd.DataFrame({
    "id": [1,1,1,2,1,2],
    "datetime": [
        pd.to_datetime("2020-01-01"),
        pd.to_datetime("2020-01-02"),
        pd.to_datetime("2020-01-03"),
        pd.to_datetime("2020-01-04"),
        pd.to_datetime("2020-01-04"),
        pd.to_datetime("2020-01-06"),
        ],
    "type": [
        "start",
        "end",
        "start",
        "start",
        "end",
        "end"
    ]
})

看起来像这样

    pid datetime    type
0   1   2020-01-01  start
1   1   2020-01-02  end
2   1   2020-01-03  start
3   2   2020-01-04  start
4   1   2020-01-04  end
5   2   2020-01-06  end

并且我想对它们进行分组,即对于每个 pid,开始日期和最近结束日期的组合被分组在一行中。请注意,每个 pid 可能会出现多次。所以期望的结果是:

pid  start_date  end_date
1   2020-01-01  2020-01-02
1   2020-01-03  2020-01-04
2   2020-01-04  2020-01-06

我想到的第一个方法是循环并执行元素匹配,但这对我来说似乎效率很低。特别是因为数据框有 >1m 条目。 接下来是 pandas groupby,但我找不到任何类似的用例。

有没有更有效的方法来对我描述的数据进行分组?

【问题讨论】:

  • 我认为 Pandas 中的任何东西都不会在 1m 数据点上有效,向我们展示您的尝试

标签: python pandas dataframe group-by


【解决方案1】:

一个想法是通过GroupBy.cumcount 创建计数器,然后使用DataFrame.pivot

df['g'] = df.groupby('type').cumcount()

df = (df.pivot(['g','id'], 'type','datetime')[['start','end']]
        .add_suffix('_date')
        .reset_index(level=0, drop=True))
print (df)
type start_date   end_date
id                        
1    2020-01-01 2020-01-02
1    2020-01-03 2020-01-04
2    2020-01-04 2020-01-06

【讨论】:

  • 但我认为如果你有 3 个 id,这种方法会失败......可能是 (1, start) (2, start) (3 start) (2, end),然后 cumcount为 0,1,2,0,但不知道该组与哪个 id 相关......如果这有意义吗?
  • @bk_ - 是的,看起来更复杂,可以用这个改变数据样本吗?
【解决方案2】:

试试groupbyaggshift

df.groupby(["id", df["type"].eq(df["type"].shift(-1))])["datetime"].agg(
    start_date="first", end_date="last"
).reset_index(level=0).reset_index(drop=True)

   id start_date   end_date
0   1 2020-01-01 2020-01-02
1   1 2020-01-03 2020-01-04
2   2 2020-01-04 2020-01-06
>>> 

【讨论】:

    【解决方案3】:

    在@jezraels 回答的帮助下,我找到了一种半有效的方法,可以在 1-2 分钟内完成工作:

    result_records = []
    df.sort_values(by="datetime", ascending=True, inplace=True)
    
    for id in df.id.unique():
        df_single_id = df.loc[df["id"]==id].copy()
    
        df_single_id['g'] = df.groupby('type').cumcount()
    
        df_single_id = df_single_id.pivot(index="g",
                                          columns="type")
        
        # merge hierarchical columns to flat
        df_single_id.columns = [f'{i}_{j}' for i,j in df_single_id.columns]
    
        result_records.extend(df_single_id.to_dict('records'))
    
    result_df = pd.Dataframe.from_dict(result_records)
    

    我确信有更好的方法来做到这一点,但在这种情况下,这个解决方案就足够了。

    【讨论】:

      猜你喜欢
      • 2018-12-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-09
      • 2021-10-16
      相关资源
      最近更新 更多