【问题标题】:DataFrame New Column to split sessions by time difference - pandasDataFrame 新列按时差拆分会话 - 熊猫
【发布时间】:2021-02-02 14:47:22
【问题描述】:

我有以下排序的 DataFrame:

import pandas as pd

hits = {'id': ['A','A','A','A','B','B','C','C'],
        'datetime': ['2010-01-02 03:00:00','2010-01-02 03:05:10','2010-01-02 03:51:35','2010-01-02 04:40:20',
                    '2010-01-02 03:29:10','2010-01-02 03:29:15','2010-01-02 03:45:20','2010-01-02 06:10:05'],
        'value': [1,2,2,1,1,3,2,4]
       }

df = pd.DataFrame(hits, columns = ['id', 'datetime','value'])

df['datetime'] =  pd.to_datetime(df['datetime'], format='%Y-%m-%d %H:%M:%S')

print (df)


  id            datetime  value
0  A 2010-01-02 03:00:00      1
1  A 2010-01-02 03:05:10      2
2  A 2010-01-02 03:51:35      2
3  A 2010-01-02 04:40:20      1
4  B 2010-01-02 03:29:10      1
5  B 2010-01-02 03:29:15      3
6  C 2010-01-02 03:45:20      2
7  C 2010-01-02 06:10:05      4

id 列允许我区分唯一用户,但我想向前迈出一步,并能够按会话对点击进行分组。一个会话定义为所有用户活动不超过 30 分钟。

在我的 DataFrame 中,所需的输出应该是:

  id            datetime  value  session
0  A 2010-01-02 03:00:00      1        1
1  A 2010-01-02 03:05:10      2        1
2  A 2010-01-02 03:51:35      2        2
3  A 2010-01-02 04:40:20      1        3
4  B 2010-01-02 03:29:10      1        1
5  B 2010-01-02 03:29:15      3        1
6  C 2010-01-02 03:45:20      2        1
7  C 2010-01-02 06:10:05      4        2

SQL 中,我将首先使用lag 来计算partition by id order by datetime asc 上的命中之间的差异,然后在新查询中我将使用sum(case when diff > 30min then 1 else 0 end),也按id 分区。

Pandas 中有类似的东西吗?

【问题讨论】:

    标签: python pandas partition


    【解决方案1】:

    diff 上使用cumsum 与阈值进行比较来识别由阈值分隔的块是一种常见的技术。比如:

    series.diff().gt('30Min').cumsum()
    

    由于您想通过 id 查找块,您只需将其包装在 groupby() 中:

    df['session'] = (df.groupby('id')['datetime']
                       .transform(lambda x: x.diff().gt('30Min').cumsum())
                    )
    

    输出:

      id            datetime  value  session
    0  A 2010-01-02 03:00:00      1        0
    1  A 2010-01-02 03:05:10      2        0
    2  A 2010-01-02 03:51:35      2        1
    3  A 2010-01-02 04:40:20      1        2
    4  B 2010-01-02 03:29:10      1        0
    5  B 2010-01-02 03:29:15      3        0
    6  C 2010-01-02 03:45:20      2        0
    7  C 2010-01-02 06:10:05      4        1
    

    【讨论】:

    • 太棒了!只是一个附带问题:你怎么知道“30分钟”的把戏?我在official docs of .gt() 中找不到它。
    • @BillHuang 不记得我什么时候知道的了。
    • @BillHuang 这种语法在其他日期/时间操作中类似,包括pd.Grouper.asfreq()等。
    • @DavidErickson pd.Grouper.asfreq() 似乎也不存在于文档中。搜索“datetime gt”也给了我看似无关的结果。也许它曾经在 0.x 版本中记录,但在 1.x 版本中被删除?
    • @BillHuang 我使用Grouper.asfreq 作为两个单独的示例。 Grouper.asfreq 的官方文档在其文档中都包含以下链接:pandas.pydata.org/pandas-docs/stable/user_guide/… Min 在该表中。但是,我见过minm,我相信minutes 也通过了。并非所有版本的 pandas 都接受每个字符串变体,因为我记得过去回答过一个 stackoverflow 问题,而我帮助的人有一个旧版本。 1m 无效,但 1min 有效。
    【解决方案2】:

    你可以做一个双重.groupby

    1. 您可以创建一个名为 sboolean series,在 id 上使用 .groupby,如果超过 30 分钟,则为每个组中的每一行返回 TrueFalse
    2. 然后,您可以再次在步骤 1 中创建的 id 上进行分组,并使用 .cumsum 返回累积计数并加 1,以便从 1 而不是 0 开始计数

    df['session'] = (df.assign(session=(df.groupby('id')['datetime'].diff() > '00:30:00')
                                          .astype(int))
                       .groupby('id')['session'].cumsum() + 1)
    Out[1]: 
      id            datetime  value  session
    0  A 2010-01-02 03:00:00      1        1
    1  A 2010-01-02 03:05:10      2        1
    2  A 2010-01-02 03:51:35      2        2
    3  A 2010-01-02 04:40:20      1        3
    4  B 2010-01-02 03:29:10      1        1
    5  B 2010-01-02 03:29:15      3        1
    6  C 2010-01-02 03:45:20      2        1
    7  C 2010-01-02 06:10:05      4        2
    

    【讨论】:

      猜你喜欢
      • 2013-07-08
      • 1970-01-01
      • 2014-02-19
      • 2019-11-17
      • 1970-01-01
      • 2021-10-26
      • 1970-01-01
      • 2021-04-28
      • 1970-01-01
      相关资源
      最近更新 更多