【问题标题】:Group all rows that fall in the same time bin in pandas对熊猫中同一时间 bin 中的所有行进行分组
【发布时间】:2018-05-12 19:17:17
【问题描述】:

我正在尝试定义在不同会话中使用了哪些应用程序。基本上,我将每 5 分钟定义为一个会话,并且想知道数据集中有多少移动会话。另外,我想知道每个会话中启动了哪些应用程序。 我的数据框中的所有行都带有时间戳。 这是数据集中的一个示例:

        timestamp               App
6773    2018-04-08 09:47:57.849 Chrome
6774    2018-04-08 09:48:17.573 YouTube
6775    2018-04-08 09:48:28.538 Instagram
6776    2018-04-08 09:48:37.381 Maps
6777    2018-04-08 09:48:46.680 Netflix
6778    2018-04-08 09:48:56.672 Google Play Store
6779    2018-04-08 09:56:58.880 Google
6780    2018-04-08 09:57:25.461 DB Navigator
6781    2018-04-08 11:28:38.762 Google
6782    2018-04-08 12:58:31.455 Google
6783    2018-04-08 14:31:18.131 Google
6784    2018-04-08 14:31:29.209 Google
6785    2018-04-08 14:58:42.875 Google
6786    2018-04-08 18:18:04.757 Chrome
6787    2018-04-08 21:08:41.368 Google
6788    2018-04-11 10:53:10.744 Google
6789    2018-04-14 19:54:37.441 Google
6790    2018-04-14 19:54:59.833 Google
6791    2018-04-14 19:55:10.844 YouTube
6792    2018-04-14 19:55:34.486 Google
6793    2018-04-14 20:23:00.315 Google
6794    2018-04-15 08:23:44.873 Google
6795    2018-04-15 08:24:07.257 Google

这是所需的输出,其中添加了一个名为 SessionID 的新列,用于定义当前会话的 id。

        timestamp               App                     SessionID
6773    2018-04-08 09:47:57.849 Chrome                  1
6774    2018-04-08 09:48:17.573 YouTube                 1
6775    2018-04-08 09:48:28.538 Instagram               1
6776    2018-04-08 09:48:37.381 Maps                    1
6777    2018-04-08 09:48:46.680 Netflix                 1
6778    2018-04-08 09:48:56.672 Google Play Store       1
6779    2018-04-08 09:56:58.880 Google                  2
6780    2018-04-08 09:57:25.461 DB Navigator            2
6781    2018-04-08 11:28:38.762 Google                  3
6782    2018-04-08 12:58:31.455 Google                  4
6783    2018-04-08 14:31:18.131 Google                  5
6784    2018-04-08 14:31:29.209 Google                  5
6785    2018-04-08 14:58:42.875 Google                  6
6786    2018-04-08 18:18:04.757 Chrome                  7
6787    2018-04-08 21:08:41.368 Google                  8
6788    2018-04-11 10:53:10.744 Google                  9
6789    2018-04-14 19:54:37.441 Google                  10
6790    2018-04-14 19:54:59.833 Google                  10
6791    2018-04-14 19:55:10.844 YouTube                 10
6792    2018-04-14 19:55:34.486 Google                  10
6793    2018-04-14 20:23:00.315 Google                  11
6794    2018-04-15 08:23:44.873 Google                  12
6795    2018-04-15 08:24:07.257 Google                  12  

【问题讨论】:

  • 我删除了您上次的编辑,因为它使问题变得非常大。这仍然是如何提出问题的一个很好的例子。
  • @AntonvBR 谢谢。我想让人们更容易复制数据。

标签: python pandas time-series binning


【解决方案1】:

这是使用np.unique的精简版

min5 = df['timestamp'].astype(np.int64)//(10**9*5*60)
df['SessionID'] = np.unique(min5, return_inverse=True)[1]+1

@Dav2357 很好地指出,您的第 10 组包括 55.something。我认为这是一个错误,但可以通过删除第一个值(如果这是你的基础)来纠正它:

