Rを使ったアクセスデータの集計(1)
plyrとggplotを使ってます。
(*)Rを勉強し始めたら、早めにplyrとggplot2を覚えるのが吉。
見通しがよくなると思います。アクセス数値の集計というより、Rの勉強エントリ。
アクセスの基本的な数値を集計して、同じ時間軸で並べます。
以下のものを図示します。まず下準備。認証まで
コード(認証)
#RGoogleAnalyticsをファイル内にダウンロードしておいて使う。
source("/home/shirai/ga/r/RGoogleAnalytics/R/RGoogleAnalytics.R")
source("/home/shirai/ga/r/RGoogleAnalytics/R/QueryBuilder.R")
#今回の目的のggplot2、同時に plyrとreshapeもloadされる。
library(ggplot2)
#オブジェクト的な使い方? dataframeの要素に関数がある
ga <- RGoogleAnalytics()
#mail, pwを自分の設定ファイルから取得する
ac <- read.csv("/home/shirai/.gacc.csv",header=T,stringsAsFactors = F)[1,]
#認証を通す
ga$SetCredential(ac$mail,ac$pw)
コード(クエリー、集計、グラフ化)
クエリーを組み立てて、データ集計、グラフ化まで
#queryオブジェクトを作ってリクエストを作る。ビルドパターンって奴?
query <- QueryBuilder()
id <- "ga:21600568"
start.date = "2010-01-01"; end.date = "2010-12-30"
query$Init(start.date=start.date, end.date = end.date, table.id = id,
dimensions = c("ga:date"),
metrics = c("ga:visits,ga:pageviews,ga:timeOnSite"))
#データ取得 $dataにデータが、それ以外にはレコード数とかもある
output <- ga$GetReportData(query)
data.b <- output$data
#使い易いように、カラム名を加工、日付データは日付型に
names(data.b) <- sub("ga:","",names(data.b))
data.b$date <- as.Date(data.b$date, "%Y%m%d")
#滞在時間は平均滞在時間に、pageviewは平均PVへ、いるものだけ残す
data.b <- transform(data.b, avStay = (timeOnSite/visits))
data.b <- transform(data.b, avPV = (pageviews/visits))
data.b <- data.b[, c("date","visits","avStay","avPV")]
#ずるして、avPVは3000(5分)以上はNAに。 異常値なので
data.b$avStay <- ifelse(data.b$avStay > 1800, NA, data.b$avStay)
#パッケージのreshape機能。いわゆる?行持ちのデータ(日付 x データ種類 x 数値)に
data.b.molten <- melt(data.b, id="date")
head(data.b.molten) #ちょっと出力
#| date | variable | value |
#| 2010-01-01 | visits | 9 |
#| 2010-01-02 | visits | 7 |
#| 2010-01-03 | visits | 9 |
#| 2010-01-04 | visits | 20 |
#| 2010-01-05 | visits | 32 |
#| 2010-01-06 | visits | 18 |
#
#ggplotで出力(x軸にdate,y軸にvalue:数値,
p <- qplot(date, value, data=data.b.molten, geom="line", main = "基本数値")
#ここで、グループ別に図示する機能 facet_gridを使う, 縦軸スケールは個別で
p <- p + facet_grid(variable ~ ., scale="free_y")
#見た目を調整して、ファイルに出力
p <- p + opts(axis.text.x = theme_text(size=5))
p <- p + opts(strip.text.x = theme_text(size=5))
p <- p + scale_x_date(major="1 month", format="%m月")
ggsave("basic.png", height=6, width=6, dpi=96)
ぎざぎざ。データの把握がしにくいですね。

対策としては、週別のデータにすればいいけど、それだと一日の変動の様子が消えてしまう。下で移動平均を考えることによって、曜日変動の除去を考えるけど、ページ別のセッション数も見ておく。
query$Init(start.date=start.date, end.date = end.date, table.id = id,
dimensions = c("ga:date,ga:pagePath"),
metrics = c("ga:uniquePageviews"),
max.results = 10000,
start.index = 1
)
ret <- ga$GetReportData(query,max.rows=50000)
pv.data <- ret$data
names(pv.data) <- sub("ga:","",names(pv.data))
pv.data$date <- as.Date(pv.data$date, "%Y%m%d")
ret <- ddply(pv.data, .(pagePath), summarise, pagesum = sum(uniquePageviews))
top5.urls <- ret[rev(order(ret[,"pagesum"]))[1:5],1]
pv.data.top5 <- subset(pv.data, pagePath %in% top5.urls)
上位5ページを表示
p <- qplot(date, uniquePageviews, data=pv.data.top5, geom="line", log="y")
p <- p + facet_grid(pagePath~., labeller = function(l,x)substr(x,0,30),scales='free_y')
p + opts(strip.text.y = theme_text(angle=0)) + scale_x_date(major="1 month", format="%m")
ggsave("visits.png", height=5, width=6,dpi=96)

