この Blog はそれなりの更新頻度かと思います。
記事の品質問題は置いておき、だいたい 1 月あたり10 記事前後は作成していて、記事数に比例して PageView も増えていく傾向があります。
どの程度記事数と、PageView に相関関係があるのか気になりましたので、Python で計算してみました。
結果を記載します。
前提
相関関係の計算にあたり、前提となる情報を記載します。
この Blog について
2015 年に始めました。
月あたり 10 記事前後を 投稿しており、Web解析のため、Google Analytics、Google Search Console を使用しています。
必要なライブラリのインストール
!pip install --upgrade pip
!pip install pandas
!pip install google2pandas
!pip install oauth2client
Google2Pandas の使い方
Google Analytics のデータ取得には、google2pandas というライブラリを使用しています。
使用する際は、Google Analytics サービスアカウントのキーの発行も必要になります。
以下の記事にまとめていますのでよろしければご確認ください。
Google2Pandas で、Google Analytics のデータを pandas Dataframe に変換する | Monotalk
Mezzanine の月次の記事数取得SQL
PostgresSQL 前提ですが、以下SQLを実行すると日次の記事数と累積記事数を取得できます。
-
Django から DB接続
python3.6 manage.py dbshell -
SQL実行
SELECT DATE_TRUNC('month', publish_date) as publish_month, COUNT(id) AS "Num of entry", SUM(COUNT(id)) OVER(ORDER BY DATE_TRUNC('month', publish_date) ASC) AS "Num of entries up to that month" FROM blog_blogpost AS blogpost GROUP BY publish_month ORDER BY publish_month ASC; -
出力結果
publish_month | Num of entry | Num of entries up to that month ------------------------+--------------+--------------------------------- 2015-04-01 00:00:00+09 | 1 | 1 2015-05-01 00:00:00+09 | 1 | 2 2015-06-01 00:00:00+09 | 4 | 6 2015-09-01 00:00:00+09 | 1 | 7 2015-11-01 00:00:00+09 | 3 | 10 2015-12-01 00:00:00+09 | 9 | 19 2016-01-01 00:00:00+09 | 9 | 28 2016-03-01 00:00:00+09 | 3 | 31 2016-04-01 00:00:00+09 | 8 | 39 2016-05-01 00:00:00+09 | 19 | 58 2016-06-01 00:00:00+09 | 14 | 72 2016-07-01 00:00:00+09 | 9 | 81 2016-08-01 00:00:00+09 | 16 | 97 2016-09-01 00:00:00+09 | 6 | 103 2016-10-01 00:00:00+09 | 11 | 114 2016-11-01 00:00:00+09 | 13 | 127 2016-12-01 00:00:00+09 | 7 | 134 2017-01-01 00:00:00+09 | 24 | 158 2017-02-01 00:00:00+09 | 12 | 170 2017-03-01 00:00:00+09 | 11 | 181 2017-04-01 00:00:00+09 | 14 | 195 2017-05-01 00:00:00+09 | 13 | 208 2017-06-01 00:00:00+09 | 18 | 226 2017-07-01 00:00:00+09 | 25 | 251 2017-08-01 00:00:00+09 | 27 | 278 2017-09-01 00:00:00+09 | 18 | 296 2017-10-01 00:00:00+09 | 16 | 312 2017-11-01 00:00:00+09 | 19 | 331 2017-12-01 00:00:00+09 | 16 | 347 2018-01-01 00:00:00+09 | 23 | 370 2018-02-01 00:00:00+09 | 13 | 383 2018-03-01 00:00:00+09 | 12 | 395 2018-04-01 00:00:00+09 | 16 | 411 2018-05-01 00:00:00+09 | 8 | 419 2018-06-01 00:00:00+09 | 10 | 429 2018-07-01 00:00:00+09 | 9 | 438 2018-08-01 00:00:00+09 | 22 | 460 2018-09-01 00:00:00+09 | 12 | 472 2018-10-01 00:00:00+09 | 8 | 480 2018-11-01 00:00:00+09 | 5 | 485 2018-12-01 00:00:00+09 | 7 | 492 2019-01-01 00:00:00+09 | 9 | 501 2019-02-01 00:00:00+09 | 8 | 509 2019-03-01 00:00:00+09 | 2 | 511 (44 行)
合計 511 記事 結構書いたなと思います。
Google Analytics データの取得
google2pandas を使います。
過去にgoogle2pandas の使い方について、Google2Pandas で、Google Analytics のデータを pandas Dataframe に変換する | Monotalk にまとめました。よろしければこちらもご確認ください。
from google2pandas import *
view_id = '103185238'
query = {
'reportRequests': [{
'viewId' : view_id,
'dateRanges': [{
'startDate' : '1825daysAgo',
'endDate' : 'today'}],
'dimensions' : [
{'name' : 'ga:yearMonth'}
],
'metrics' : [
{'expression' : 'ga:pageViews'}
]
}]
}
conn = GoogleAnalyticsQueryV4(secrets='./ga_client.json')
df = conn.execute_query(query)
# 出力
df['pageViews'] = df['pageViews'].astype(int)
ga_page_views = df.sort_values('yearMonth', ascending=True)
ga_page_views
| yearMonth | pageViews | |
|---|---|---|
| 0 | 201506 | 646 |
| 1 | 201507 | 920 |
| 2 | 201508 | 314 |
| 3 | 201509 | 163 |
| 4 | 201510 | 121 |
| 5 | 201511 | 970 |
| 6 | 201512 | 504 |
| 7 | 201601 | 642 |
| 8 | 201602 | 251 |
| 9 | 201603 | 385 |
| 10 | 201604 | 432 |
| 11 | 201605 | 664 |
| 12 | 201606 | 913 |
| 13 | 201607 | 946 |
| 14 | 201608 | 1145 |
| 15 | 201609 | 1267 |
| 16 | 201610 | 1481 |
| 17 | 201611 | 2847 |
| 18 | 201612 | 3734 |
| 19 | 201701 | 5911 |
| 20 | 201702 | 11972 |
| 21 | 201703 | 2390 |
| 22 | 201704 | 2405 |
| 23 | 201705 | 3114 |
| 24 | 201706 | 3955 |
| 25 | 201707 | 4244 |
| 26 | 201708 | 4448 |
| 27 | 201709 | 5644 |
| 28 | 201710 | 6985 |
| 29 | 201711 | 8005 |
| 30 | 201712 | 7844 |
| 31 | 201801 | 8804 |
| 32 | 201802 | 10040 |
| 33 | 201803 | 12893 |
| 34 | 201804 | 13694 |
| 35 | 201805 | 16909 |
| 36 | 201806 | 18984 |
| 37 | 201807 | 18341 |
| 38 | 201808 | 19236 |
| 39 | 201809 | 19010 |
| 40 | 201810 | 20003 |
| 41 | 201811 | 20071 |
| 42 | 201812 | 24221 |
| 43 | 201901 | 24895 |
| 44 | 201902 | 24356 |
| 45 | 201903 | 4878 |
相関関係を求める
月次の記事数の増加量と、Pageview の相関関係を求めます。
import pandas as pd
# gist に up した Mezzanine の月次の投稿数を取得
monthly_entries = pd.read_csv('https://gist.githubusercontent.com/kemsakurai/ef86122b072e509e3968d4e5cea3bd5f/raw/35fd7f8c5a16cd2fcf318a811c32a32ef42181b8/Number%2520of%2520blog%2520posts%2520per%2520month.tsv',sep='\t')
# YYYY-MM-DD を yearMonth の形式に変換する
# - を除去
monthly_entries['yearMonth'] = monthly_entries['YYYY-MM-DD'].str.replace("-","").str[0:6]
# dataframe を inner join で結合
merge_df = pd.merge(monthly_entries, ga_page_views, how='inner')
# Entry と、PageView の散布図を描画
merge_df.plot(kind='scatter', x='Num of entries up to that month', y='pageViews')
<matplotlib.axes._subplots.AxesSubplot at 0x119ee3c18>
合計記事数が増えれば、PageView が増えると言えそうです。
続いて時系列グラフを描きます。
merge_df.plot(x="yearMonth", subplots=True, sharex=True,figsize=(15, 10))
array([<matplotlib.axes._subplots.AxesSubplot object at 0x11aee16d8>,
<matplotlib.axes._subplots.AxesSubplot object at 0x11af0cbe0>,
<matplotlib.axes._subplots.AxesSubplot object at 0x11b125080>],
dtype=object)
2017年にPageView が上がり、その後下がってるのは監視サービスを導入したためです。
監視ボットのアクセスがGoogle Analytics に記録されていたようです。
続いてdf.corr() で相関係数を求めます。
del merge_df['Num of entry']
merge_df.corr()
| Num of entries up to that month | pageViews | |
|---|---|---|
| Num of entries up to that month | 1.000000 | 0.876561 |
| pageViews | 0.876561 | 1.000000 |
0.876561 で正の相関があることがわかります。
デバイスごとの PageView との相関を計算する
PageView 全体での相関は計算しました。今度はデバイスごとでどのような相関を示すのか計算してみます。
デバイスごとのデータを取得するには、Google Analytics のデータ取得時にディメンションとしてga:deviceCategory を追加します。
from google2pandas import *
view_id = '103185238'
query = {
'reportRequests': [{
'viewId' : view_id,
'dateRanges': [{
'startDate' : '1825daysAgo',
'endDate' : 'today'}],
'dimensions' : [
{'name' : 'ga:yearMonth'},
{'name' : 'ga:deviceCategory'}
],
'metrics' : [
{'expression' : 'ga:pageViews'}
]
}]
}
conn = GoogleAnalyticsQueryV4(secrets='./ga_client.json')
df = conn.execute_query(query)
# 出力
df['pageViews'] = df['pageViews'].astype(int)
ga_page_views = df.sort_values('yearMonth', ascending=True)
ga_page_views
| yearMonth | deviceCategory | pageViews | |
|---|---|---|---|
| 0 | 201506 | desktop | 624 |
| 1 | 201506 | mobile | 22 |
| 2 | 201507 | desktop | 915 |
| 3 | 201507 | mobile | 5 |
| 4 | 201508 | desktop | 309 |
| 5 | 201508 | mobile | 5 |
| 6 | 201509 | desktop | 160 |
| 7 | 201509 | mobile | 2 |
| 8 | 201509 | tablet | 1 |
| 9 | 201510 | desktop | 117 |
| 10 | 201510 | mobile | 4 |
| 11 | 201511 | desktop | 942 |
| 12 | 201511 | mobile | 28 |
| 14 | 201512 | mobile | 44 |
| 15 | 201512 | tablet | 4 |
| 13 | 201512 | desktop | 456 |
| 16 | 201601 | desktop | 536 |
| 17 | 201601 | mobile | 100 |
| 18 | 201601 | tablet | 6 |
| 19 | 201602 | desktop | 232 |
| 20 | 201602 | mobile | 18 |
| 21 | 201602 | tablet | 1 |
| 22 | 201603 | desktop | 359 |
| 23 | 201603 | mobile | 26 |
| 24 | 201604 | desktop | 389 |
| 25 | 201604 | mobile | 40 |
| 26 | 201604 | tablet | 3 |
| 29 | 201605 | tablet | 6 |
| 27 | 201605 | desktop | 547 |
| 28 | 201605 | mobile | 111 |
| ... | ... | ... | ... |
| 102 | 201806 | desktop | 17387 |
| 103 | 201806 | mobile | 1359 |
| 104 | 201806 | tablet | 238 |
| 105 | 201807 | desktop | 16958 |
| 106 | 201807 | mobile | 1179 |
| 107 | 201807 | tablet | 204 |
| 108 | 201808 | desktop | 17171 |
| 109 | 201808 | mobile | 1880 |
| 110 | 201808 | tablet | 185 |
| 113 | 201809 | tablet | 187 |
| 111 | 201809 | desktop | 16412 |
| 112 | 201809 | mobile | 2411 |
| 114 | 201810 | desktop | 18044 |
| 115 | 201810 | mobile | 1734 |
| 116 | 201810 | tablet | 225 |
| 117 | 201811 | desktop | 18362 |
| 118 | 201811 | mobile | 1313 |
| 119 | 201811 | tablet | 396 |
| 120 | 201812 | desktop | 21959 |
| 121 | 201812 | mobile | 1776 |
| 122 | 201812 | tablet | 486 |
| 124 | 201901 | mobile | 1853 |
| 123 | 201901 | desktop | 22544 |
| 125 | 201901 | tablet | 498 |
| 127 | 201902 | mobile | 1451 |
| 126 | 201902 | desktop | 22401 |
| 128 | 201902 | tablet | 504 |
| 130 | 201903 | mobile | 444 |
| 129 | 201903 | desktop | 6917 |
| 131 | 201903 | tablet | 85 |
132 rows × 3 columns
deviceCategory が列に追加され、desktop、tablet、mobile が設定されています。
import pandas as pd
# gist に up した Mezzanine の月次の投稿数を取得
monthly_entries = pd.read_csv('https://gist.githubusercontent.com/kemsakurai/ef86122b072e509e3968d4e5cea3bd5f/raw/35fd7f8c5a16cd2fcf318a811c32a32ef42181b8/Number%2520of%2520blog%2520posts%2520per%2520month.tsv',sep='\t')
# YYYY-MM-DD を yearMonth の形式に変換する
# - を除去
monthly_entries['yearMonth'] = monthly_entries['YYYY-MM-DD'].str.replace("-","").str[0:6]
# dataframe を inner join で結合
merge_df = pd.merge(monthly_entries, ga_page_views, how='inner')
# Entry と、PageView の散布図を描画
# Deviceごとに色を変える
colors = {'desktop':'red', 'tablet':'blue', 'mobile':'green'}
merge_df.plot(kind='scatter', x='Num of entries up to that month', y='pageViews',c=merge_df['deviceCategory'].apply(lambda x: colors[x]))
<matplotlib.axes._subplots.AxesSubplot at 0x11b946f28>
# deviceCategory で Groupby して必要な項目を取得、結果の相関係数を出力
merge_df.groupby('deviceCategory')[['YYYY-MM-DD','yearMonth','pageViews','Num of entries up to that month']].corr()
| pageViews | Num of entries up to that month | ||
|---|---|---|---|
| deviceCategory | |||
| desktop | pageViews | 1.000000 | 0.889307 |
| Num of entries up to that month | 0.889307 | 1.000000 | |
| mobile | pageViews | 1.000000 | 0.718562 |
| Num of entries up to that month | 0.718562 | 1.000000 | |
| tablet | pageViews | 1.000000 | 0.775839 |
| Num of entries up to that month | 0.775839 | 1.000000 |
deviceCategory desktopが最も強い、正の相関を示していて、deviceCategory mobile の 正の相関が最も弱いです。
Desktop と Mobile で 相関が異なる理由
以下が理由かと思います。
-
そもそも Mobile から閲覧する絶対数が少ない。
このサイトのアクセスの90%がDesktopからのアクセスになります。
そもそも絶対数が少ないかなと思います。
技術ブログを閲覧するのは業務中ということでしょうか。 -
Mobileから流入のある記事が限られていて、そのページの検索順位の変動に大きく左右される。
一部の記事でやけに Mobile からのアクセス率の高い記事があります。
そのような記事数は少なくてそのページの検索順位の変動があった場合、閲覧数が増減しているように思いました。
参考
以下、参考にしました。
- 【PostgreSQL】TimestampをDateに変換する - Qiita
- #PostgreSQL Window関数で投稿数の累計を抽出 - Qiita
- 重複行削除・カウント Webアプリ / Webサービス
- python - Pandas Correlation Groupby - Stack Overflow
以上です。
コメント