@shou2003

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

【Python】アクセスログテーブルにユーザーのpv数カラムを追加したい

解決したいこと

アクセスログのテーブルにユーザーごとの過去3日におけるpv数を集計したカラムを追加したいと思っております。
初心者ですが、Pythonを使って実現できる方法をご教授頂けますと幸いです。

例)
以下のような時系列テーブルがあったとして、
E列にユーザーごとの過去3日間(過去259,200秒間)における合計pv数を都度集計した行を追加。
image.png

実現後のユーザーAのデータ例。
こういった形で各ユーザーごとの集計を可能にしたい。
image.png

以上となります。
実現方法及び参考となるリンク若しくは書籍を含めてご教授頂けますと幸いでございます。
よろしくお願い致します。

0 likes

2Answer

ケースによってコードの書き方が様々だと思いまして、以下の内容をご共有いただけますと幸いです!

  • 行列操作をする場合Pandasなどのライブラリを使うケースが多いのですが、Pandasを使った解法でも構いませんでしょうか?それともライブラリを使わずにPythonのみを使った方が宜しいでしょうか?
  • データの読み込みや結果の保存はCSVなどでしょうか?それともエクセルなどを考慮する必要がありますでしょうか?エクセルの場合エクセル用のPythonライブラリも必要になります!
1Like

Comments

  1. @shou2003

    Questioner

    拾って頂きありがとうございます!

    ・pandasを使った解法で問題ございませんが、ライブラリを使わないとかなり大変な作業になりますでしょうか。

    ・csvでの読み込みと保存を想定しております。

・pandasを使った解法で問題ございませんが、ライブラリを使わないとかなり大変な作業になりますでしょうか。

そこまで大変・・・という程のものでもないのですが、ライブラリを使った方がやはり楽なのと、この手のものは大体Pandas・・・という印象なので、今後もPythonでこういった作業をやる場合はライブラリをインストールしておくと便利だと思います!

Pandasのインストールは色々な記事が既に世の中に色々あるのでここでは割愛いたします!Anacondaなどの最初からインストールされているものを使うか、pip install pandasといったpipのコマンドで(エラーにならなければ)入ると思います!


まずデータに関してですが、雑ではありますがそれっぽいサンプルデータを用意しておきました!

インストール済みのPandasライブラリを使う場合、import pandas as pdといった記述が必要になります。

Pandasで読み込む場合pd.read_csv関数で第一引数にパスを指定することで対応ができます!GitHubのURLを使うこともできますので、サンプルコードではGitHubのURLを指定していますが、ローカルで扱う場合にはURLの代わりに./sample_data.csvみたいな指定に書き換えをお願いします!

import pandas as pd

# ローカルファイルから読み込む場合は、 pd.read_csv('./sample_data.csv')
# といったような指定になります。
df: pd.DataFrame = pd.read_csv('https://raw.githubusercontent.com/simon-ritchie/note/master/20210830_qiita_question/sample_data.csv')
print(df)

以下のような内容にしてあります。

     visit_date           visit_time   unix_time user
0    2021-08-25  2021-08-25 00:03:11  1629817391    F
1    2021-08-25  2021-08-25 00:04:49  1629817489    F
2    2021-08-25  2021-08-25 00:08:15  1629817695    B
3    2021-08-25  2021-08-25 00:11:25  1629817885    A
4    2021-08-25  2021-08-25 00:12:32  1629817952    H
..          ...                  ...         ...  ...
995  2021-08-30  2021-08-30 14:39:32  1630301972    G
996  2021-08-30  2021-08-30 14:45:20  1630302320    A
997  2021-08-30  2021-08-30 14:49:58  1630302598    C
998  2021-08-30  2021-08-30 14:50:27  1630302627    G
999  2021-08-30  2021-08-30 14:50:52  1630302652    A

コードは以下のようにしてみました!(各行の説明は順番に触れていきます)