累計で表示してみる
pv.data.top5.cumsum <- ddply(pv.data.top5, .(pagePath), transform, cumsum = cumsum(uniquePageviews))
p2 <- ggplot(data=pv.data.top5.cumsum, aes(date,cumsum,color=pagePath)) + geomline()
p2 + opts(legend.position="bottom") + scalexdate(major="1 month", format="%m")
p2 + opts(legend.position="bottom", legend.box="vertical")
ggsave("cumsumvisits.png", height=5, width=6, dpi=96)

積み上げのが比較しやすいのかも
10月過ぎから勢いがついたページがある。
query$start.index(1)
query$dimensions("ga:data,ga:pagePath,ga:source")
ret <- ga$GetReportData(query,max.rows=50000)
pv.data <- ret$data
names(pv.data) <- sub("ga:","",names(pv.data))
pv.data$date <- as.Date(pv.data$date, "%Y%m%d")
pv.data.top5 <- subset(pv.data, pagePath %in% top5.urls)
pv.data.top5.yg <- subset(pv.data.top5, source %in% c("yahoo","google"))
pv.data.top5.yg.cumsum <- ddply(pv.data.top5.yg, .(pagePath), transform, cumsum = cumsum(uniquePageviews))
p2 <- ggplot(data=pv.data.top5.cumsum, aes(date,cumsum,color=source)) + geom_line()
p2 + facet_grid(pagePath~.,scale="free_y",labeller=function(l,x)substr(x,1,20)) + opts(strip.text.y = theme_text(angle=0))
ggsave("upv_yg.png", width=5,height=5,dpi=96)

