CARTで結論を絞る

cart

Google Analyticsでエクセルなどでデータをいじる場合、 僕がよくやったのは、ディメンジョンを多めにセットしておいて(データ行は一万行を超えないようにする、indexするのが面倒な場合)、データを得た後、エクセルのピボットで軸を入れ替えながら、突出したポイントを探すことでした。エクセル2010でスライサー使えば、アドバンスセグメント+カスタムレポートという形が簡単かつ柔軟に実現できる。

それで、データに浸かっていじってれば、そのうち、イメージができることはできるけど、時間かかります。 そういう場合に、よく言われるのは、予め仮説を持って、その検証のみに時間を使う。サイトに対する今までの見識を持って仮説を出し、データでの裏付けを探す。見識がなければ、世間で一般的に使われる指標を一般的なディメンジョンで分類するという形になると思います。または、テストできる環境を整え、指標を対比する。。。などだったと思います。

で、それとは違うアプローチとして、データマイニングがあります。仮説なしで、結論みたい?なものをだしてもらう。ほとんどは凡庸な結論だけど、時々、宝ものが出てくるかもしれない。。。冒頭の本は、そのアプローチ、データマイニングのやり方を説明してくれてます。この本は、数式抜きでやり方を丁寧に説明してくれてるし、書き方からも読者に理解させようとする気が伝わってきます。とても良いです。


で、その中の分析木をやってみました。ただ、分析木は、マイニングというより、シンプルな結論の提示という気がします。なので、この本にあるように、mvpartというライブラリを使います。コードは最後に提示します。

冒頭のグラフが図で、結果そのものが下記。
split:分岐基準、n:その基準での観察数(セッション数), loss: n回は間違え分類した。(分類名が続いてる。今回なら、bounce, nobounces)

========================================================

n= 226

node), split, n, loss, yval, (yprob)
* denotes terminal node

1) root 226 79 bounce (0.65044 0.34956)
2) visitCount>=6.5 22  1 bounce (0.95455 0.04545) *
3) visitCount< 6.5 204 78 bounce (0.61765 0.38235)
6) landingPagePath=/cookie/utma,2010-03-11-16-53,igation_analysis,ng-apis/gaq-push 132 41 bounce (0.68939 0.31061)
12) visitCount< 3.5 130 39 bounce (0.70000 0.30000) *
13) visitCount>=3.5 2  0 nobounce (0.00000 1.00000) *
7) landingPagePath=/ 72 35 nobounce (0.48611 0.51389)
14) medium=(none),organic 48 22 bounce (0.54167 0.45833)
28) visitCount< 4.5 44 19 bounce (0.56818 0.43182) *
29) visitCount>=4.5 4  1 nobounce (0.25000 0.75000) *
15) medium=referral 24  9 nobounce (0.37500 0.62500)
30) visitCount>=2.5 2  0 bounce (1.00000 0.00000) *
31) visitCount< 2.5 22  7 nobounce (0.31818 0.68182) *

=========================================================

226セッションで79が直帰と間違え分類で、
訪問回数で6,5で分かれて(22(間違え1) – 204(間違え78))
ランディングページで分かれて、(トップページと、それ以外, 132 – 72)
その子供たちは、また訪問回数やら、メディアやらで分岐される。

数字自体は、まあそうだろうなあ、、というもので、それで終わりだけど、ガイドラインにはなると思う。

分類項目の設定は、こちらで決めたり、訪問回を連続変数にしたりしてるのは、予断があるわけだし、最大の問題点として、分析単位がセッションになっていて、ユーザー単位になってない点があります。同一のユーザーが複数回記録されているデータというのは留保しないといかん。

ただ、それでも、ある一定の基準に沿って、ディメンジョンの項目を整理して、差異の大きい部分だけを抽出しているので、データから結論を得るプロセスとしていいのではないかなと思います。

あと、枝分かれの基準のジニ係数の計算をちょこっとだけやってみる。
(詳しく(正しく?)は、上記の本、データマイニング入門の本を読んでください)

最初の枝分かれは、訪問回数の6.5 ト表示なので、1-6, 7以上と分かれてる。

そのジニ係数は、

7回以上で  1 – ((22/23)^2 + (1/23)^2) => 0.087

6回以下で  1- ( (78/204)^2 + (126/204)^2) => 0.472

これを加重して、(23/226) * 0.087 + (204/226) * 0.472 =>  0.435

元々のルートのジニ係数は、1 – ((79/226)^2 + (147/226)^2) => 0.455

なので、分岐基準は、0.02となる。他の切れ目でやって、分岐基準が小さくなることを確認したいけど、パスします。。。

以下コード。
RGoogleAnalyticsは、cranにはないので、ダウンロードしてinstallする。
あとは、email, password, tableidを、各自でセットするれば、同じように動くと思う。画像ファイルの保存場所と。

library(RGoogleAnalytics)
library(mvpart)

##GAのデータを取得する
ga <- RGoogleAnalytics()
ga$SetCredentials(mailaddress, password)
query <- QueryBuilder()
query$Init(start.date = "2011-01-01",
           end.date   = "2011-12-15",
           dimensions = "ga:medium,ga:region,ga:date,ga:hour,ga:visitCount,ga:landingPagePath",
           metrics    = "ga:entrances,ga:bounces",
           sort       = "ga:date",
           table.id   = "ga:xxxxxxxxx")
data <- ga$GetReportData(query)$data

##データの整理
if(!is.null(data$visitCount)) data$visitCount <- as.numeric(data$visitCount)
data$date <- as.Date(data$date, "%Y%m%d")
data$wday <- weekdays(data$date)
data$wnum <- format(data$date, "%w")