from typing import List

import pandas as pd

# GitHub上のCSVからデータを読み込んでいます。
df: pd.DataFrame = pd.read_csv('https://raw.githubusercontent.com/simon-ritchie/note/master/20210830_qiita_question/sample_data.csv')

pv_list: List[int] = []
for index, sr in df.iterrows():
    user: str = sr['user']
    unix_time: int = int(sr['unix_time'])
    three_days_ago_unixtime: int = unix_time - 259200
    sliced_df: pd.DataFrame = df[df['user'] == user]
    sliced_df = sliced_df[sliced_df['unix_time'] >= three_days_ago_unixtime]
    sliced_df = sliced_df[sliced_df['unix_time'] <= unix_time]
    pv: int = len(sliced_df)
    pv_list.append(pv)
df['pv'] = pv_list

print(df.head(50))
print(df.tail(50))

df.to_csv('result.csv', index=False, encoding='utf-8')

計算結果は以下のようになります!(先頭と末尾の50件ずつを表示しております)

    visit_date           visit_time   unix_time user  pv
0   2021-08-25  2021-08-25 00:03:11  1629817391    F   1
1   2021-08-25  2021-08-25 00:04:49  1629817489    F   2
2   2021-08-25  2021-08-25 00:08:15  1629817695    B   1
3   2021-08-25  2021-08-25 00:11:25  1629817885    A   1
4   2021-08-25  2021-08-25 00:12:32  1629817952    H   1
5   2021-08-25  2021-08-25 00:14:13  1629818053    C   1
6   2021-08-25  2021-08-25 00:20:59  1629818459    F   3
7   2021-08-25  2021-08-25 00:49:12  1629820152    D   1
8   2021-08-25  2021-08-25 00:59:20  1629820760    B   2
9   2021-08-25  2021-08-25 01:16:32  1629821792    B   3
10  2021-08-25  2021-08-25 01:24:49  1629822289    G   1
11  2021-08-25  2021-08-25 01:45:39  1629823539    E   1
12  2021-08-25  2021-08-25 01:47:22  1629823642    D   2
13  2021-08-25  2021-08-25 01:50:40  1629823840    D   3
14  2021-08-25  2021-08-25 01:59:44  1629824384    E   2
15  2021-08-25  2021-08-25 02:20:00  1629825600    G   2
16  2021-08-25  2021-08-25 02:32:46  1629826366    F   4
17  2021-08-25  2021-08-25 02:41:27  1629826887    F   5
18  2021-08-25  2021-08-25 02:42:11  1629826931    H   2
19  2021-08-25  2021-08-25 02:43:08  1629826988    E   3
20  2021-08-25  2021-08-25 02:56:08  1629827768    B   4
21  2021-08-25  2021-08-25 02:58:35  1629827915    C   2
22  2021-08-25  2021-08-25 03:10:15  1629828615    E   4
23  2021-08-25  2021-08-25 03:16:24  1629828984    A   2
24  2021-08-25  2021-08-25 03:18:30  1629829110    A   3
25  2021-08-25  2021-08-25 03:25:51  1629829551    A   4
26  2021-08-25  2021-08-25 03:26:05  1629829565    G   3
27  2021-08-25  2021-08-25 03:27:41  1629829661    A   5
28  2021-08-25  2021-08-25 03:31:36  1629829896    A   6
29  2021-08-25  2021-08-25 03:58:13  1629831493    B   5
30  2021-08-25  2021-08-25 04:21:05  1629832865    G   4
31  2021-08-25  2021-08-25 04:23:14  1629832994    D   4
32  2021-08-25  2021-08-25 04:23:35  1629833015    C   3
33  2021-08-25  2021-08-25 04:25:08  1629833108    B   6
34  2021-08-25  2021-08-25 04:35:54  1629833754    E   5
35  2021-08-25  2021-08-25 04:46:50  1629834410    B   7
36  2021-08-25  2021-08-25 04:57:12  1629835032    E   6
37  2021-08-25  2021-08-25 05:03:41  1629835421    A   7
38  2021-08-25  2021-08-25 05:04:59  1629835499    E   7
39  2021-08-25  2021-08-25 05:14:39  1629836079    A   8
40  2021-08-25  2021-08-25 05:19:12  1629836352    D   5
41  2021-08-25  2021-08-25 05:36:12  1629837372    G   5
42  2021-08-25  2021-08-25 06:11:42  1629839502    G   6
43  2021-08-25  2021-08-25 06:12:10  1629839530    A   9
44  2021-08-25  2021-08-25 06:13:42  1629839622    D   6
45  2021-08-25  2021-08-25 06:19:09  1629839949    B   8
46  2021-08-25  2021-08-25 06:23:05  1629840185    B   9
47  2021-08-25  2021-08-25 06:27:30  1629840450    E   8
48  2021-08-25  2021-08-25 06:42:22  1629841342    B  10
49  2021-08-25  2021-08-25 06:47:23  1629841643    H   3
     visit_date           visit_time   unix_time user  pv