あんまり関係なさそう。あとyahooから来るのは一ヶ月遅い(かった)。
曜日効果を考慮することによって、日別の変動を捉えつつ、ギザギザ問題の解消を目指します。Rのdocompose関数を使って、曜日効果とトレンドを分離します。
基本のアイデアは、観測値を (季節分+トレンド+誤差) と考えて、7日間の移動平均をとれば、曜日効果はキャンセルアウトされる。
んで、誤差もキャンセルアウトとまずは考える。なので、移動平均はトレンドの値と考えられる。曜日効果分は、曜日ごとの平均を出して、全体の平均から引いて出す。
で、実測値から、トレンドと曜日分を引いたのが誤差分。
こんな考えらしい。細かくは曜日分や誤差分にトレンドを入れたりするみたいだけど、decompose関数は普通にそのまんまみたい。で、decompose関数でいきます。
#前のデータをそのままで visitsのdecomposeする。tsオブジェクトにする
#曜日効果なので、7日間を指定,日付に関しては無視
visits.c <- ts(data.b$visits, freq=7)
#decompose関数はそのまんま、入れるだけ
visits.d <- decompose(visits.c)
#ggplotで出力するので、data.frameに戻す
visits.d1 <- as.data.frame(visits.d[c(2,1,3)])
#NAが初めと終わりに3日づつでるので、除去
visits.d2 <- visits.d1[c(-1,-2,-3,-362,-363,-364),]
#日付を再代入
visits.d2$date <- seq(as.Date("2010-01-04"),as.Date("2010-12-27"),by=1)
#元データと合体して、列順を入れ替え
visits.d2$observe <- data.b$visits[c(-1,-2,-3,-362,-363,-364)]
visits.d2 <- visits.d2[, c(5,1,2,3,4)]
#meltさせて行持ちにして、グラフ
p <- qplot(date,value, data = melt.data.frame(visits.d2, id.vars="date"), geom="line")
p + facet_grid(variable~., scales='free_y')
ggsave("decompose.png", width=6, height=6, dpi=96)
セッション数の曜日別差異(合計は 0 )| 月 | 火 | 水 | 木 | 金 | 土 | 日 | 計 |
| 9.7 | 14.3 | 16.7 | 15.4 | 11.9 | -32.8 | -35.2 | 0. |
とりあえず、トレンドは見える感じです。
週刊平均値をプロットするよりは、ダイナミック。日別よりは見やすい。
エンゲージメントの測定として、滞在時間を対象にします
library(stringr)
query$Init(start.date=start.date, end.date = end.date, table.id = id,
dimensions = c("ga:date"),
metrics = c("ga:visits,ga:pageviews,ga:timeOnSite"))
d1 <- ga$GetReportData(query)
d2 <- d1$data
names(d2) <- str_replace(names(d2), "ga:", "")
d2$date <- as.Date(d2$date,"%Y%m%d")
d2 <- transform(d2, avTime = timeOnSite/visits)
#4月以降のデータにする(1−3月は計測方法が違うので)
d3 <- subset(d2, date >= as.Date("2010-04-01"))
dc.avtime <- decompose(ts(d3$avTime, f=7))
dc.visits <- decompose(ts(d3$visits, f=7))
#曜日別の滞在時間(結果画面で)
print(dc.visits$figure)
print(dc.avtime$figure)
#trendDataだけ持ってくる
d.bind <- data.frame(
visits = dc.visits$trend,
avTime = dc.avtime$trend,
date = seq(as.Date("2010-04-01"),as.Date("2010-12-30"),by=1))
#平均化によるデータのない部分を除去
d.bind2 <- d.bind[c(-1,-2,-3, -272,-273,-274),]
#グラフ化
p <- qplot(date,value, data=melt(d.bind2, id.var="date"),geom="line")
p <- p + facet_grid(variable~.,scale="free_y")
p <- p + scale_x_date(major="1 month", format="%m")
p + opts(ylab="上:セッション数 下:平均滞在時間(秒)
ggsave("trend_visits_timeonsite.png",height=6,width=6,dpi=96)
| 月 | 火 | 水 | 木 | 金 | 土 | 日 |
| 9.7 | 14.3 | 16.7 | 15.4 | 11.9 | -32.8 | -35.2 |
滞在時間(単位は秒数)
| 月曜 | 火曜 | 水曜 | 木曜 | 金曜 | 土曜 | 日曜 |
| 45.8 | 4.9 | 53.9 | 20.3 | 20.3 | (-)46.0 | (-)99.2 |
トレンドデータ(曜日効果除去後のもの)
なんかこれだけでは、よくわからんかも。 曜日別というより、他のセグメントを当たる必要がある。
日別の集計値としての、平均滞在時間ではなんとも言いがたい。
実は、GAはセッション滞在時間もセグメント情報として持ってる。
なので、他のセグメント情報と掛け合わせで、滞在時間によるセッションの分布がだせる。
指標側では平均しか見えないけど、こちらは分布まで見える。
ここでは、月別とメディア別とランディングページ別を見てみる
ただ、上でも書きましたが、滞在時間データそのものの信頼性には疑問はあります。
僕自身のRの演習が主目的になっちゃってます。
月別のセグメントも入れて、データを取得。滞在時間は ga:visitLength
#クエリーを組み立てる。その前は、前のコードから続いてるものがある
query$dimensions("ga:visitLength,ga:month")
query$metrics("ga:visits")
#前と同じくmax.rowsは10000に増やす
output <- ga$GetReportData(query,max.rows=10000)
output$total.result #=>5708
d1 <- output$data
names(d1) <- sub("ga:","",names(d1))
#バープロットは、通常はcountデータをとるけど、weight指定で合計もいける
p <- qplot(visitLength, data=d1, geom="bar", weight=visits, log="y")
p + facet_grid(month~.)
なんかみずらい、、、右側に月の表示。左側にセッション数、x軸は滞在時間だけど、、謎グラフになった。
あと滞在時間が、飛び飛びになってるが怪しいし、0秒がどこだか不明だし、、
全然だめ、、

横軸を詰める。facet_wrapで表示。
p <- qplot(visitLength, data=d1, geom="bar", weight=visits, log="xy") p + facet_wrap(~month)
こちらのがみやすい。

#http://tolstoy.newcastle.edu.au/R/e2/help/06/10/2836.html d2 <- data.frame(lapply(d1, rep, d1$visits)[1:2]) d2$visitLength <- as.numeric(d2$visitLength) qplot(x=d2$visitLength, data=d2, geom="density",binwidth=10) + facet_grid(month ~ .) ggsave(dpi=96,width=6,height=6,file="hist_month_visits.png")

なんか形は違ってきましたね、、、くらいか。
#中央値と平均値を求める
stt <- ddply(d2, .(month), function(x) data.frame(median=median(x$visitLength),
mean=mean(x$visitLength)))
#以下、グラフ出力今回は、barplotとdensityを合わせる
p <- ggplot(d2, aes(visitLength)) + geom_histogram(aes(y=..density..)) + geom_density()
p <- p + facet_grid(month ~ ., labeller=function(l,x)paste(x,"月",sep=""))
p <- p + opts(strip.text.y = theme_text(hjust=1, angle=0))
p <- p + geom_vline(data=stt, aes(xintercept=stt$median), color=I("red"))
p <- p + geom_vline(data=stt, aes(xintercept=stt$mean), color=I("green"))
ggsave("hist_month_visits2.png",width=6,height=10,dpi=96)
レジェンドが引けなかった、、、緑が平均値。赤が中央値。
複数のgeomがある場合のlegendの対象指定はどうなのだろう?

時間変化とは別にメディア別もやってみる。
query$dimensions("ga:visitLength,ga:medium")
query$metrics("ga:visits")
output.m <- ga$GetReportData(query,max.rows=10000)
d.m <- output.m$data
colnames(d.m) <- sub("ga:","", colnames(d.m))
#organic, referral, (none) に絞る
d.m1 <- subset(d.m, medium %in% c("organic","referral","(none)"))
#visits単位のレコードに
d.m2 <- data.frame(lapply(d.m1[1:2], rep, d.m1$visits))
d.m2$visitLength <- as.numeric(d.m2$visitLength)
p <- ggplot(d.m2, aes(x=medium, y=visitLength)) + stat_boxplot()
p <- p + coord_flip() + ylab("滞在時間(秒)")
ggsave("boxplot_medium_visits.png",width=6,height=6,dpi=96)

分類が大まかすぎる。下でキーワード別をやる