読者です 読者をやめる 読者になる 読者になる

廿TT

譬如水怙牛過窓櫺 頭角四蹄都過了 因甚麼尾巴過不得

マーケットバスケット分析による検索キーワードのグルーピングと視覚化

Google アナリティクス R SEO

検索キーワードを分類して整理したい

Google アナリティクスの検索キーワードレポート、そのまま全部見るのは大変なので、なんらかの方法で要約したい。

f:id:abrahamcow:20141019133005p:plain
セッション上位10件を見ると、「割れ窓理論」系のワードと「cd 収納術」系のワードに分けられそうな感じがする。

検索キーワードをどう分類するか、いろいろな方法が考えられると思う。
(正直、〇〇分析とかやるよりも grep 文字列マッチングで地道に集計したほうが手堅く有益な気がしなくもないが…… → ブランド指名系検索キーワードの構成比を図示(R + Google アナリティクス) - 廿TT


ここではアソシエーション分析(マーケットバスケット分析)をやってみる。

マーケットバスケット分析とは、よくある「この商品を買った人はこんな商品も買っています」というあれだ。

この分析手法は、ある商品とある商品が買い物カゴに入る同時確率を推定して、設定した閾値を超えるものをすべて列挙する。

早速、RGoogleAnalytics でデータを抽出する。
RGoogleAnalytics をいじっている - 廿TT 参照)

library("RGoogleAnalytics")
query <- QueryBuilder()
access_token <- query$authorize()

アクセストークンをコピペ。

ga <- RGoogleAnalytics()
ga.profiles <- ga$GetProfileData(access_token)

query$Init(start.date = "2014-09-01",
           end.date = "2014-09-30",
           dimensions = "ga:keyword,ga:landingPagePath",
           metrics = "ga:sessions,ga:bounces,ga:users,ga:goal2Completions,ga:pageviews,ga:sessionDuration",
           sort = "-ga:sessions", #降順
           #filters="",
           segment="gaid::-5", #自然検索トラフィック
           #max.results = ,
           table.id = paste("ga:",ga.profiles$id[1],sep="",collapse=","),
           access_token=access_token)

gakw <- ga$GetReportData(query)

Google アナリティクス API のディメンション、指標は、

を参照。

次に、検索語を MeCab形態素解析する。
形態素とはなにか、ということについてはここでは深入りしない。
文章を単語レベルに分解したものだと思ってほしい。

Mecab、RMeCab はそれぞれ下記からダウンロードできる。

library(RMeCab)
library(dplyr)

sub1 <- filter(gakw,keyword != "(not provided)")
sub2 <- filter(gakw,keyword == "(not provided)")
  #"(not provided)"は「検索語句不明」という意味なので、どけておく
tmp1 <- sapply(sub1$keyword,RMeCabC)
kwlist1 <- sapply(tmp1,unlist)

RMeCabC によってバラバラにされた検索語句は、こんな感じになる。

> kwlist1[1:3]
$割れ窓理論
  動詞   名詞   名詞 
"割れ"   "窓" "理論" 

$`cd 収納術`
  名詞   名詞   名詞 
  "cd" "収納"   "術" 

$`cd 整理`
  名詞   名詞 
  "cd" "整理" 

これをアソシエーション分析にかけて、同時にあらわれる語句を調べる。
arules パッケージを使うと、かんたんにこれができる。

library(arules)
kw1_t<-as(kwlist1[duplicated(kwlist1)],"transactions")
kw1_a <- apriori(kw1_t,parameter = list(
  supp = 0.02, conf = 0.001,target = "rules"))

結果、こんな風にアソシエーションルールが抽出される。

> inspect(head(SORT(kw1_a, by = "lift"),10))
   lhs         rhs         support confidence lift
1  {割り算} => {kpi}    0.04347826          1   23
2  {kpi}    => {割り算} 0.04347826          1   23
3  {sir}    => {モデル} 0.04347826          1   23
4  {モデル} => {sir}    0.04347826          1   23
5  {ホップ} => {ヒップ} 0.04347826          1   23
6  {ヒップ} => {ホップ} 0.04347826          1   23
7  {}     => {分析}   0.04347826          1   23
8  {分析}   => {}     0.04347826          1   23
9  {}     => {検定}   0.04347826          1   23
10 {検定}   => {}     0.04347826          1   23
 警告メッセージ: 
