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

廿TT

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

直帰率と新規率の相関を調べて新規訪問者が回遊しやすいサイトになっているかチェックする

R 時系列 Google アナリティクス Graphviz

目的

Webアクセス解析データの可視化を以下の二つに大別するとすると、

  1. 定点観測型:重要な指標の傾向を整理して把握する
  2. 課題発見型:データをある側面から眺めて新たな仮説を立てる

今回やろうとしているのは後者です。

直帰率や新規率(新規訪問の割合)はアクセス解析の分野でよく使われる指標です。

直帰率と新規率の相関関係を調べることで、サイトが新規訪問者(一見さん)にとって回遊しやすいものになっているかどうかチェックしてみます。

新規ユーザーの獲得を見込んで広告やWEO対策等の集客を行う場合、またはサイト改善やコンテンツ施策を打つ場合の参考になればと思います。

R + Google アナリティクスによる実践

当ブログ内のページ、

では、関連エントリーをいっぱいあげています。

この記事一本だけで完結するものというよりは、いろんな主張が派生した記事です。

このページを“「A/Bテスト」LP”と呼ぶことにします。

この記事を入り口にして訪問(この記事にランディング)したケースで直帰率と新規率の関係をしらべてみます。

比較対象はこのブログ全体の直帰率、新規率とします。

f:id:abrahamcow:20141229095806p:plain

f:id:abrahamcow:20141229100018p:plain

サイト全体では各月の新規率と直帰率に正の相関が見られますが、「A/Bテスト」LP では負の相関が出ています。

  • 正の相関:新規率が高いときに直帰率が上がる
    • サイト全体では一見さんが回遊しにくい
  • 負の相関:新規率が高いときに直帰率が下がる
    • 「A/Bテスト」LP では比較的一見さんが回遊しやすい

ここから「A/Bテスト」LP では、関連エントリーをいっぱいあげたのが良かったのかな? 単に長い記事だからユーザーが迷ってるのかな? などの仮説が立てられます。

この事実を参考に、サイトのナビゲーションを変えてみたり、関連記事を追加してみたりして、回遊傾向をウォッチしていけば、サイト改善に役立ちます。

このグラフを書いた R のコードは以下の通りです。

R から APIGoogle アナリティクスのデータを引っ張ってくる方法は、
RGoogleAnalytics をいじっている - 廿TT
を参照して下さい。


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

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

#データ取得
ga <- RGoogleAnalytics()
ga.profiles <- ga$GetProfileData(access_token)

sta <- "2014-01-01"
en <- "2014-12-31"

query$Init(start.date = sta,
           end.date = en,
           dimensions = "ga:yearMonth,ga:userType",  
           metrics = "ga:sessions,ga:bounces", 
           table.id = paste("ga:",ga.profiles$id[1],sep="",collapse=","),
#           filter = "",
           access_token=access_token)

dat_total <- ga$GetReportData(query) #サイト全体

query$Init(start.date = sta,
           end.date = en,
           dimensions = "ga:yearMonth,ga:userType,ga:landingPagePath",  
           metrics = "ga:sessions,ga:bounces", 
           table.id = paste("ga:",ga.profiles$id[1],sep="",collapse=","),
           filter = "ga:landingPagePath==/entry/2014/01/05/081748",
#filter のところだけ変えればあなたのサイトでもまったく同じコードが(たぶん)動きます。
           access_token=access_token)

dat_LP<- ga$GetReportData(query)
###
##データ整形
#total
ss_total <- tapply(dat_total$sessions,dat_total$yearMonth,sum)
newss_total <- dat_total[dat_total$userType == "New Visitor", 3]
bounces_total <- tapply(dat_total$bounces,dat_total$yearMonth,sum)

#LP focus
ss_LP <- tapply(dat_LP$sessions,dat_LP$yearMonth,sum)
newss_LP <- dat_total[dat_LP$userType == "New Visitor", 3]
bounces_LP <- tapply(dat_LP$bounces,dat_LP$yearMonth,sum)

t1 <- seq.Date(from=as.Date(sta),to=as.Date(en), by="1 month")

df_total <- data.frame(yearMonth=t1,
                  newRate =newss_total/ss_total,
                  bounceRate =bounces_total/ss_total)

df_LP <- data.frame(yearMonth=t1,
                       newRate =newss_LP/ss_LP,
                       bounceRate =bounces_LP/ss_LP)

tmp <-as.character(t1)
ym <- paste(substr(tmp,1,4),substr(tmp,6,7),sep="/")
##
#相関係数
rho1 <-cor(df_total$newRate,df_total$bounceRate)
rho2 <-cor(df_LP$newRate,df_LP$bounceRate)

#cor.test(df_total$newRate,df_total$bounceRate)
#cor.test(df_LP$newRate,df_LP$bounceRate)

#####
#(確率)集中楕円の作成
library(ellipse)
df_el1 <- ellipse(rho1, #傾き
                 scale=c(sd(df_total$newRate),sd(df_total$bounceRate)), #長軸と短軸
                 centre=c(mean(df_total$newRate),mean(df_total$bounceRate))) #中心