##Langind Pageを上位(entrance数)5つに絞る
top5.lp <- arrange(
                   ddply(data, .(landingPagePath), summarise, sum=sum(entrances)),
                   -sum
                   )[1:5,"landingPagePath"]
dt0 <- subset(data, landingPagePath %in% top5.lp)
##URLが長いのがあるので、後ろから15文字だけにする
dt0$landingPagePath <- sapply(dt0$landingPagePath, function(x){ l <- nchar(x); substr(x, l-15, l)})

##top5.reg <- arrange(ddply(data, .(region), summarise, sum=sum(entrances)), -sum)[1:5, "region"]
##dt1 <- subset(dt0, region %in% c("Tokyo","Osaka","Aichi","Fukuoka","Kanagawa"))

##1セッションで1レコードの形にする
dt0 <- within(dt0, nobounces <- entrances-bounces)
a0 <- adply(dt0, 1, function(x)data.frame(engage=rep("bounce",x$bounces)))
a1 <- adply(dt0, 1, function(x)data.frame(engage=rep("nobounce",x$nobounces)))
dt0 <- rbind(a0,a1)

##いらないディメンジョン、指標を外しておく。以下の書式?なら元から無い項目でもOK
dt0 <- dt0[, !names(dt0) %in%
           c("visits","bounces","nobounces","date","region","hour","wday","wnum","entrances")]

##RPARTの部分
ret <- rpart(engage ~ ., data=dt0, method="class",cp=0.01)
png("~/Dropbox/cart.png",width=800,height=600)
rpart:::plot.rpart(ret, uniform=T, branch=1, margin=0.15)
rpart:::text.rpart(ret, all=T,pretty=0,fancy=T,digits=3,use.n=T)
dev.off()
plotcp(ret)

リスティング広告運用についての雑感

チョット前に、ちょっとだけ、リスティング管理を経験しました。それで、知識の棚卸しをする意味で、私が思っていることを書きます。 google analyticsのデータの利用についても、織りまぜて書きます。記憶の掃き出しをしておきます。あいまいな部分や誤認識もあると思いますので、そういう前提で読んでください。(GAも、ここ数ヶ月は触ってないので、知識が古ぼけてきてます。サイトも更新してないですね。。。)

リスティング広告(サーチ)の流れ

出稿する側は、キーワードとそれにヒモ付た広告を出す。(管理上、いろんなグルーピング機能があって、それらの上位グループ(広告グループ、キャンペーン)で制御が色々入れられる)。

ユーザーが検索する。 と、検索語にマッチしたキーワードに紐づいた広告が表示される。 ユーザーから見ると、検索語を打ち込む => 広告文を見る => ランディングページ訪問という流れ。

ここで大事だと思った点の一つは、ユーザーは出稿側がシステムに出す出稿キーワードは見ないし知らないという点。(完全一致はそのままなので、広告を出す側のキーワード設定とユーザーの検索語が一致するが、) 。

というのは、リスティング広告の管理をしてると、キーワード単位(マッチタイプ、テスト機能で分化管理、グループ、キャンペーンで統合管理されるが)で考えがちだと思われるのだけど、ユーザーサイドの行動である実際のクエリーを出発点に組み立てる事は大事だと。ユーザーは自分のクエリーをだして、広告文をクリックして、サイトに行く。ユーザーの頭に出稿する側のキーワードは入ってない。部分一致の場合。

ユーザーの行動プロセスを起点に考えると、上記のような流れ。で、その前提でデータを見えるようにしないとイカン。

もちろん、google, yahooの入札システムサイドでも上手く立ちまわる事は大切(CPC, 順位などの管理)だ。 そして、こちらも巨大なブラックボックスで、仮説、検証サイクルが廻る事にはなる。

で、

クエリーデータを得る、マッチタイプで分ける

上記の点を念頭にしたとき、僕としては、以下の2点、A,Bを抑えたときに、作業が楽になった。

A .クエリーデータの取得(Google Analyticsで)
B. adwords側でのクエリーデータのマッチタイプ別の分別管理。(yahooは楽にならなかった、、、)

A. クエリーデータの取得(Google Analyticsで)

1.yahooリスティング

これは、独自に取らないと行けない。また取れてもadwordsのようにリスティング側のデータを教えてくれないので、クエリーデータを取るだけになるが、それでもやる必要がある。

方法としては、一般にはutm_source, utm_medium, utm_camapignを付けてということになっているが、あんまり有効な方法には見えない。この方法だと、クエリーデータはリファラーから取って、cookieのutmzのutmctrに入るはずだけど、utmパラメータを設定すると、リファラー内のキーワード情報は、日本語のまま入ってしまう。で、エンコードしてcookieに入らないと、最終的なデータで文字化けやデータ不取得となることがあるのだ。通常の場合(utmパラメータを使わない)と違う仕様にした理由は分からない。マルチバイトの人が声を上げてないだけかもしれないし、他に理由があるのかもしれないし、僕の勘違いかもしれない。 でも、この辺りのデータ不備で悩んでる人は多いと思う。

で、utm_xxxx を使わないとすると、、、ヤフーリスティング側の仕様を利用する方法が良い。

yahooリスティングからクリックされた時には、ヤフーがパラメータを付けてくれる。それを使う。

クエリー情報、キーワード情報、マッチタイプ情報、キャンペーンID情報、広告グループID情報、広告ID情報 をURLパラメータとして付加してくれてる。これを素直にGAに入れれば良い。

その方法としては2つ。サイト内検索を使う。 or   トラッキングAPIを使う。

サイト内検索は、2つのパラメータ情報を取得できる(独立した?ディメンジョンで) ので、キーワード+クエリー、クエリー+マッチタイプとい形で取得しておく。サイト内検索の設定に、ovraw, ovkey, ovmtcとかを設定すれば良い。サイト内検索でのディメンジョン情報は、ゴール指標と結びついているので、コンバージョン測定もやり易い。問題はcookieに情報が入るわけではないので、再訪問データとのかけ合わせが出来ないけど、これは通常のデータ分析でも難しいので、要らない(僕は)。