950  2021-08-30  2021-08-30 11:07:46  1630289266    A  66
951  2021-08-30  2021-08-30 11:07:50  1630289270    C  62
952  2021-08-30  2021-08-30 11:18:33  1630289913    E  66
953  2021-08-30  2021-08-30 11:23:07  1630290187    H  59
954  2021-08-30  2021-08-30 11:43:37  1630291417    H  60
955  2021-08-30  2021-08-30 11:47:38  1630291658    B  58
956  2021-08-30  2021-08-30 11:48:44  1630291724    H  61
957  2021-08-30  2021-08-30 11:53:24  1630292004    A  67
958  2021-08-30  2021-08-30 12:05:42  1630292742    G  81
959  2021-08-30  2021-08-30 12:08:51  1630292931    D  70
960  2021-08-30  2021-08-30 12:14:34  1630293274    A  68
961  2021-08-30  2021-08-30 12:17:49  1630293469    A  69
962  2021-08-30  2021-08-30 12:18:20  1630293500    G  82
963  2021-08-30  2021-08-30 12:18:29  1630293509    A  70
964  2021-08-30  2021-08-30 12:22:56  1630293776    D  70
965  2021-08-30  2021-08-30 12:33:36  1630294416    G  83
966  2021-08-30  2021-08-30 12:34:57  1630294497    H  61
967  2021-08-30  2021-08-30 12:39:06  1630294746    F  66
968  2021-08-30  2021-08-30 12:42:27  1630294947    F  67
969  2021-08-30  2021-08-30 13:00:41  1630296041    A  67
970  2021-08-30  2021-08-30 13:03:59  1630296239    E  65
971  2021-08-30  2021-08-30 13:04:50  1630296290    F  68
972  2021-08-30  2021-08-30 13:08:39  1630296519    C  61
973  2021-08-30  2021-08-30 13:09:45  1630296585    A  68
974  2021-08-30  2021-08-30 13:10:07  1630296607    F  68
975  2021-08-30  2021-08-30 13:20:40  1630297240    B  59
976  2021-08-30  2021-08-30 13:21:55  1630297315    G  82
977  2021-08-30  2021-08-30 13:29:09  1630297749    H  60
978  2021-08-30  2021-08-30 13:31:09  1630297869    C  61
979  2021-08-30  2021-08-30 13:33:20  1630298000    H  61
980  2021-08-30  2021-08-30 13:34:23  1630298063    D  70
981  2021-08-30  2021-08-30 13:36:44  1630298204    B  58
982  2021-08-30  2021-08-30 13:38:40  1630298320    B  59
983  2021-08-30  2021-08-30 13:39:42  1630298382    C  62
984  2021-08-30  2021-08-30 13:43:12  1630298592    B  60
985  2021-08-30  2021-08-30 13:53:17  1630299197    G  83
986  2021-08-30  2021-08-30 13:53:37  1630299217    E  66
987  2021-08-30  2021-08-30 14:05:49  1630299949    B  61
988  2021-08-30  2021-08-30 14:08:22  1630300102    E  67
989  2021-08-30  2021-08-30 14:11:33  1630300293    D  70
990  2021-08-30  2021-08-30 14:14:06  1630300446    A  68
991  2021-08-30  2021-08-30 14:19:29  1630300769    G  84
992  2021-08-30  2021-08-30 14:25:43  1630301143    A  69
993  2021-08-30  2021-08-30 14:33:26  1630301606    A  70
994  2021-08-30  2021-08-30 14:39:26  1630301966    F  69
995  2021-08-30  2021-08-30 14:39:32  1630301972    G  85
996  2021-08-30  2021-08-30 14:45:20  1630302320    A  70
997  2021-08-30  2021-08-30 14:49:58  1630302598    C  62
998  2021-08-30  2021-08-30 14:50:27  1630302627    G  86
999  2021-08-30  2021-08-30 14:50:52  1630302652    A  71