df_el1 <- as.data.frame(df_el1)

df_el2 <- ellipse(rho2, #傾き
                  scale=c(sd(df_LP$newRate),sd(df_LP$bounceRate)), #長軸と短軸
                  centre=c(mean(df_LP$newRate),mean(df_LP$bounceRate))) #中心
df_el2 <- as.data.frame(df_el2)
######
#プロット
library(ggplot2)
library(scales)
p1 <- ggplot(df_total)
p2 <- ggplot(df_LP)

theme_set(theme_bw(15,"HiraKakuPro-W3"))

p1 + geom_point(aes(x=newRate,y=bounceRate),colour="grey80",size=5)+
  geom_text(aes(x=newRate,y=bounceRate),label=ym)+
  labs(x="新規率", y="直帰率",
       title=paste("サイト全体(相関係数 = ",round(rho1,2),")"), sep="")+
  geom_path(data=df_el1, aes(x=x, y=y), size=1, linetype=2)+
  scale_y_continuous(labels = percent)+ scale_x_continuous(labels = percent)

p2 + geom_point(aes(x=newRate,y=bounceRate),colour="grey80",size=5)+
  geom_text(aes(x=newRate,y=bounceRate),label=ym)+
  labs(x="新規率", y="直帰率",
       title=paste("「A/Bテスト」LP(相関係数 = ",round(rho2,2),")"), sep="")+
  geom_path(data=df_el2, aes(x=x, y=y), size=1, linetype=2)+
  scale_y_continuous(labels = percent)+ scale_x_continuous(labels = percent)

散布図は意外と伝わらない

散布図はある程度統計慣れしている人にはごくあたり前のものですが、非専門家には意外なくらい伝わりません。

変数どうしの関係を見るものとはあまり捉えられず、一つ一つの点に注目されがちです。