s = df['timestamp'].astype(np.int64)
df['SessionID'] = np.unique((s-s[0])//(10**9*5*60), return_inverse=True)[1]+1

解释:我们使用.astype(np.int64) 可以将日期时间对象转换为纳秒的事实。然后我们用(10**9**5*60)除以得到除数。

除数是相同 5 分钟槽中的值相等的值。

import pandas as pd
import numpy as np

data = '''\
2018-04-08T09:47:57.849,Chrome
2018-04-08T09:48:17.573,YouTube
2018-04-08T09:48:28.538,Instagram
2018-04-08T09:48:37.381,Maps
2018-04-08T09:48:46.680,Netflix
2018-04-08T09:48:56.672,Google Play Store
2018-04-08T09:56:58.880,Google
2018-04-08T09:57:25.461,DB Navigator
2018-04-08T11:28:38.762,Google
2018-04-08T12:58:31.455,Google
2018-04-08T14:31:18.131,Google
2018-04-08T14:31:29.209,Google
2018-04-08T14:58:42.875,Google
2018-04-08T18:18:04.757,Chrome
2018-04-08T21:08:41.368,Google
2018-04-11T10:53:10.744,Google
2018-04-14T19:54:37.441,Google
2018-04-14T19:54:59.833,Google
2018-04-14T19:55:10.844,YouTube
2018-04-14T19:55:34.486,Google
2018-04-14T20:23:00.315,Google
2018-04-15T08:23:44.873,Google
2018-04-15T08:24:07.257,Google'''

df = pd.read_csv(pd.compat.StringIO(data), names=['timestamp','App'], parse_dates=[0])
s = df['timestamp'].astype(np.int64)
df['SessionID'] = np.unique((s-s[0])//(10**9*5*60), return_inverse=True)[1]+1

print(df)

返回:

                 timestamp                App  SessionID
0  2018-04-08 09:47:57.849             Chrome          1
1  2018-04-08 09:48:17.573            YouTube          1
2  2018-04-08 09:48:28.538          Instagram          1
3  2018-04-08 09:48:37.381               Maps          1
4  2018-04-08 09:48:46.680            Netflix          1
5  2018-04-08 09:48:56.672  Google Play Store          1
6  2018-04-08 09:56:58.880             Google          2
7  2018-04-08 09:57:25.461       DB Navigator          2
8  2018-04-08 11:28:38.762             Google          3
9  2018-04-08 12:58:31.455             Google          4
10 2018-04-08 14:31:18.131             Google          5
11 2018-04-08 14:31:29.209             Google          5
12 2018-04-08 14:58:42.875             Google          6
13 2018-04-08 18:18:04.757             Chrome          7
14 2018-04-08 21:08:41.368             Google          8
15 2018-04-11 10:53:10.744             Google          9
16 2018-04-14 19:54:37.441             Google         10
17 2018-04-14 19:54:59.833             Google         10
18 2018-04-14 19:55:10.844            YouTube         10
19 2018-04-14 19:55:34.486             Google         10
20 2018-04-14 20:23:00.315             Google         11
21 2018-04-15 08:23:44.873             Google         12
22 2018-04-15 08:24:07.257             Google         12

时间比较:

1000 loops, best of 3: 962 µs per loop   <-- Anton-vBR 
1000 loops, best of 3: 1.34 ms per loop  <-- Dav2357 
1000 loops, best of 3: 1.43 ms per loop  <-- Wen

【讨论】:

  • 谢谢。您能否也请添加另一种可能的会话方式?建立会话的另一种方法是:对于每个用户,如果在小于 5 分钟的时间增量内发生两个后续操作,则将其分组到一个会话中。在超过 5 分钟的时间增量内完成的任何操作都将是新会话的开始。
  • @Moh 所以每个会话最多 2 个?
  • 不,没有最大值。只要动作是在前一个动作的 5 分钟内完成的,就应该将它们分组在同一个会话中。谢谢。
  • @Moh 抱歉,您可以像上次那样用数据解释这一点。
  • @Moh 如果您可以创建另一个可能更好的问题,因为这也适用于未来的用户。
【解决方案2】:

您可以四舍五入到最接近的 5 分钟,然后使用 factorize

fivemin=5*60*1000000000 
s=pd.to_datetime(((df.timestamp.astype(np.int64) // fivemin + 1 ) * fivemin))
df['new']=pd.factorize(s.astype(str))[0]+1
df
Out[66]: 
                    App               timestamp  new
6773             Chrome 2018-04-08 09:47:57.849    1
6774            YouTube 2018-04-08 09:48:17.573    1
6775          Instagram 2018-04-08 09:48:28.538    1
6776               Maps 2018-04-08 09:48:37.381    1
6777            Netflix 2018-04-08 09:48:46.680    1
6778  Google Play Store 2018-04-08 09:48:56.672    1
6779             Google 2018-04-08 09:56:58.880    2
6780       DB Navigator 2018-04-08 09:57:25.461    2
6781             Google 2018-04-08 11:28:38.762    3
6782             Google 2018-04-08 12:58:31.455    4
6783             Google 2018-04-08 14:31:18.131    5
6784             Google 2018-04-08 14:31:29.209    5
6785             Google 2018-04-08 14:58:42.875    6
6786             Chrome 2018-04-08 18:18:04.757    7
6787             Google 2018-04-08 21:08:41.368    8
6788             Google 2018-04-11 10:53:10.744    9
6789             Google 2018-04-14 19:54:37.441   10
6790             Google 2018-04-14 19:54:59.833   10
6791            YouTube 2018-04-14 19:55:10.844   11
6792             Google 2018-04-14 19:55:34.486   11
6793             Google 2018-04-14 20:23:00.315   12
6794             Google 2018-04-15 08:23:44.873   13
6795             Google 2018-04-15 08:24:07.257   13

【讨论】:

    【解决方案3】:

    @Wen,@Anton vBR,如果我错了,请纠正我,但看起来您的解决方案针对 6791 6792 相对于 OP 发布的所需输出返回了不同的 bin。我尝试了这种方式,它使这两个正确:

    start_t=df["Timestamp"].iloc[0]
    df["ID"]=pd.factorize(df["Timestamp"].apply(lambda x: int((x-start_t).total_seconds()/60) // 5 ))[0]+1
    

    输出:

                        App               Timestamp  ID
    6773             Chrome 2018-04-08 09:47:57.849   1
    6774            YouTube 2018-04-08 09:48:17.573   1
    6775          Instagram 2018-04-08 09:48:28.538   1
    6776               Maps 2018-04-08 09:48:37.381   1
    6777            Netflix 2018-04-08 09:48:46.680   1
    6778  Google Play Store 2018-04-08 09:48:56.672   1
    6779             Google 2018-04-08 09:56:58.880   2
    6780       DB Navigator 2018-04-08 09:57:25.461   2
    6781             Google 2018-04-08 11:28:38.762   3
    6782             Google 2018-04-08 12:58:31.455   4
    6783             Google 2018-04-08 14:31:18.131   5
    6784             Google 2018-04-08 14:31:29.209   5
    6785             Google 2018-04-08 14:58:42.875   6
    6786             Chrome 2018-04-08 18:18:04.757   7
    6787             Google 2018-04-08 21:08:41.368   8
    6788             Google 2018-04-11 10:53:10.744   9
    6789             Google 2018-04-14 19:54:37.441  10
    6790             Google 2018-04-14 19:54:59.833  10
    6791            YouTube 2018-04-14 19:55:10.844  10
    6792             Google 2018-04-14 19:55:34.486  10
    6793             Google 2018-04-14 20:23:00.315  11
    6794             Google 2018-04-15 08:23:44.873  12
    6795             Google 2018-04-15 08:24:07.257  12
    

    【讨论】:

    • 很好地注意到。问题是这是一个错误还是故意的。我猜这是一个错误。
    • 我认为这两种方法的区别在于,在您的情况下,您正在考虑绝对时间,我使用的是第一条记录的经过时间,但不确定 OP 会选择哪个。
    • 好吧,很好地指出!您的解决方案也可以正常工作,但我会尽可能避免使用.apply()。我将您的代码放在时间比较中,说实话并没有那么慢!点赞!
    • 我在不使用.apply() 的情况下考虑了第一个值(就像您所做的那样),在我的回答中发布了一个替代解决方案。
    • 我在这里找到了这篇文章:engineering.upside.com/…。我很快就读完了。我认为它更准确地描述了问题。
    猜你喜欢
    • 1970-01-01
    • 2023-02-07
    • 2023-03-16
    • 2020-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-10
    相关资源
    最近更新 更多