トラッキングAPIは、このへんだけど、やったことない。その上、問題があって、URLパラメータの情報は、ヤフーが日本語にして(エンコードせずに)付加してくれてるので、上で書いたデコード問題が発生しそう。ただ、クエリー情報以外の情報(キャンペーンID,広告グループID、広告IDとか)を収納できそうなので、これと、後で、ヤフーリスティング側からのIDの情報とJOINすれば、かっちりとしたデータが取れるとは思う。 集計してそのデータが活かせるかは、分からないけど。

と書いたように、サイト内検索の機能を使うのが手軽で良い。正統的な方法のutm系のパラメータとも共存して測定できるし。 ただ、APIを使う人は、ランディングページのパラメータを取り出して、エクセルなどで処理すれば、こんな方法は要らないかもしれない。

サイト内検索はパラメータによるデータの振り分け機能と考えると、GAの応用の幅が広がる。

あと、GAで実際に集計するプロファイルからは、yahooリスティングのパラメータを除去しておかないと、ランディングページの種類が大量になり、コンテンツ単位の集計は破綻すると思うので、プロファイル設定はキチンと手を入れておく必要。生データ用は、別のプロファイルで残しておく。

ヤフーはこれくらいはさっと思いついてやった。GAに詳しかった?人の立場としては…  ただ、応急処置的だし、javascriptでもっとやりたいとも思ったけど、、、全部自分で管理できないので、それは無理だし、それ以上のデータを持っても、サイトのパフォーマンス上げられる自信もなかった。リスティングの運営は手間暇かかるし、ビジネス的にはリスティングの運営のがお金に響くので、リスティング運用に時間をかけるのが本筋。また、リスティングの情報は質が高い(解釈しやすいし、相場情報になってる)

ヤフーリスティングは、情報が国内に限られるので、adwordsよりノウハウをネットで探すのが難しかしい。ただ、サポートは手厚いので、キチンとサポートを利用できる体制だと運用が楽かも。そういう意味ではある程度、組織だって運営できる所に利点があるのかな。代理店制度とかそのへんはよく分からない。

2. adwords

クエリーデータを起点にデータを取得するの続き。

アドワーズに関しては、自動設定にしておけば、クエリーデータは、アドワーズ側のレポートで見られるし、GAにも送ってくれる。なので、上記のヤフーの計測レベルは既に達成されている。

ただ、GA側のadwordsデータを使うと、もっといろいろ利点があるので書く。

まず、アドワーズのクエリーレポートはデータが来るのが遅い。一緒に提示されるデータで遅い物があるのか、クエリーデータがでるのに、2,3日掛かったような気がする、、、

一方、GAの方は、通常のクリックデータと同じなので、通常?、一時間以内にはデータが乗る。

なので、アカウントのスタート時とか、大規模の修正を掛けたときはGA側でクエリーのデータを追うべきだ。キーワードの除外設定は早ければ早いほど予算を残せるし、有効なクエリーを見つけて、キーワード登録するのが早ければ早いほどコンバージョンが実績で残る。

また、APIを使うのが前提になるけど、adwordsのクエリーディメンジョンに、他のディメンジョンも追加できるし、通常のメトリックスやゴール指標もデータとして乗っかるので、かなり細かく追っていこうと思えば、できる。追っかけて成果を出す解析スキルはないけど、、、、

あと、アドワーズレポートとの違いはまだある。クリック単位ででデータ(ディメンジョン、指標)が見られるけど(正確にはGAのディメンジョンで細分化出来る範囲で)、順位ディメンジョンの他にトップポジション、右ポジションという、広告配置位置のディメンジョンでも指標がだせるので、パフォーマンスの違いが測定できるし、adwordsで順位が一位だが、RHSになってるものもチェックできる。(必要なら広告ランクを増して、TOPに持っていかないとイカン)

* 先週あたり、アドワーズ側でもデータが出るようになったらしい。ただ、それでも、GAの方がデータの粒度を細かくできる。多くのディメンジョンのあるデータキューブ(アナリティクのデータはそう考えると良いはず。多次元データベース)みたいなのに組み込まれるわけだし。だと。 サイト内データ指標ともある程度ひもづくし。できない組み合わせもあったけど。

と、GA側のデータなら、adwordsのデータはかなり細かく、そして迅速に分かるので、有効に使うえる。

あと、GAとは関係ないけど、adwordsの管理の話を少し。

B 部分一致と完全一致は、分別して管理するのが良いはず

リスティングを経験する中で、最初に戸惑ったウチの一つは、部分一致と完全一致の所だったような気がする、、、他にもあっただろうけど、、、

メイン?なキーワードは、完全一致と部分一致の両方で出稿していくことになると思うけど、除外設定の知識も最初はなかったので、設定に悩んだ記憶がある。除外設定をしったあとは、完全一致と部分一致を区別するには、(部分一致+ 完全除外一致) で部分一致の出稿すれば、完全と部分の分離ができると分かって楽になった。

基本的に複数の広告グループで、完全グループと部分グループを別々に作るのが良いと思う。他の人の現場の方法は知らないけど、ここもそう言っててる。http://certifiedknowledge.org/blog/3-strategies-for-organizing-your-match-types/ (キャンペーン単位で分けるのもありかもしれない。 (このサイトは有用な情報が多い。他にも良い記事がある。)

