はじめに
今回「イベントデータ」という言葉を (時刻, イベント名 (, ユーザ)(, 数量)) のようなデータ の意味で使います。
ユーザの予測モデルなどを作ろうとした時に、このようなイベントデータを「1ユーザ1行」になるように整形したくなることがあります。
pandas でこの辺を処理を行う方法についてメモしておきます。
方法
まず、下記のようなイベントデータを例に考えます。
import pandas as pd
data = [
['user1', '2015-7-1 10:00:00', 'login'],
['user1', '2015-7-1 10:00:10', 'item'],
['user1', '2015-7-1 10:00:30', 'item'],
['user2', '2015-7-1 10:01:00', 'top'],
['user1', '2015-7-1 10:01:30', 'cart'],
['user2', '2015-7-1 10:01:50', 'login'],
['user2', '2015-7-1 10:02:30', 'logout'],
['user1', '2015-7-2 13:00:00', 'login'],
['user1', '2015-7-2 13:01:00', 'logout'],
['user3', '2015-7-2 13:01:00', 'top'],
['user2', '2015-7-2 13:01:50', 'login'],
['user2', '2015-7-2 13:02:30', 'item'],
['user2', '2015-7-2 13:03:30', 'cart'],
['user2', '2015-7-2 13:03:30', 'history'],
]
df = pd.DataFrame(data)
df.columns = ['user_id', 'time', 'event']
df['time'] = pd.to_datetime(df['time'])
df['dummy'] = 1
df
user_id | time | event | dummy | |
---|---|---|---|---|
0 | user1 | 2015-07-01 10:00:00 | login | 1 |
1 | user1 | 2015-07-01 10:00:10 | item | 1 |
2 | user1 | 2015-07-01 10:00:30 | item | 1 |
3 | user2 | 2015-07-01 10:01:00 | top | 1 |
4 | user1 | 2015-07-01 10:01:30 | cart | 1 |
5 | user2 | 2015-07-01 10:01:50 | login | 1 |
6 | user2 | 2015-07-01 10:02:30 | logout | 1 |
7 | user1 | 2015-07-02 13:00:00 | login | 1 |
8 | user1 | 2015-07-02 13:01:00 | logout | 1 |
9 | user3 | 2015-07-02 13:01:00 | top | 1 |
10 | user2 | 2015-07-02 13:01:50 | login | 1 |
11 | user2 | 2015-07-02 13:02:30 | item | 1 |
12 | user2 | 2015-07-02 13:03:30 | cart | 1 |
13 | user2 | 2015-07-02 13:03:30 | history | 1 |
このデータを 「1ユーザ1行」として「日毎イベント毎の発生回数の総和」を列とするように整形したいとします。
pivot_table と Grouper
DataFrameクラスの pivot_table()
を使うと Excelのピボットテーブルと似たような操作を行うことができます。
日毎に集計したいのですが、 Grouper()
というのを使うとdatetime型の列に対してよしなにGroup化できます。なんて便利な・・。
res2 = df.pivot_table(index=['user_id', pd.Grouper(freq='d', key='time')], columns='event', values='dummy', aggfunc=len)
res2
event | cart | history | item | login | logout | top | |
---|---|---|---|---|---|---|---|
user_id | time | ||||||
user1 | 2015-07-01 | 1 | NaN | 2 | 1 | NaN | NaN |
2015-07-02 | NaN | NaN | NaN | 1 | 1 | NaN | |
user2 | 2015-07-01 | NaN | NaN | NaN | 1 | 1 | 1 |
2015-07-02 | 1 | 1 | 1 | 1 | NaN | NaN | |
user3 | 2015-07-02 | NaN | NaN | NaN | NaN | NaN | 1 |
Grouperの freq='d'
なので1日単位での集計になります。 freq='M'
とかにすると月単位などになります。
日付を列にもってくる
unstack() を使うと良いです。
res3 = res2.unstack()
res3
event | cart | history | item | login | logout | top | ||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
time | 2015-07-01 | 2015-07-02 | 2015-07-01 | 2015-07-02 | 2015-07-01 | 2015-07-02 | 2015-07-01 | 2015-07-02 | 2015-07-01 | 2015-07-02 | 2015-07-01 | 2015-07-02 |
user_id | ||||||||||||
user1 | 1 | NaN | NaN | NaN | 2 | NaN | 1 | 1 | NaN | 1 | NaN | NaN |
user2 | NaN | 1 | NaN | 1 | NaN | 1 | 1 | 1 | 1 | NaN | 1 | NaN |
user3 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1 |
日付を上にもってきたい
swaplevel(入れ替えたいIndex1, 入れ替えたいIndex2)
を使います。
あと、列(軸番号1)について操作したいので axis=1
を付けます。
res4 = res3.swaplevel(0, 1, axis=1)
res4
time | 2015-07-01 | 2015-07-02 | 2015-07-01 | 2015-07-02 | 2015-07-01 | 2015-07-02 | 2015-07-01 | 2015-07-02 | 2015-07-01 | 2015-07-02 | 2015-07-01 | 2015-07-02 |
---|---|---|---|---|---|---|---|---|---|---|---|---|
event | cart | cart | history | history | item | item | login | login | logout | logout | top | top |
user_id | ||||||||||||
user1 | 1 | NaN | NaN | NaN | 2 | NaN | 1 | 1 | NaN | 1 | NaN | NaN |
user2 | NaN | 1 | NaN | 1 | NaN | 1 | 1 | 1 | 1 | NaN | 1 | NaN |
user3 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1 |
列は日付順にならんで欲しい
sortlevel()
を使います。
res4.sortlevel(0, axis=1)
time | 2015-07-01 | 2015-07-02 | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
event | cart | history | item | login | logout | top | cart | history | item | login | logout | top |
user_id | ||||||||||||
user1 | 1 | NaN | 2 | 1 | NaN | NaN | NaN | NaN | NaN | 1 | 1 | NaN |
user2 | NaN | NaN | NaN | 1 | 1 | 1 | 1 | 1 | 1 | 1 | NaN | NaN |
user3 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1 |
これで目的の形に整形できました。
さいごに
「Eventの種類数×日付数」の数の列ができてしまうので、それが多すぎる場合は使えないですね。
Event種類数が多くないか、月単位で集計するという場合には使えると思います。
それにしてもpandas便利ですねー