In .local(x, ...) : arules: SORT is deprecated use sort instead.

検索クエリのかけあわせ状況がわかる。
しかし、これでもやはり一個ずつ見ていくのは大変なので、arulesViz パッケージを用いてグラフ状に図示してみる。
ノードとエッジのあるグラフ(グラフ (データ構造) - Wikipedia)も統計グラフも「グラフ」という言葉で呼ぶのでややこしいが、ノードとエッジのあるグラフによる統計グラフだ。

library(arulesViz)
library(igraph)
plot(kw1_a,method="graph",interactive=TRUE, #
     control=list(type="items",layout=layout.fruchterman.reingold,cex=2),margin=-0.1)
#レイアウトはfruchterman.reingoldがいいようだ(経験則)

f:id:abrahamcow:20141019135510p:plain
ポストスクリプトファイルに書き出すと日本語が潰れてしまうみたいなので、やや不格好ながらスクリーンショットで撮った。

円の大きさが「支持度(support)」に対応している。
支持度というのは、同時確率の推定値のことだ。

これで、共起関係のある語句とない語句が概観できるだろう。

検索キーワードグループごとの集計

さて、検索キーワードのグループがなんとなく求まったので、続いてグループごとに集計してみよう。

こんな感じの使い捨て関数を書いた。

shukei <-function(g1, gname="group"){
  flag1 <- sapply(kwlist1, function(x){any(x %in% g1)})
  df1 <- data.frame(
    group = apply(sub1[flag1,3:8],2,sum),
    other = apply(sub1[!flag1,3:8],2,sum),
    not_provided = apply(sub2[,3:8],2,sum)
  )
  df1 = t(df1)
  df1 = as.data.frame(df1)
  df2 <- with(df1, data.frame(
    SS = sessions,
    bouncesRate = bounces/sessions,
    PVparSS=pageviews/sessions,
    avgTimeOnSite =sessionDuration/sessions,
    SSparUU= sessions/users,
    CVR = goal2Completions/sessions))
  rownames(df1)[1] <- gname
  rownames(df2) <- rownames(df1)
  tab1 <- table(sub1$landingPagePath[flag1])
  list(df1,df2, tab1)
}

最初の図の左下のかたまり

f:id:abrahamcow:20141019150743p:plain

を参考に、「割れ」「窓」「ニューヨーク」を含む検索クエリを「窓系」キーワードと呼ぶことにして、ひとつのグループにして集計(合算)してみる

g1 <- c("割れ", "窓", "ニューヨーク")
res1 <- shukei(g1,"窓系")

こんな感じだ。

> res1
[[1]]
             sessions bounces users goal2Completions pageviews sessionDuration
窓系              162     142   156                2       193            4191
other             825     716   805                8       981           23843
not_provided     2094    1752  1917               55      2713          117914

[[2]]
               SS bouncesRate  PVparSS avgTimeOnSite  SSparUU        CVR
窓系          162   0.8765432 1.191358      25.87037 1.038462 0.01234568
other         825   0.8678788 1.189091      28.90061 1.024845 0.00969697
not_provided 2094   0.8366762 1.295606      56.31041 1.092332 0.02626552

[[3]]

     /entry/2014/01/25/232720 /entry/2014/01/25/232720?vm=r      /entry/2014/07/03/032505 
                           45                             1                             1 

一つ目のテーブルは、sessions, bounces, users, ... の数字を「窓系」と "other" =その他、"not_provided" =検索キーワード不明、に分けて単純に足し合わせたもの。

二つ目のテーブルは上で足し合わせた数字どうしを割り算して、「エンゲージメント」と呼ばれるような指標を計算したもの。

三つ目のテーブルは、「窓系」検索キーワードのランディングページを集計したものだ。
「窓系」ワードでのバリエーションのうち、45件が、/entry/2014/01/25/232720 に、1件が /entry/2014/01/25/232720?vm=r に、/entry/2014/07/03/032505 にランディングしている。

「エンゲージメント」関連の数値を図にするとこうなる。

library(reshape2)
library(ggplot2)

theme_set(theme_bw(base_size=15, base_family="HiraKakuProN-W3")) 
#日本語フォントを指定
df1 =melt(t(res1[[2]]),id="SS")
ggplot(df1,aes(y=Var2,x=value)) +
  geom_point(size=3) + 
  facet_wrap(~Var1,scales="free_x", ncol=6)+ #単位が違うのでx軸はfree
  labs(x="",y="") 