この辺りは、adwordsの話。 ヤフーリスティングは除外設定が、フレーズ一致的な除外に固定されているの、上記のことはできず、入札単価の調整などになる。でも、単価調整方式だとCTRの履歴がどうしても歪みがちで、広告ランクが部分一致側に高く出て、マッチタイプの分別コントロールが非常に難しくなることがあると思う。 いい対策はないかと、ネットで探しても、この辺りで良い情報は見つからなかった。除外もキレイに出来ないし。この辺りの秋に改正されるらしい。

しかし、ヤフーリスティングとadwordsを同時に利用すると、adwordsすごいなあと思う。全体のシステムもすごいのだろうけど、adwordsのような数字でいっぱいなものが、ウェブのインターフェースで運用できるのにも驚いた。ウェブ上で情報をブラウズして操作するシステムで、自分が知る限り最高のモノの一つだと思う。salesforceとかを使うともっとすごいシステム(UI)ってあるのだろうか? うーん、証券システムかなあ、、こっちもすごいかもしれない。バックエンドは知らない。

まとめ

リスティングについて、ほぼゼロベースの知識で望んだ時の経験の記憶の棚卸です。GAのついての知識はあったので、それを活かす形でやった記憶の掃き出しです。 新しいGAのことについて(新しいUIとか間接効果とか)は知らないので、聞かないでください。

そういえば、アドワーズのサーチファネルも、クエリーパスを時々見ると、ある種の感覚的なヒントがつかめると思う。定量的な分析はスキルが不足してる。僕には。

あと、リスティング広告の運用については、無駄なクリックの削減が大きなテーマだと思う。で、その最初の一歩は、出稿キーワードと検索クエリの対応関係をつかむとやりやすいかなと。出稿キーワードの感覚も掴めるし。 GAを使うと、ヤフーリスティングの管理画面よりその辺りが明確に見えてきます。そして、adwordsも、adwordsのレポートデータより、GAのレポートのが便利な点が多いです。

あと、ヤフーリスティングに関しては、要らないサイトを除外できる機能は必須機能だと思う。全体のクリックの2割がヘン?なサイトにクリックされてたとしたら、25%余分な贅肉がついたCPAで、競合と勝負してることになる。それで、運用がジリ貧な循環になり、本来できた運用ができなくなって撤退となる可能性すらあるわけで、単純な知識の差で永続的な差が付きかねない。

また、全然クリックされなくても、品質インデックスに悪影響を与えない可能性も無くもないので、クリックされない外部サイトも早めに除外する方が良いかも(yahooは外部サイトのクリック履歴を組み入れないと宣言してない。adwordsは除外設定自体が無いけど、QSにはカウントしないと宣言してる)

以上です。

あと、アドワーズの全般に向こうのフォーラムに出たベストプラクティスを、メモ書きしたものがあった。リンクを張っておきます。https://sites.google.com/a/abc-analytics.com/adwords/forumno-besutopurakutisuno-matome

リスティング広告は、データがスグに出るし、競合との勝負という点があるので独善的になりにくいし、adwordsに関してはテスト機能+レポート機能も強化されて、思いつきレベルでもいろいろ試せるし(テストでなくても試せるけど、比較はしにくい)。サイトの継続的な改善にはまずはリスティングというのは良い方法だと思った。その後もニーズを探るのもリスティングが良さそうでもある。ヤフーリスティングもGAとうまい具合に統合できるといいなあと思った。

ページ遷移レポートの自動配信を実現する

前回、スクロール計測のレポートを自動配信する話を書いたのですが、今回は、Google Analtyicsのページ遷移データをグラフ化してメール配信する話です。

同じく、google apps script + google chart api でのメールによるレポート配信です。

AuthSubでの作成も作りました。試してみてください。

Google Analyticsにおけるページ遷移のデータ

あるページにおける、遷移上の前後のページは、WEBのレポート画面で見ることができます。ナビゲーションサマリーですね。ナビゲーションサマリーの数字の見方は、以前Wikiの方に書きました。

WEB上のレポート画面では、重複ページの回数を抜いて、パーセンテージにして表示してます。(移動先のデータ(nextPage)のデータがよく分かってないのですが、、)

今回は、Data Export APIでデータを持ってくるので、重複ページを抜かないとと思っていたら、uniquePageviewの数字がそのままの数字なので、端折って、uniquePageviewの数字を使いました。uniquePageviewを使う妥当性について、Forumなどで質問があったみたいですが、数値を見る分には大丈夫そうなので、uniquePageviewを使います。単に重複を抜くためだけですが。(もう少し考えてみた。けっこう注意が必要かも)

とにかく、ディメンジョンに, “priviousPagepath”と”nextPagePath”。指標に uniquePageviewを使います。

Google Chart APIの GraphViz Chart

前回のデータを表示する時に、chart apiの文書を見てたら、おもしろいチャートの種類を見つけました。 Connectivity graphsとかいてあります。日本語だと 連結グラフ?

これも、パラメータを指定するだけで、グラフをimageにして返してくれます。またしても、パラメータの設定が難しかったのですが、PDFの説明書みたいなのを見ながら設定してみました。微妙に chart apiでの記述と、h本家の記述と違う部分もあると思います。subGraphも設定できるみたいで、それでクラスター表示みたいなのをしようと思ったけど、Google Chart Api側で、動くように設定する方法がわかりませんでした。(* google chart apiでの方法を知っていたら教えてください。)

前回も書きましたが、http://code.google.com/apis/chart/docs/chart_playground.htmlでできるチャート図を確認しながら、設定をいじれるので、便利です。

とりあえずの例

それで、実際にこのサイトのデータで生成してみました。メールレポートの場合は、google apps scriptでHTMLメールを使って、scriptの自動実行時間を指定すれば良いです。参考に、ちょっと違うけど、フォーム受付けを自動返信する話です。今回は、これを時間指定にするだけです。

できたimage画像です。 (画像クリックでできたURLそのものに遷移します)