(以前にも愚痴ったことがあります。仮説:できるビジネスマンにとって、散布図は2×2の表なのだ。 - 廿TT

そのため、上記の図では集中楕円をつけ、一つ一つの点が何年何月のものかラベル付けをしております。

ドキュメンテーション、プレゼンテーションにあたっては下のスライドのようにしつこいくらい説明することをおすすめします。

また 問題解決手法>QC七つ道具>散布図 に例示されているような表と、

  • 相関係数の絶対値(目安)
    • 0.7以上:強い相関
    • 0.4から0.7:中程度の相関
    • 0.2から0.4:弱い相関
    • 0.2以下:無視できるくらいの相関

というような箇条書きも、合わせてつけるとよいと思います。
(ただしここでいう「強い相関」「弱い相関」は目安であり「相関係数の有意性」とは別の概念ですので注意してください。)

折れ線グラフ矢印問題

f:id:abrahamcow:20141229103242p:plain

散布図があまりに伝わらなかったので前職では折れ線グラフに矢印をつけたりして説明していましたが、本当はそんなことしたくなかった。

下図は乱数でつくった無相関(相関係数=0.036)な系列の折れ線グラフですが、人間の手で折れ線グラフに矢印をつけたりしたら、たまたまギザギザが一致してるところを誇張して、相関があるかのように見せかけることもできてしまいます。

f:id:abrahamcow:20141229111423p:plain

これは極端な例なので「おれはそんなことしないし、そんなのにだまされない」と思うかもしれませんが、統計グラフに人間の手でイラストを描き込んでしまうと、先入観などでついうっかりこれに近いことをやってしまいがちです。

散布図を使えばそのような誤りは回避できます。

f:id:abrahamcow:20141229112553p:plain

現実問題、例えばニューラルネットとか機械学習の高度な手法を使う場合は、分析者がお客さん/雇い主にやってることのすべてを理解してもらうのは無理だと感じます。

そのような場合はイラストや例え話で説明して、信用取引でやっていくしかないでしょう。

しかし、散布図や、ヒストグラム、箱ひげ図くらいはわかってもらうよう努力すべきじゃないかなーと思います。

また非専門家もそういった基本的な記述統計を知っておくことは、学習コストを差し引いても十分なメリットがある、とぼくは考えます。

追記:最近は高校数学でも散布図や相関係数教わるみたいです。すばらしい。
高等学校数学B 統計とコンピューター - Wikibooks

#上図のコード
#セッション数をランダムに(ポアソン到着)
sim_ss <- sapply(1:100,function(i){rpois(1, 10000)})

#二項分布
sim_new <- sapply(1:100, function(i){rbinom(1, size=sim_ss[i], prob=0.8)}) #新規訪問
sim_bounce <- sapply(1:100, function(i){rbinom(1, size=sim_ss[i], prob=0.85)}) #直帰数

#nihongo()
plot.ts(sim_new/sim_ss,type="l", ylim=c(0.75,0.9), col="blue3", lwd=2, ylab="")
lines(sim_bounce/sim_ss, col="red3", lwd=2)
legend("topleft",legend=c("直帰率","新規率"),lty=1, lwd=2,col=c("red3","blue3"))

cor(sim_new/sim_ss,sim_bounce/sim_ss)
#[1] 0.03565328

plot(sim_new/sim_ss,sim_bounce/sim_ss, xlab="新規率", ylab="直帰率")


ちなみに plot.ts 関数を使うと番号(順序)付きの散布図を描画してくれます。
f:id:abrahamcow:20141229113723p:plain

plot.ts(sim_new/sim_ss,sim_bounce/sim_ss, xlab="新規率", ylab="直帰率")

見せかけの回帰、擬似相関のシミュレーション

「ちょっとまって。散布図で相関みるっていってるけど、時系列データの相関ってそんな簡単に解釈していいの? 見せかけの回帰とかあるんじゃないの?」
と思った方、お待たせしました。その話をします。


時系列データの相関係数はあてにならない……のか? 教えて下さい - 廿TT

Web解析データの直帰率と新規率は時系列データですが、見せかけの回帰は起こりません。

  • 直帰率 = 直帰数 / セッション数
  • 新規率 = 新規訪問数 / セッション数

で計算されます。

見せかけの回帰はランダムウォーク系列どうしで起こります。

ランダムウォーク - Wikipedia

ランダムウォークとは、大雑把にいえば
「各時点の値が、一歩前の値に対して数字を足したり引いたりすることで決まる系列」
です。

直帰率、新規率は背後にある確率の推定値になっていますので、ランダムウォーク系列ではありません。

シミュレーションを行ってみます。

セッション数(サイトへの訪問数)にポアソン分布を仮定します。ポアソン分布はカウンティングデータの分布の一番シンプルなものです。

直帰数、新規数に互いに相異なる二項分布を仮定します。訪問にしめる直帰、訪問にしめる新規が確率一定でランダムに生じるとみなすということです。

1万回のシミュレーションを回した結果が以下の通りです。

f:id:abrahamcow:20141229122921p:plain

このように直帰率と新規率の相関係数は 0 を中心に(正規?)分布しています。

ただし直帰数、新規訪問数の相関係数は下図のように分布します。

f:id:abrahamcow:20141229123247p:plain

0.8から0.85の間にモード(最頻値 - Wikipedia)が来ており、互いに相異なる二項分布で生成されたにもかかわらず、非常に高い相関を示します。

これは擬似相関(見せかけの相関)と見せかけの回帰によるものです。

直帰数、新規訪問数の増加(減少)の背後にはともにセッション数の増加(減少)がありますので、直帰数と新規訪問数にある相関は擬似相関です。

また、直帰数、新規訪問数の推移はランダムウォークですので、見せかけの回帰が起こります。

f:id:abrahamcow:20141229123505p:plain

//上図のコード(dot言語)
graph {
	rankdir=LR; 
	subgraph cluster_1 { 
		style=filled;
		color=lightgrey;
		node [style=filled,color=white];
		label="潜在変数"; 
		"セッション数"
	} 

	subgraph cluster_2 {
		style=filled;
		color=white;
		"直帰数";
		"新規訪問数"; 
	}

	"セッション数" -- "直帰数"[label="相関"]; 
	"セッション数"  -- "新規訪問数"[label="相関"]; 
	"直帰数" -- "新規訪問数"[label="擬似相関"];
}
#シミュレーションのコード
simrho1<-numeric(10000)
simrho2<-numeric(10000)

for(i in 1:10000){
sim_ss <- sapply(1:100,function(i){rpois(1, 10000)})
sim_new <- sapply(1:100, function(i){rbinom(1, size=sim_ss[i], prob=0.8)})
sim_bounce <- sapply(1:100, function(i){rbinom(1, size=sim_ss[i], prob=0.85)})
simrho1[i] <- cor(sim_new,sim_bounce)
simrho2[i] <- cor(sim_new/sim_ss,sim_bounce/sim_ss)
}
nihongo()
hist(rho1,main="新規訪問数と直帰数",xlab="相関係数",ylab="頻度")
hist(rho2,main="新規率と直帰率",xlab="相関係数",ylab="頻度")

参考文献(本文中に言及のないもの)

統計グラフのウラ・オモテ―初歩から学ぶ、グラフの「読み書き」 (ブルーバックス)

統計グラフのウラ・オモテ―初歩から学ぶ、グラフの「読み書き」 (ブルーバックス)

(pp.100-104 に集中楕円の説明がある)

追記: ggplot2 1.0.0 以降なら stat_ellipse 一発で簡単に確率楕円を書けるそうです。今後はこちらを使おうかと思います。

R {ggplot2} の散布図に凸包 / 確率楕円を描きたい - StatsFragments

関連エントリ


エンゲージメントを測る指標はエンゲージメント「率」だけじゃない。やみくもに割り算値をKPIにするのはよくない。 - 廿TT


Google アナリティクスデータKPI設計のための可視化。散布図行列で定点観測するエンゲージメント指標を絞り込む。 - 廿TT