f:id:abrahamcow:20141019140824p:plain

同様に、ヒップホップ系、cd系なんかも可視化する。
f:id:abrahamcow:20141019150805p:plain

g2 <- c("ラップ", "ヒップ", "ホップ")
res2 <- shukei(g2,"ヒップホップ系")
df2 =melt(t(res2[[2]]),id="SS")
ggplot(df2,aes(y=Var2,x=value)) +
  geom_point(size=3) + 
  facet_wrap(~Var1,scales="free_x", ncol=6)+ 
  labs(x="",y="") 

g3 <- c("cd", "dvd", "整理", "収納")
res3 <- shukei(g3,"cd系")
df3 =melt(t(res3[[2]]),id="SS")
ggplot(df3,aes(y=Var2,x=value)) +
  geom_point(size=3) +
  facet_wrap(~Var1,scales="free_x", ncol=6)+ 
  labs(x="",y="")

f:id:abrahamcow:20141019141057p:plain

f:id:abrahamcow:20141019141108p:plain

SS はセッション、bouncesRate は直帰率、 PVparSS はページビュー/セッション(一訪問あたりの閲覧ページ数)、avgTimeOnSite は平均滞在時間、SSparUU はセッション/ユーザー数(一ユーザーあたりの訪問回数)、 CVR はコンバージョンレート(コンバージョン数/セッション数)だ。

web 解析的に「良い」検索キーワードグループは、「窓系」ワードだ。

なぜかというと、「窓系」ワードは "other" =その他ワードに比べて、CVRやページビュー/セッションが高いからだ。

翻って、「ヒップホップ系」、「cd系」ワードは "other" =その他の有象無象ワードに比べて、一訪問あたりの閲覧ページ数や平均滞在時間が軒並み小さい。

その他ワードで訪問した場合のほうが、サイトへの関心が高いことが伺える。

これらに対しては対策の余地あり、ということになる。

こういった場合、まず最初に気にするべきは、検索キーワードとランディングページ(閲覧開始ページ)がマッチしているか、ということだ。

Web 解析的によい数字が出ていた、「窓系」ワードのランディングページ、
http://abrahamcow.hatenablog.com/entry/2014/01/25/232720
を参考に、下記のページを見直してみましょう、ということになる。

> res2[3]
[[1]]

/entry/2014/03/05/001432 
                      59 

> res3[3]
[[1]]

/entry/2014/02/06/003147 
                      49 

例えば、「cd系の記事をもっと書いて、関連するページとしてリンクを張って、もっとサイト内を循環してもらおう」とか、そういうことを考える。

あるいはec系のサイトだったら、良い数値が出てた「窓系」のワードの掛け合わせでリスティング広告を打つとか、そういうことも考えられる。

アマゾンアフィリエイトのコーナー

検索にガンガンヒットさせるSEOの教科書

検索にガンガンヒットさせるSEOの教科書

いい本です。SEOなんて検索エンジンSEO業者がイタチごっこしてるだけ、みたいな意見がよくある。事実、そういう面も多いが、この本がやってることの基本はサイト内の情報を機械可読な形で整理整頓すること。


Rによるテキストマイニング入門

Rによるテキストマイニング入門

テキストマイニング入門」というより「RMeCabでできること」一覧といった感じの本。RMeCabのリファレンスが手元にほしい方のみ購入するといいと思う。


Rによるデータサイエンス-データ解析の基礎から最新手法まで

Rによるデータサイエンス-データ解析の基礎から最新手法まで

R でできる代表的な分析・パッケージを網羅的に紹介している。
今回のアソシエーション分析などもこれを参考にした。
この本を買うつもりのない方も、R ユーザーなら金先生のページ(JIN'S PAGE)はブックマークに入れておいて損はない。


言語処理のための機械学習入門 (自然言語処理シリーズ)

言語処理のための機械学習入門 (自然言語処理シリーズ)

この本は今回の記事に直接は関係していないが、「言語処理のための」というより、「機械学習入門」の最初の一冊としておすすめ。K-means や EM アルゴリズム、ナイーブベイズSVM など、機械学習の代表的な手法について一通りの理解ができるよう、親切に例題付きで書いてある。
数学的な議論の展開とかが、抽象的になりすぎず、かつていねい。