閲覧開始数のベスト8ページのデータと、ページ遷移の組み合わせで数が多かったベスト10のデータを抜き出してます。頭に数字があるのは、そのページの期間内の閲覧開始数です。矢印線上にある数字がページ遷移の数です。 数字だけの奴(_108_)は、indexページです。閲覧開始数が108でした。

(自分自身に返ってる奴は、データの初期段階でパラメータが違うものをカットしたせいです。処理が粗いです。説明もゴニョゴニョですが、、)

サイトオーバーレイもいいですが、サイト全体のユーザーの動きはコチラのほうがイメージし易いと思います。

気を付ける点としては、上のリンク先の記事でも説明してるのですが、セッションベースのデータでないので、遷移の数は、ランディングからすぐに遷移した数ではないです。 secondPagePathもデータとしてはあるので、そちらでもグラフはかけそうではあるのですが、、、

あと、chart apiで生成できるピクセル数の大きさは、縦×横で、300000pxまでなのですが、このgraphVizでできるデータはそれを超えてます、、、、また、これはexperimentのマークがついてるので、仕様変更の可能性もありそうではあります。

それでも、自動化できたので、データを見せていただける方には、メールで日次配信します(コメントで遷移図希望と書いて下さい)。今回のは、Google Analtyisの初期設定で取れるデータなので、閲覧権限をもらえれば、それでOKです。 Auth認証でのデータ表示に対応するのは、詳しい人なら簡単にできそうなので、そういう人に任せます。

google apps scriptと chart apiを使ったレポート自動化

今回は、アクセス解析の話ではないのですが、データとして GAを使ったので、ここに書きます。

前回、スクロール率をgoogle analyticsで計測した話を書きました。 レポート部分は、エクセルでの表示だったんですが、今回、自動化のひとつの方法として、タイトルの方法をやったので、報告します。

Google Apps Script(GAS)は、かなりの便利ツール

データを外部から引っ張ってきて、加工して、メール配信というのが、割合簡単にできます。

今回は、google analyticsのデータを引っ張って、加工して、HTMLメールで配信という形の紹介です。

GASの記述は、javascriptです。 google analyticsの集計の設定も javascriptですので、 javascriptは習得しがいのある技術かもしれません。

手順としては、URLFetchで google analytics Data Export  Api からデータを取得して、それを google chart apiを使って、 HTMLメールで送る作業です。

Google Chart APIも、自動化ツールとして価値大。

URLを指定すれば、チャート画像を返してくれます。

ですので、変化する数字を自動的にグラフ化してレポーティングしたい場合に価値が高いです。

パラメータがたくさんあって、マニュアル(解説)が英語なのでとっつきにくいですが、データ、チャートタイプ、ラベル、軸、などの概念とapiでのパラメータが一致すれば、それなりに使えるのではと思ってます。

別サイトにメモみたいなのを書きましたが、、、あまり参考にならないかも。

GMailの問題?

いろいろいじって、なんとか自動化にこぎつけたのですが、GmailのHTML表示の時に、imageタグの部分が上手く表示されなくて、いろいろ探したのですが、結局あきらめました。 たぶん、文字数の問題のような気がしてるのですが、、、どうなんでしょう。 Yahoo Japanの Web mailなら問題なかったです。

出力画像と スクリプトを貼りつけます。

スクロール計測とそのレポート配信を試してみたい方は連絡下さい(コメントでスクロール計測希望と書いてくだい)。 設定して、週次 or 日次で、特定のディメンジョンで切ったスクロール率のよる閲覧割合をメールレポーティングします。無料です。 以下の画像のような形でのHTMLメールが送られます。

画像レポートの後に、参考に、GASのスクリプトを貼りつけておきます。 貼り付けた以外にも追加の定義(関数、代入式)などがあるので、全部ではないです。

2010-06-03から2010-06-15のセッション数上位3ページの精読率調査です。数字はセッション数です。

以下、レポート配信に使ったscriptの一部。

function CRReport(id,name,title,address){
    //id = "ga:21600568", name="shirai", title="wordpress", address="kimiyuki[at?]gmail.com";
    var startdate; var enddate; var dimensions = []; var metrics = []; var filters = ""; var segments = "";
    var entries; var sort;var data;
    var tmp;
    var siteurl;
    startdate = getMyDate(-15); enddate = getMyDate(-3);
    dimensions = ["ga:pagePath,ga:hostname"]; metrics=["ga:visits"]; sort=["-ga:visits"];
    entries =  getDataFromApi(id, startdate, enddate,dimensions, metrics, filters, segments, sort);
    pagePaths = [];
    entries.slice(0,3).forEach(function(entry){
        var el = entry.getElements("http://schemas.google.com/analytics/2009", "dimension");
        el.forEach(function(e){
            if(e.getAttribute("name").getValue() == "ga:pagePath") pagePaths.push(e.getAttribute("value").getValue());
            if(!siteurl && e.getAttribute("name").getValue() == "ga:hostname") siteurl = e.getAttribute("value").getValue();
        });
    });             
    
    data = {};
    var w1 = [null,null, null]; var w2 = [];
    pagePaths.forEach(function(path){
        Utilities.sleep(500);
        w1 = [null,null,null], w2=[];
        var dim_sg = "ga:medium";
        dimensions = ["ga:eventCategory", "ga:eventAction", "ga:pagePath",dim_sg]; metrics = ["ga:uniqueEvents"]; sort = null;
        filters="ga:eventCategory==CompRead0523B;ga:pagePath==" + path;
        segments=null;
        entries = getDataFromApi(id, startdate, enddate, dimensions, metrics, filters, segments, sort);
        entries.forEach(function(entry){
            var ds = entry.getElements("http://schemas.google.com/analytics/2009", "dimension");
            var es = entry.getElements("http://schemas.google.com/analytics/2009", "metric");
            var w = new Array();
            ds.forEach(function(e){
                if(e.getAttribute("name").getValue()=="ga:eventAction") w1[1] = parseInt(e.getAttribute("value").getValue().replace("z",""));
                if(e.getAttribute("name").getValue()== dim_sg){w1[0] = e.getAttribute("value").getValue();}
            });
            es.forEach(function(e){
                if(e.getAttribute("name").getValue() == "ga:uniqueEvents") w1[2] = parseInt(e.getAttribute("value").getValue());
            });
            w2.push([w1[0], w1[1],w1[2]]); //値にして設定するため
            w1 = [null,null, null];
        });
        w2 = w2.sort(function(a,b){return a[1] > b[1]});
        if(data[path] == null) data[path] = [];
        data[path] = w2.map(function(x){return x});
        //data[path] = w2.map(function(e){return [e[0], Math.floor((e[1]/total)*100)]});
    });
    // Logger.log(data);
    html = outputChart(data, startdate, enddate, siteurl);
    Logger.log(html);
    MailApp.sendEmail(address, "CompRead","html mail", {cc: "kimiyuki[aat]gmail.com", htmlBody: html});
}


