77506


以下のようなログデータに対してpandasでセッションIDを付与したいのですがやり方がわかりません。

[code]

    user time 
    0    2020-05-23 00:01:19
    0    2020-05-23 00:13:32
    1    2020-05-23 00:13:45
    0    2020-05-23 12:59:45
    2    2020-05-23 13:13:13
    1    2020-05-23 15:13:45
    
[/code]

userはユーザを識別するための一意なID、timeはユーザーがアクセスした時間を表します。  
同じユーザーの6時間以内のアクセスであれば同じセッションIDが割り振られます。  
ユーザーが異なればセッションIDも異なります。

そのため上記の例を補完すると以下のようになります。

[code]

    user time                 session_id
    0    2020-05-23 00:01:19  0
    0    2020-05-23 00:13:32  0
    1    2020-05-23 00:13:45  1
    0    2020-05-23 12:59:45  2
    2    2020-05-23 13:13:13  3
    1    2020-05-23 15:13:45  4
    
[/code]

教えていただけますと幸いです。  
よろしくお願いします。


CC BY-SA 4.0
2021-06-12T09:10:22.117
77503
2021-06-13T08:21:29.360
2021-06-12T16:59:44.643
3060
46840
question
python pandas
pandasでログデータに対してセッションIDを付与したい


一応、[本家の方で解決](https://stackoverflow.com/questions/67948917)となった様ですが、、、

どうも `freq='6H'` という指定に問題がありそうです。

[code]

    import pandas as pd
    
    df = pd.DataFrame({
      'user': [0, 0, 1, 0, 2, 1],
      'time': map(pd.Timestamp, [
                '2020-05-23 00:01:19',
                '2020-05-23 00:13:32',
                '2020-05-23 00:13:45',
                '2020-05-23 06:10:45', 
                '2020-05-23 13:13:13',
                '2020-05-23 15:13:45',
              ]),
    })
    
    df['session_id'] = (
      df.groupby(['user', pd.Grouper(key='time', freq='6H')], sort=False)
        .ngroup())
    
    print(df)
    
    ## 処理結果
       user                time  session_id
    0     0 2020-05-23 00:01:19           0
    1     0 2020-05-23 00:13:32           0  <== user 0
    2     1 2020-05-23 00:13:45           1
    3     0 2020-05-23 06:10:45           2  <== user 0
    4     2 2020-05-23 13:13:13           3
    5     1 2020-05-23 15:13:45           4
    
[/code]

3 行目の session_id が `2` になっています。前回の session は `00:13:32` なので 6 時間以内のはずです。

それでは「6 時間以内の差」でグループ化してみます。

[code]

    s = df.sort_values(['user', 'time']).groupby('user').time.diff()\
          .gt(pd.Timedelta('6h')).cumsum().sort_index()
    
    df['session_id'] = df.groupby(['user', s], sort=False).ngroup()
    
    print(df)
    
    ## 処理結果
       user                time  session_id
    0     0 2020-05-23 00:01:19           0
    1     0 2020-05-23 00:13:32           0 <== user 0
    2     1 2020-05-23 00:13:45           1
    3     0 2020-05-23 06:10:45           0 <== user 0
    4     2 2020-05-23 13:13:13           2
    5     1 2020-05-23 15:13:45           3
    
[/code]

こちらでは 3 行目の session_id が `0` になります。

つまり、`freq='6H'` は一日を `00:00〜06:00`, `06:00〜12:00`, `12:00〜18:00`,
`18:00〜24:00` で区切ってグループ化するという事なのでした。

なお、この処理方法は以下を参考に(というかコピペ)しました。

[How to assign a unique ID for different groups in pandas
dataframe?](https://stackoverflow.com/questions/56978362)


CC BY-SA 4.0
2021-06-12T11:35:14.100
77506
2021-06-13T08:21:29.360
2021-06-13T08:21:29.360
77503
answer


コメントで確認したのは、以下のような処理を考えていたためです。  
最初に`user`を主キー、`time`を副キーとしてソートしておき、あとはループで順次直前の行と比較して`session_id`を埋めていく方法です。  
最後は`time`でソートし直します。`session_id`は質問例のような時刻順にはなっていません。

[code]

    # 初期化処理は metropolis さん回答からコピー
    import pandas as pd
    
    df = pd.DataFrame({
      'user': [0, 0, 1, 0, 2, 1],
      'time': map(pd.Timestamp, [
              '2020-05-23 00:01:19',
              '2020-05-23 00:13:32',
              '2020-05-23 00:13:45',
              '2020-05-23 12:59:45',
              '2020-05-23 13:13:13',
              '2020-05-23 15:13:45',
            ]),
    })
    
    df = df.sort_values(['user','time'],ascending=[True,True]) # userを主キー、timeを副キーでソート
    df['session_id'] = None                                    # session_id列追加
    id_index = list(df.columns.values).index('session_id')     # session_idの列番号取得
    
    session_timeout = 6 * 60 * 60 # 6時間の秒数
    session_id = 0                # session_id初期値
    
    df.iat[0, id_index] = session_id
    prev = df.iloc[0]
    for i in range(1,len(df)):
        curr = df.iloc[i]
        # userが変わるか、前回より6時間経過していたら新しいsession_id
        if prev.user != curr.user or (curr.time - prev.time).total_seconds() > session_timeout:
            session_id += 1
        df.iat[i, id_index] = session_id # session_id設定
        prev = curr
    
    df = df.sort_values('time',ascending=True).reset_index(drop=True) # timeでソート、indexも振り直し
    print(df)
    
    # 以下が実行結果
       user                time session_id
    0     0 2020-05-23 00:01:19          0
    1     0 2020-05-23 00:13:32          0
    2     1 2020-05-23 00:13:45          2
    3     0 2020-05-23 12:59:45          1
    4     2 2020-05-23 13:13:13          4
    5     1 2020-05-23 15:13:45          3
    
[/code]


CC BY-SA 4.0
2021-06-12T15:43:33.430
77511
2021-06-12T15:43:33.430
26370
77503
answer