まずpv_list: List[int] = []の部分ですが、これは各行のPVの計算結果を保存するためのリストとして初期化してあります。

for index, sr in df.iterrows():の記述ですが、これはPandasのデータフレームと呼ばれるデータで1行ずつループを回す形で各行のデータにアクセスしていくための記述となります。詳細は以下の記事などをご確認ください!

srという変数に各行のデータが格納され、sr['user']sr['unix_time']といったように辞書のように書くことで行ごとのの各列のデータにアクセスができます!今回は各行のユーザーとUNIXTIMEの値を変数に格納しています(unix_time: int = int(sr['unix_time'])などの部分)。

three_days_ago_unixtime: int = unix_time - 259200の部分で、対象の行の3日前のUNIXTIMEを算出しています。

その後はsliced_dfという変数名を使って、

  • 対象のユーザーのみにする(df[df['user'] == user]部分)
  • UNIXTIMEが3日前よりも後のデータのみにする(sliced_df[sliced_df['unix_time'] >= three_days_ago_unixtime]部分)
  • UNIXTIMEがその行のUNIXTIME以前のデータのみにする(sliced_df[sliced_df['unix_time'] <= unix_time]

という、スライスと呼ばれるデータの絞り込み処理を入れています!

スライス処理等に関しては以下の記事などをご確認ください。==>=などの比較演算子で条件を設定できます!

続いてpv: int = len(sliced_df)部分で、前述の条件を満たした行数をカウントしてPVを取得しています。計算した値はpv_list.append(pv)として結果のデータを保持するためのリストに追加しています。

最後にdf['pv'] = pv_listとして、エクセルで言うところのE列に該当するpvカラム(列)を追加しています!

その後に続くprint(df.head(50))print(df.tail(50))は先頭の行と末尾の行を50行ずつ表示するための記述です。

参考 :

結果はto_csvメソッドでCSVで保存ができます!今回はresult.csvというファイル名で保存しています!

参考 :


注意点として、今回使った書き方(1行ずつのループ・スライスなど)は結構遅めです!コードの分かりやすさを優先しましたが、行数が大分多い・・・場合にはこの書き方はあまり良くないので、コードは複雑になりますがもっとチューニングなどを考慮した書き方が必要になってきます!(その辺までは長くなるので今回は触れておりません)

役立ちそうでしたらLGTM押していただけますと喜びます!

1Like

Comments

  1. @shou2003

    Questioner

    大変ご丁寧にご回答を頂き感謝します。

    Pythonの入門書を通読したばかりの初心者で、コードの理解に時間が掛かるかもですが少しずつ紐解かせて頂きます。

    途中で追加質問がある際は、またよろしくお願い致します。

Your answer might help someone💌