function outputChart(data, startdate, enddate ,siteurl){
    var html = "
" + startdate + "から" + enddate + "のセッション数上位3ページの精読率調査です。数字はセッション数です。
"; for(e in data){ //Logger.log(e); //Logger.log(data[e] instanceof Array); //Logger.log(data[e].length); //Logger.log(data[e].toString()); //making dimentionable data var dimmed_data = data[e].reduce(function(r, x){ if(r[x[0]] == null){ r[x[0]] = x[2]; }else{ r[x[0]] += x[2]; } return r; }, {}); //Logger.log(dimmed_data.toSource()); var tmp = []; Logger.log("object="); Logger.log(dimmed_data.toSource() + "n"); for(c in dimmed_data){tmp.push(])}; dimmed_data = tmp.sort(function(a,b){ return a[1] < b[1]}); Logger.log("dimmed_data="); Logger.log(dimmed_data.toString()+"n"); var label = data[e].map(function(x){return x[1]}).unique(); var data1 = data[e].filter(function(x){return x[0] == dimmed_data[0][0]}).map(function(x){ return x[2]}); var data2 = data[e].filter(function(x){return x[0] == dimmed_data[1][0]}).map(function(x){ return x[2]}); var data3 = dimmed_data.length < 3 ? "" : data[e].filter(function(x){return x[0] == dimmed_data[2][0]}).map(function(x){return x[2]}); //Logger.log(label); //Logger.log(data1); //Logger.log(data2); //html += "" + e + "
" + data[e].map(function(x){return x[0] + '=>' + x[1]}).join(',') + ""; html += "
"; html += makeChart(label, data1, data2,data3, dimmed_data[0][0], dimmed_data[1][0], (dimmed_data.length > 2 ? dimmed_data[2][0] : "")); html += "
"; html += "" + e + "
"; html += "
  
" //全角空白を入れた。間隔を作るため } html += "
"; return html; } function makeChart(label, data1, data2, data3, dataLabel1, dataLabel2, dataLabel3){ var max = data1.concat(data2).reduce(function(r, e){return r > e ? r : e;}, 10); var chds = "chds=" + "0," + max; var chxr = "chxr=1,0," + max; var chco = "chco=4d89f9,c6d9fd" + (data3 != "" ? ",63C6DE" : ""); var chdl = "chdl=" + dataLabel1 + "|" + dataLabel2 + (dataLabel3 != "" ? "|"+dataLabel3 : ""); var chm = "chm=N,000000,0,,12,0,e|N,00FFFF,1,,12,0,e" + (data3 != "" ? "|N,0000FF,2,,12,0,e" : ""); var url = ""; //Logger.log(url); return url //.replace("&", "&"); }

ページのどこまで読まれたかを計測する

ウェブサイトのコンテンツをページングするのはユーザビリティを損なう事が多いと僕は思ってます。ユーザビリティの定義は知りませんが、読むこと以外に”意識”を取られると嫌な感じがします。 提供側の論理は知りません。ユーザビリティですから。

次のページへのリンクをクリックという行為は、

  1. そのアンカーテキストに目の焦点を併せる
  2. マウスを叩く
  3. リンク先のページがloadされる。

という順番で進むと思います。

速読の練習をした人はよく分かると思いますが、1の行為は、文字を探し・焦点を合わせるという、非常にコストのかかる行為です。読むという作業においては。。文脈を追う作業は超短期記憶のつなぎ合わせで、意識のallocation作業は天敵です。

一方で、アクセス数字を見たい人に取っては、ページ単位の計測が基本である以上、ページとコンテンツの粒度は一致してる方がいいと思ったりします。<h2> <h3>といった見出し単位での閲覧率を知りたいと思ったりすると思います。閲覧時間の方が知りたいかも、、、 とにかく、ページ閲覧量と滞在時間ではなくて、コンテンツ消費量とコンテンツ消化速度を知りたいとか思うと思います。

ヒートマップ使ったり、細かく計測タグを作れば、データは得られそうですが、面倒です。

で、とりあえずの一歩として、ページ全体でどの割合までページが表示されたかを、google analyticsで計測してみたので、その経過を書きます。

EventTrackで、表示領域割合をアクションとして送る。

当初、表示割合を計測するのに カスタム変数のページスコープ変数を使えばいいのかと思いました。上手く説明できないのですが、ページスコープは使いにくい機能(*)なので、前に使った手法(ページ別閲覧時間分布の作成)と同じような手法のtrackEventを利用する方法でやりました。

* ページスコープのカスタム変数を設定してtrackPageviewで送ったら、それは新規のページビューになってしまう。動的な行動記録をアトヅケで送れない。

それで、方針としは、

  1. 一秒毎に、画面の表示位置(割合)を取得。
  2. それが、そのページ閲覧中で最大値を超えていたら、trackEventする。
  3. 10%単位で区切る。google analyticsでは集計値しか取れないので、値は積み上げで送る。
    • 30%まで閲覧されたというデータを送る場合には、0, 10, 20, 30 と送る。
    • なんとなくだけど、データの連続送信制限があった気がしたので、少しsleepを入れておく。
    • このサイトのrs.jsという奴です。データを送る順番をコントロールするために、jsdeferred.jquery.js をいれました。このサイトの説明を参考にした

データを見る : totalEventsとpageviews

eventの数字を見るときは、totalEvents(イベント数)と uniqueEvents(ユニークイベント数)の二つを見ますが、uniqueEventは、セッション内の同一カテゴリ・アクション・ラベルを一つ(distinct)にします。

image

自作のDataViewerで見たところ。labelにページURL(path)を入れていたのですが、ディメンジョンとして pathPathも有効なので、labelは他の用途に振り向けてもいいかもしれません。

ちなみに、ABテスト別に見たい時は、カスタム変数を出してフィルタリングします。

ab_test_for_how_much_viewed_againt_content

ちなみにこの自作ツール、チョコチョコ改良してます。、カスタムレポートを組むより、操作時間は短いはずです。 日別のデータ作成には便利です。 GAのレポート画面だと、日別のデータをグラフにしてくれますが、数字で出すには、カスタムレポート組まないといけない。

レポート化

アクセス解析サミットで清水さんの講演を見て、スクロール計測のレポートの形式を真似ました。

image

画像にパーセントで線を入れるのにbookmarkletを実行、画像を撮って、エクセルでrept関数。

javascript:hg=document.height;wd=document.width;for(var i=1; i<10; i++){dv=document.createElement("div");dv.innerHTML= "<hr style=’color:#f00; background-color:#f00; height:5px;’/><strong>"+(i*10).toString()+"Percent==></strong>";dv.style.position="absolute";dv.style.top=parseInt((i/10)*hg).toString()+"px";dv.style.left="0";dv.style.width=wd+"px";dv.style.zIndex="1000";document.body.appendChild(dv.cloneNode(true));}

このbookmarkletは適当に作ったもの。 ページ全体画像撮りは、このchromeのextensionを使った

このレポートを自動化するには、

bookmarklet実行 + 画像収集 + データ抽出 + どこかで画像オブジェクトにする

という作業が必要。

Google Analtyicsで、trackEventを使って、ページのスクロールの割合を見て、レポートにするまで、でした。 アクションは知らない。

GWOとGAをくっつける

併用についてのGoogle の公式のヘルプ記事がありますので、まずは、そちらをご覧ください。

GWOは、Google Website Optimzer。GAは、Google Analytics。ともに、Googleが提供するウェブサイトの効果計測の為のツールです。この二つのツールの使用は、機能として非常に似ているので、両者の良い部分を併せてみようと言うのが、今回のエントリーです。ちなみに、GWOで何をOptimize(最適化)するのかというと、コンバージョンレートを最適化(ためのテスト)します。A/Bテスト、もしくは多変量テストで数字を見比べて、適するテストを選択するわけです。

まず、両者の比較から入ります。

GAの方が分析用の機能は豊富

GWOは、ユーザー単位(cookie)でのコンバージョンを測るという機能を提供するのですが、同じことは、GAでもできます。カスタム変数をユーザーレベルで設定すれば、そのユーザーグループでのコンバージョン率も測れます。

さらにGAは、Analyticsとウタッテルわけですので、他の指標もグループ別に分析できるわけです。

ユーザーグループ別の、滞在時間、テストページ以外の閲覧ページなどが可能です。さらに、trackEventを発行していけば、javascriptが補足するイベントをどんどん計測できるわけです。trackEventは、4つのパラメーターをとれますので、その中にsetTimeout, setIntervalなどで時間の値を組み込んでもいいです。そして、これらを、ユーザーグループ別に計測できるわけです。また、フィルター機能で自分のアクセスを除外したりするのも簡単です。

しかし、GA単独で事が足りるか? というと、そうも行かないかもしれません。

GWO側の優位点は?

実は、GWOにあまり詳しくないので、自信がないのですが、GWOの利点は、

  • テストの実装が簡単
    • 基本は、オリジナルページにcontrol scriptを埋めてやると、そこでテストページの振り分けをしてくれる。GAは、自分で作る必要がある。
    • 特に、多変量テストは、自前で作ったり、管理するのは大変だと想像します。(やったことない、、)。Follow-up Experimentという仕組みもあるようです。よくわからないけど、多数の組み合わせの中の勝ち残りと、オリジナルで、最終確認テストができるという意味?
  • 結果の解釈に説得感がある
  • なんとなく安心
    • GAで、cookieに依って違うページを見せるのは少し気持ちが悪いです。検索エンジンに良く思われないのではないか?と思ってしまいます。GWOだからOKという理屈はないですが、なんとなく安心ではあります。

とGAとGWOの利点を見ました。以後、両者の利点をいいとこ取りに挑戦です。

手順

GWOでテストを作り、そのGWOのcookie値をGA側のカスタム変数に入れて、GAでも分析できるようにします。GWOには、combination関数というのがあって、テストの振り分け判別用の値が取れるみたいですが、オリジナルページ(control script内)でしかとれないようですMVTを行う場合には、ページ振り分けがあるわけではないので問題ない。 ですので、関数を使わずにcookieを直接読んで、値を設定することにします。

具体例は省略します(*このサイトのトップページが、現在テスト対象になってます。そのうち外します)。

GAと同じです。ウェブプロパティIDが違うので、別々にキチンと設定してやればいいはずです。

今、WEBで検索していて見つけたのですが、Poor man’s GWO/Analytics Integration のやり方がいいです。いろいろ改善点があるのですが、カスタム変数に入れるのではなく、ユーザー定義に入れておけば、セカンダリーディメンジョンで見られるので、アドバンスフィルターで検索するなりして、手早くレポートで確認ができると思います。他にも、改善点がいろいろとあるようです。”いつかやる”にしておきます。

レポート画面

ユーザー定義ではなく、カスタム変数に入れてしまったので、レポート画面が面倒くさいですが、カスタムレポートで見たい指標を並べておいて、ゴールのページを通過したセッションをアドバンスセグメントで、括り出しておく形ですがいいです。ユーザー定義を使った場合は、ゴールのページで、ユーザー定義をみればいいと思います。

WS000002

GAでは、コンバージョン以外の指標を見られるのが、アドバンテージ。

GWOの方は、

WS000003

GWOでは、統計的な判断をしてくれたレポートにしてくれるのが、アドバンテージ。

GAとユーザーの母数が違いますが、GAの方で当初設定ミスをしたせいだと思ってます。また、GWOの方はフィルターが使えないので、自分のアクセスを除外できないと思います。GWOとGAで完全の統合してしまえばいいとも思いますが、冒頭で挙げたGWOの利点とバッティングするのかもしれません。

*) wordpressでテストを作る時は、ページのテンプレートを作って、そこにコントロールスクリプトを入れてページを作る。そして、もう一つの方は、別にテンプレートを用意する形になるのかもしれません。最初、両方にコントロールスクリプトが入っていて、ページがリダイレクトループみたいになってしまいました。CMSを使っていると、普段は便利なのですが、こういう時は、”調べるコスト”が発生しますね。 同時に”設定ミスリスク”もある所が怖いです。

