【保存版】
Google Analytics から
結果を
参考
- esafak/mca: Multiple correspondence analysis
- Googleアナリティクスと
コレスポンデンス分析を 用いた 年齢別の ユーザー像の 捉え方 | トライフィールズ - ワインの
味を 分析して リア充達の クリスマスディナーを 台無しにしよう - Qiita - 【保存版】
ビジネスで 使える 13の データ分析手法を 分かりやすく 解説
分析して 得る 情報
記事の
python で コレスポンデンス分析を 行う ライブラリ
python で
今回はmca
を
コレスポンデンス分析 : 分析技術と
インテリジェンス
Orange のOrange.projection.correspondence を 使って、 コレスポンデンス分析を 実行しています。 MaxHalford/prince: Python factor analysis library (PCA, CA, MCA, FAMD)
一般的な因子分析を 行う ライブラリ だと、 README.md
に記載が あります。 TomAugspurger/skmca: A scikit-learn compatible implementation of MCA
prince
のラッパーで、 scikit-learn と 同じような インターフェースで コレスポンデンス分析を 実装できます。 esafak/mca: Multiple correspondence analysis
Pandas のデータフレームを 使用できる MCAライブラリにです。 skmca
でもPandas の データフレームは 使えます。
ライブラリの インストール
必要な
mca の インストール
コレスポンデンス分析を
pip install
python3 -m pip install --user mca
Output
Collecting mca Using cached mca-1.0.2.tar.gz Requirement already satisfied: scipy in /Users/xxxxxx/Library/Python/3.6/lib/python/site-packages (from mca) Requirement already satisfied: numpy in /Users/xxxxxx/Library/Python/3.6/lib/python/site-packages (from mca) Requirement already satisfied: pandas in /Users/xxxxxx/Library/Python/3.6/lib/python/site-packages (from mca) Requirement already satisfied: pytz>=2011k in /Users/xxxxxx/Library/Python/3.6/lib/python/site-packages (from pandas->mca) Requirement already satisfied: python-dateutil>=2 in /usr/local/lib/python3.6/site-packages (from pandas->mca) Requirement already satisfied: six>=1.5 in /usr/local/lib/python3.6/site-packages (from python-dateutil>=2->pandas->mca) Building wheels for collected packages: mca Running setup.py bdist_wheel for mca ... done Stored in directory: /Users/xxxxxx/Library/Caches/pip/wheels/85/0d/24/fe459ababfb49e7669f16d9607247c055d25537c0f5c7c0d93 Successfully built mca Installing collected packages: mca Successfully installed mca-1.0.2
その他の ライブラリの インストール
gspread
、oauth2client
を
python3 -m pip install gspread --user python3 -m pip install oauth2client --user python3 -m pip install df2gspread --user
データの 入手
Google Analytics Spreadsheet Add-on | Analytics Implementation Guides and Solutions | Google Developers で
以下の
Metrics
- ga:pageviews
Dimensions
- ga:dimension6 (ブログ記事の
カテゴリ) - ga:interestAffinityCategory
- ga:pagePath
手順
以下の
- データの
取得 - データ加工
mca
の呼び出し - 可視化
1. データの 取得
Google スプレッドシートから
from oauth2client.service_account import ServiceAccountCredentials def download_as_df(sheet_id, wks_name): """ Google Spread Sheet からデータを取得、pandas data frame として返す。 """ from df2gspread import gspread2df as g2d # key_file 以下の指定方法だと、notebook と同じディレクトリにあるキーファイルを取得しています。 key_file = "spreadsheet_api_key.json" scope = ['https://spreadsheets.google.com/feeds'] credentials = ServiceAccountCredentials.from_json_keyfile_name(key_file, scope) df = g2d.download(sheet_id, wks_name=wks_name, col_names=True, row_names=False, credentials=credentials, start_cell = 'A15') return df
# ファイルのダウンロード df = download_as_df("1ruwQJIt35zFDXrcFlibwqJcMn1QJwtoJsTBQw7L-1WQ","ユーザの好みと、ページカテゴリの関係") # データ出力 df
ga:dimension6 | ga:interestAffinityCategory | ga:pagePath | ga:pageviews | |
---|---|---|---|---|
0 | Elasticsearch | Lifestyles & Hobbies/Business Professionals | /blog/Implement-Paging-with-Elasticsearch-Java... | 29 |
1 | Elasticsearch | Lifestyles & Hobbies/Green Living Enthusiasts | /blog/Implement-Paging-with-Elasticsearch-Java... | 58 |
2 | Elasticsearch | Lifestyles & Hobbies/Shutterbugs | /blog/Implement-Paging-with-Elasticsearch-Java... | 24 |
... | ... | ... | ... | ... |
569 | python | Technology/Technophiles | /blog/pycharm-terminal-からpythonスクリプトを実行できるようにする/ | 98 |
599 rows × 4 columns
2.データ加工
データ加工と
* Affinity Category の
* クロス集計表に
# Affinity Category の日本語化をするファンクションの定義 import requests translation_dictionary = {} def create_dictionary(): r = requests.get("https://gist.githubusercontent.com/kemsakurai/6c287eab5aa45415bf5c50df1e61a47f/raw/3ea1cf361a8914ad20c70ac46e8473e2590597e9/AffinityCategoryTranslation.tsv") lines = r.text.split("\n") index = 0 for line in lines: index = index + 1 if index == 1: continue elems = line.split("\t") translation_dictionary.update({elems[0]: elems[1]}) def translate_affinity_category(english_string): results = [] for elem in english_string.split("/"): results.append(translation_dictionary.get(elem, "undefined")) return '/'.join(results)
# 辞書の初期化 create_dictionary() # 大カテゴリのみを切り出し df["ga:interestAffinityCategory"] = [i.split("/")[0] for i in df["ga:interestAffinityCategory"]] # 切り出したカテゴリを日本語に変換 df["ga:interestAffinityCategory_ja"] = [translate_affinity_category(i) for i in df["ga:interestAffinityCategory"]] # 後続の集計処理がうまく計算できないので、整数に変換する df["ga:pageviews"] = df["ga:pageviews"].astype(int) # 結果の出力 df
ga:dimension6 | ga:interestAffinityCategory | ga:pagePath | ga:pageviews | ga:interestAffinityCategory_ja | |
---|---|---|---|---|---|
0 | Elasticsearch | Lifestyles & Hobbies | /blog/Implement-Paging-with-Elasticsearch-Java... | 29 | ライフスタイル&趣味 |
1 | Elasticsearch | Lifestyles & Hobbies | /blog/Implement-Paging-with-Elasticsearch-Java... | 58 | ライフスタイル&趣味 |
2 | Elasticsearch | Lifestyles & Hobbies | /blog/Implement-Paging-with-Elasticsearch-Java... | 24 | ライフスタイル&趣味 |
3 | Elasticsearch | Media & Entertainment | /blog/Implement-Paging-with-Elasticsearch-Java... | 51 | メディア&エンターテイメント |
4 | Elasticsearch | Media & Entertainment | /blog/Implement-Paging-with-Elasticsearch-Java... | 46 | メディア&エンターテイメント |
5 | Elasticsearch | Media & Entertainment | /blog/elasticsearch-の-kuromoji-plugin-が削除されてin... | 13 | メディア&エンターテイメント |
... | ... | ... | ... | ... | ... |
598 | データ分析 | Technology | /blog/mac-os-siera-に-superset-をインストールする/ | 14 | 技術 |
599 rows × 5 columns
# 不要なカラムを削除 del df["ga:interestAffinityCategory"] del df["ga:pagePath"] # 再集計 df = df.groupby(['ga:dimension6','ga:interestAffinityCategory_ja'])['ga:pageviews'].sum().reset_index() # ソート df = df.sort_values(by="ga:pageviews",ascending=False) # 結果の出力 df
ga:dimension6 | ga:interestAffinityCategory_ja | ga:pageviews | |
---|---|---|---|
200 | python | メディア&エンターテイメント | 1316 |
... | ... | ... | ... |
177 | msites;資格取得 | ホーム&ガーデン | 15 |
219 rows × 3 columns
# クロス集計 df = df.pivot_table(values='ga:pageviews', index='ga:dimension6', columns='ga:interestAffinityCategory_ja', aggfunc = 'sum') # クロス集計結果を整数に変換 df = df.fillna(0).astype(int) # 結果の出力 df
ga:interestAffinityCategory_ja | スポーツ&フィットネス | ニュース&政治 | フード&ダイニング | ホーム&ガーデン | メディア&エンターテイメント | ライフスタイル&趣味 | 技術 | 旅行 | 自動車&輸送 | 買い物好き | 銀行&金融 |
---|---|---|---|---|---|---|---|---|---|---|---|
ga:dimension6 | |||||||||||
Elasticsearch | 31 | 0 | 0 | 0 | 110 | 111 | 126 | 11 | 0 | 73 | 0 |
Elasticsearch;java | 26 | 16 | 0 | 18 | 42 | 70 | 48 | 15 | 0 | 28 | 0 |
Google Apps Script | 17 | 61 | 15 | 19 | 112 | 80 | 63 | 17 | 0 | 34 | 14 |
Google Spread Sheet | 121 | 236 | 83 | 83 | 507 | 418 | 286 | 75 | 11 | 211 | 50 |
Jupyter Notebook | 14 | 34 | 0 | 0 | 18 | 32 | 33 | 0 | 0 | 21 | 0 |
Lombok;findbugs;java | 0 | 0 | 0 | 0 | 11 | 21 | 24 | 0 | 0 | 13 | 0 |
MongoDB | 0 | 12 | 0 | 0 | 0 | 14 | 12 | 0 | 0 | 13 | 0 |
SEO;python;データ分析 | 24 | 30 | 0 | 22 | 22 | 97 | 58 | 23 | 0 | 29 | 25 |
SonarQube | 16 | 0 | 0 | 0 | 16 | 47 | 35 | 0 | 0 | 21 | 0 |
SonarQube;python | 11 | 25 | 0 | 17 | 25 | 50 | 37 | 11 | 0 | 21 | 0 |
Web API;python | 28 | 76 | 16 | 29 | 157 | 133 | 100 | 22 | 0 | 56 | 18 |
amp | 0 | 0 | 0 | 0 | 0 | 0 | 13 | 0 | 0 | 12 | 0 |
django | 76 | 194 | 37 | 45 | 408 | 306 | 288 | 37 | 0 | 164 | 35 |
django;mezzanine | 0 | 0 | 0 | 17 | 0 | 15 | 40 | 0 | 0 | 21 | 0 |
django;mezzanine;python | 13 | 0 | 0 | 0 | 0 | 44 | 32 | 0 | 0 | 16 | 0 |
django;postgresql | 0 | 14 | 0 | 0 | 13 | 43 | 30 | 0 | 0 | 16 | 0 |
eclipselink;java | 0 | 0 | 0 | 11 | 0 | 24 | 17 | 0 | 0 | 15 | 0 |
findbugs | 0 | 0 | 0 | 0 | 0 | 13 | 17 | 0 | 0 | 17 | 0 |
gradle | 22 | 54 | 0 | 23 | 89 | 94 | 68 | 14 | 0 | 36 | 0 |
jackson;java | 33 | 93 | 16 | 40 | 141 | 159 | 126 | 22 | 0 | 78 | 15 |
java | 160 | 270 | 85 | 86 | 644 | 543 | 422 | 63 | 0 | 260 | 54 |
java;pmd | 26 | 71 | 0 | 0 | 88 | 92 | 86 | 0 | 0 | 43 | 0 |
java;rest;wicket | 0 | 0 | 0 | 0 | 0 | 0 | 36 | 0 | 0 | 21 | 0 |
java;wicket | 0 | 0 | 0 | 0 | 48 | 41 | 42 | 0 | 0 | 33 | 0 |
javascript | 0 | 22 | 0 | 18 | 118 | 73 | 63 | 0 | 0 | 33 | 0 |
linux;python | 0 | 0 | 0 | 0 | 17 | 20 | 23 | 0 | 0 | 23 | 0 |
memcached | 54 | 111 | 26 | 33 | 169 | 174 | 135 | 27 | 0 | 74 | 31 |
mezzanine;python | 0 | 0 | 0 | 0 | 0 | 43 | 42 | 0 | 0 | 32 | 0 |
msites;資格取得 | 18 | 0 | 0 | 15 | 30 | 48 | 43 | 13 | 0 | 25 | 0 |
pmd | 0 | 0 | 0 | 0 | 0 | 21 | 35 | 0 | 0 | 17 | 0 |
postgresql | 31 | 65 | 16 | 26 | 113 | 117 | 94 | 18 | 0 | 49 | 16 |
python | 250 | 565 | 110 | 194 | 1316 | 918 | 773 | 129 | 13 | 476 | 71 |
python;データ分析 | 26 | 0 | 0 | 0 | 45 | 71 | 53 | 0 | 0 | 29 | 0 |
spring-boot | 0 | 0 | 0 | 0 | 15 | 56 | 45 | 0 | 0 | 24 | 0 |
wicket | 0 | 0 | 0 | 0 | 0 | 0 | 13 | 0 | 0 | 0 | 0 |
データ分析 | 0 | 0 | 0 | 0 | 0 | 14 | 14 | 0 | 0 | 0 | 0 |
3.mca の 呼び出し
クロス集計データの
# コレスポンデンス分析のライブラリ import mca # コレスポンデンス分析 ncols = df.shape[1] # Benzécri? 補正するかどうか、データ構造の問題か、True だとエラーとなったため、False で設定 mca_ben = mca.MCA(df, ncols=ncols, benzecri=False) mca_ben.fs_r(N=2)
array([[ 4.04399401e-01, -1.52979611e-01], [ 1.32693892e-01, 2.68842331e-01], [ -2.23112167e-01, 3.42928950e-02], [ -1.59749683e-01, 2.37024719e-02], [ 1.30182305e-01, -5.47186735e-02], [ 6.84718573e-01, -1.58233299e-01], [ 4.44952860e-01, 4.10821376e-03], [ 2.85366767e-02, 6.05926046e-01], [ 5.39503680e-01, 3.58668344e-02], [ 8.45671201e-02, 3.06657490e-01], [ -1.22543395e-01, 4.62416906e-02], [ 1.23966422e+00, -2.25021302e-01], [ -5.67431872e-02, -6.78476641e-02], [ 7.72658336e-01, 4.27289369e-01], [ 7.21808152e-01, 2.17287190e-01], [ 4.30533385e-01, -4.64137005e-02], [ 6.54749307e-01, 4.96416905e-01], [ 1.02923931e+00, -3.24243400e-02], [ -1.24779452e-02, 5.91214264e-02], [ -2.55507110e-02, 1.04271517e-01], [ -7.17114562e-02, -2.35859666e-02], [ 8.42241200e-02, -1.78772790e-01], [ 1.24523117e+00, -2.02165463e-01], [ 4.49509345e-01, -3.42900571e-01], [ 5.81775959e-02, -2.59105041e-01], [ 6.35473732e-01, -2.65732128e-01], [ -1.23921506e-01, 1.17432604e-01], [ 9.62526283e-01, 4.09888658e-02], [ 2.68648888e-01, 3.02809495e-01], [ 1.02700453e+00, 7.05955985e-04], [ -7.98232747e-02, 1.09810136e-01], [ -1.03646230e-01, -6.40915368e-02], [ 3.98004987e-01, -7.22225097e-02], [ 7.19449170e-01, -3.80962202e-02], [ 1.26361260e+00, -1.26698070e-01], [ 8.72559804e-01, 1.77653717e-01]])
mca_ben.fs_c(N=2)
array([[-0.09804034, 0.11634891], [-0.30316912, -0.00814008], [-0.43927978, -0.03347052], [-0.16271903, 0.36136428], [-0.19793054, -0.17165508], [ 0.11685867, 0.06648366], [ 0.30667071, -0.01747563], [-0.26910795, 0.33520229], [-0.53301903, -0.1729311 ], [ 0.29456212, -0.0457295 ], [-0.39228832, 0.36158167]])
import pandas as pd # 表頭の座標を書き出す result_row = pd.DataFrame(mca_ben.fs_r(N=2)) result_row.index = list(df.index) result_row
0 | 1 | |
---|---|---|
Elasticsearch | 0.404399 | -0.152980 |
Elasticsearch;java | 0.132694 | 0.268842 |
Google Apps Script | -0.223112 | 0.034293 |
Google Spread Sheet | -0.159750 | 0.023702 |
Jupyter Notebook | 0.130182 | -0.054719 |
Lombok;findbugs;java | 0.684719 | -0.158233 |
MongoDB | 0.444953 | 0.004108 |
SEO;python;データ分析 | 0.028537 | 0.605926 |
SonarQube | 0.539504 | 0.035867 |
SonarQube;python | 0.084567 | 0.306657 |
Web API;python | -0.122543 | 0.046242 |
amp | 1.239664 | -0.225021 |
django | -0.056743 | -0.067848 |
django;mezzanine | 0.772658 | 0.427289 |
django;mezzanine;python | 0.721808 | 0.217287 |
django;postgresql | 0.430533 | -0.046414 |
eclipselink;java | 0.654749 | 0.496417 |
findbugs | 1.029239 | -0.032424 |
gradle | -0.012478 | 0.059121 |
jackson;java | -0.025551 | 0.104272 |
java | -0.071711 | -0.023586 |
java;pmd | 0.084224 | -0.178773 |
java;rest;wicket | 1.245231 | -0.202165 |
java;wicket | 0.449509 | -0.342901 |
javascript | 0.058178 | -0.259105 |
linux;python | 0.635474 | -0.265732 |
memcached | -0.123922 | 0.117433 |
mezzanine;python | 0.962526 | 0.040989 |
msites;資格取得 | 0.268649 | 0.302809 |
pmd | 1.027005 | 0.000706 |
postgresql | -0.079823 | 0.109810 |
python | -0.103646 | -0.064092 |
python;データ分析 | 0.398005 | -0.072223 |
spring-boot | 0.719449 | -0.038096 |
wicket | 1.263613 | -0.126698 |
データ分析 | 0.872560 | 0.177654 |
# 表側の座標を書き出す result_col = pd.DataFrame(mca_ben.fs_c(N=2)) result_col.index = list(df.columns) result_col
0 | 1 | |
---|---|---|
スポーツ&フィットネス | -0.098040 | 0.116349 |
ニュース&政治 | -0.303169 | -0.008140 |
フード&ダイニング | -0.439280 | -0.033471 |
ホーム&ガーデン | -0.162719 | 0.361364 |
メディア&エンターテイメント | -0.197931 | -0.171655 |
ライフスタイル&趣味 | 0.116859 | 0.066484 |
技術 | 0.306671 | -0.017476 |
旅行 | -0.269108 | 0.335202 |
自動車&輸送 | -0.533019 | -0.172931 |
買い物好き | 0.294562 | -0.045729 |
銀行&金融 | -0.392288 | 0.361582 |
4.可視化
matplotlib で
# 作図用ライブラリ import matplotlib.pyplot as plt import matplotlib %matplotlib inline plt.rcParams['font.family'] = 'IPAPGothic' #全体のフォントを設定 plt.rcParams["figure.figsize"] = [20, 12] # グラフのサイズを指定 plt.rcParams['font.size'] = 12 #フォントサイズを設定 default : 12 plt.rcParams['xtick.labelsize'] = 10 # 横軸のフォントサイズ plt.rcParams['ytick.labelsize'] = 10 # 縦軸のフォントサイズ matplotlib.font_manager._rebuild() import random as rnd # 図の設定(任意) plt.figure(figsize=(10,10)) plt.rcParams["font.size"] = 10
<matplotlib.figure.Figure at 0x1089c03c8>
# 表頭をプロット plt.scatter(result_col[0], result_col[1], s=100, marker="o") # ラベル付け cnt = 0 for label in list(result_col.index): r = rnd.random() * 0.1 plt.text(result_col.iloc[cnt, 0]+r, result_col.iloc[cnt, 1]+r, label) plt.plot([result_col.iloc[cnt, 0]+r, result_col.iloc[cnt, 0]], [result_col.iloc[cnt, 1]+r, result_col.iloc[cnt, 1]]) cnt += 1
# 表側をプロット plt.scatter(result_row[0], result_row[1], s=100, marker="o") # ラベル付け cnt = 0 for label in list(result_row.index): r = rnd.random() * 0.1 plt.text(result_row.iloc[cnt, 0]+r, result_row.iloc[cnt, 1]+r, label) plt.plot([result_row.iloc[cnt, 0]+r, result_row.iloc[cnt, 0]], [result_row.iloc[cnt, 1]+r, result_row.iloc[cnt, 1]]) cnt += 1
結果の 分析
点と点の
どんな
表頭の グラフ
上記、
緑色
ページの種類に 因らず、 PageView数が 多い グループに なります。 赤色
PageView数が少ない グループに なります。 自動車&輸送
系のカテゴリに 興味が ある 人は、 ページの アクセスが 偏っていました。 黄色
PageView数は少ない。 python 関係の ページに アクセス数が 多い グループのようでした。
表側の グラフ
上記丸を
赤丸
PageViewが多く、 ユーザーの アフィニティカテゴリに 偏りが 少なかった グループに なります。 緑色
PageViewはそこそこで、 ユーザーの アフィニティカテゴリに 偏りが あった グループに なります。 紫色
PageViewが少ない、 ユーザーの アフィニティカテゴリに 偏りが あった グループに なります。
アフィニティカテゴリの偏りは、 緑色の グループとは 異なります。
まとめ
Google Analytics のデータを
以下まとめます。
- mca を
使う ことで、 python での コレスポンデンス分析は 実施可能だった。 - カスタムディメンションを
予め 送信して おく ことで、 独自指標での 分析が できる。 - 図の解釈を
人間が 行わないと いけないので、 ラベルと して 実データを 表示できる インタラクティブな グラフの ほうが 分析しやすいかもしれない。
以上です。
コメント