外部サイトへの流出を記録しておく

外部サイトへのリンクは、バーチャルページビューとイベントを発行しておくと、トラフィック(流入元)の記録や滞在時間など、いろいろなデータが残せます。複数のドメインを管理しているパターンで、クロスドメインやサブドメインの設定が使えない場合などにも、この外部リンクの計測は代用にならないまでも、参考になると思います。*もちろん、ドメイン関係の設定ができればその方が100倍いいです。
計測タグはこんな感じです。
<a href="hoge.com" onclick='pageTracker._trackEvent("Outbound", this.href, document.location.pathname, 時間値);
pageTracker._trackEvent("/outbound/" + this.href.replace(/https?:///, '');')> 外部リンク</a>

2010-08-06
こっちのヘルプをみると、document.locationでリンクを飛ばして、最後にreturn falseをする風に書いてあって、前のだと計測漏れが出そうなので訂正しました。あと、setTimeoutで100遅らせるとリンクされる前にビーコンが飛んでくれる確度が高まるよう。

<a href="hoge.com" onclick='pageTracker._trackEvent("Outbound", this.href, document.location.pathname, 時間値);
pageTracker._trackEvent("/outbound/" + this.href.replace(/https?:///, '');var a =href;setTimeout(function(){document.location=a.href},100); return false;')> 外部リンク</a>

試してません。詳しくはリンク先のヘルプを。 ただ、こっちのヘルプは訂正まえのパターンなので、、、いいかも。
みたいな感じです。実際は、jQueryなどを使ったりして、一括でeventを登録するタイプが便利ですが、基本は同じです。

レポート画面でのイベントの値の確認

trackEvent_outbound

outbound_linkを選んで、

イベントトラックの値の所には、ページロードからの時間を入れておきました。ページ上での外部離脱までの閲覧時間がでます。

WS000002

平均で15秒で外部リンクされてます。エントリーの冒頭部分のリンクなのでしょうがないです。(リンクされてない場合の閲覧時間も、きっちり分けて出せますが、省きます)

一方、利用状況を見ると、セッションで見た指標が出るので、セッション単位での滞在時間がでます。

WS000004

セッション単位でみると、38分です。計測方法が普通と違うのですが、平均してそれくらいページが開かれて居たことをしめします。外部に離脱しても戻ってきているか、外部リンクが新しいタブで開かれているのかのどちらかです。(* ページビューは、偽造なので実際はこの半分くらいです)

トラフィックデータの確認

簡単には、セカンダリ-ディメンジョンで確認できます。

WS000008

アドバンスセグメントで、イベントのカテゴリ-・アクションでセグメントすれば、しっかりとしたセッション分析ができます。

ページ単位での計測

イベントトラッキングではページ単位の分析はできないのですが、trackPageviewも同時に計測していた場合、そちらでページ遷移?が見られます。

WS000005

途中で、httpを抜いたURLにした為、ページが分かれてますが。下のは離脱したまま還って来てないですが、上のは、スグ(平均5秒)戻ってきていますね。(と言いたいのですが、他のeventTrackの影響っぽいので、たぶん、新しいタブを開いた形の計測になってしまったようです。短くは説明できません)

WS000007 WS000006

ナビゲーションサマリーで見ると、前後が同じページ(リンクされたページ)ですね。

データがしょぼいのですが、ページ遷移データみたいなものも見られます。Google Analtyicsのページ遷移は、ランディングペ-ジ分析なので、今回のバーチャルペ-ジビュ-でのページではデータが見られないです。

WS000009

と言うことで、外部へのリンクの計測に、trackEventとtrackPageviewの両方を使う事で、セッション単位の分析、ページ単位での分析の両方ができる事をみました。

最近になってやっと気づいたのですか、trackEventも使いどころがありそうな気がしてきました。

最後に、カスタムレポートで、ページビューとかユニークユーザー数を出してみました。この通り解釈していいのか不安ですが。

これで終わりです。

WS000010