ページ種別毎にグループ化したヒートマップを作った話

[ディップ Advent Calendar](http://qiita.com/advent-calendar/2017/dip-dev) の栄えあるクリスマスイブの記事です。（焦り）
そろそろエンジニアぽい記事を書きます。

## 概要
しばらく前に自分が担当したスマホ用Webサービスに適用した自作ヒートマップツールの話です。
巷によくあるヒートマップツールでは計測し辛い点をカバーした流れを書きます。

## ヒートマップって？
ここで話すヒートマップとは、Webサイトの改善に使われる [こんな感じのやつ](https://www.google.co.jp/search?tbm=isch&q=ヒートマップ) です。
サイト訪問者が見ていたところに色を付けて可視化するやつですね。
今は便利な時代なので、無料で簡単にサイトに適用できるプロダクトが沢山あります。

## 既存のヒートマップで困ったところ
先述のヒートマップですが、基本的に「特定のページに対して絶対座標で集計される」想定になっています。
しかし今回対象となるサービスは「たくさんのコンテンツが（比較的）短いライフサイクルで入れ替わる」というものでした。
また当時自分は特定のコンテンツに対する改善ではなく、コンテンツページ全体に対する改善を検討していました。
そのため特定のコンテンツに対してヒートマップを見たとしても

* コンテンツが多数あるため、どのコンテンツを対象とすべきか決め辛い
* 一つのコンテンツへのアクセスの絶対量が少なくデータが偏る可能性がある
* コンテンツ毎に内容も違うため、特定コンテンツのデータではコンテンツページ全体の傾向を図りにくい
* 特定コンテンツのデータを分析する間に、そのコンテンツの寿命が終わる

といった理由で恩恵が得られにくいのです。
以上からコンテンツページ全体をグループ化して計測できれば……と思いましたが、ここで絶対座標による計測の罠が現れます。

## 絶対座標による計測の罠
例えば、とあるサービスにはこんな感じの構成のコンテンツがたくさんあるとします。

* タイトル
* 見出し画像
* 概要
* 主文
* ユーザアクションボタン
* 補足

コンテンツの具体例として「コンテンツ１号」を用意します。
![コンテンツ１号くん](https://qiita-image-store.s3.amazonaws.com/0/72466/530ba81e-c75a-5b71-527d-74dcf5d84e9b.png)

そしてこのコンテンツ１号に対し **メイン部分（やや下）がすごく見られてるよ！** というデータが得られたとしましょう。
![コンテンツ１号くんのココが赤い！](https://qiita-image-store.s3.amazonaws.com/0/72466/c92c7ab4-fc9b-a0ef-1c5c-2ad79fc67347.png)


しかしこのデータは他のコンテンツには流用できません。
なぜなら、下記のような少し全体的にコンパクトなコンテンツ２号があった場合
![コンテンツ２号くん](https://qiita-image-store.s3.amazonaws.com/0/72466/07ff3d9f-504a-3c7c-5ec9-10e24c737e81.png)

さきほどのコンテンツ１号で計測したデータを同様に反映させると

![１号くんto２号くん](https://qiita-image-store.s3.amazonaws.com/0/72466/c77ce0ce-adf9-6e84-1fbc-6fcc64c6eeed.png)


のように、全然違う要素であるユーザアクションボタンや補足の領域がよく見られていることになってしまいます。
この問題を解決するために **２つのアプローチ** をとりました。

## グループ化計測のポイントその１：コンテンツ分割
絶対座標によるヒートマップは特定のページ毎に計測するのであれば非常に強力です。
というのも **対象のページの構成を気にすることなく計測できる** ためです。
今回の「コンテンツ１号」についても、このページだけを対象として考えるのであれば、コンテンツ全体がどういう構成（タイトルと概要と……と補足がある）を全く気にする必要はありません。
ですが今回は自作するということで、逆に **全力で特定のページ構成に特化する作り** にしました。
具体的には、ヒートマップデータの計測する際にページ全体ではなく **コンテンツを構成する各要素毎に分割して計測** するようにしました。
![計測エリア分割](https://qiita-image-store.s3.amazonaws.com/0/72466/b165fec4-759a-71bb-316d-fe1ccefdba8d.png)

各ページで計測された値は計測エリア同士を対応付けた上で集計します。
こうすることで少なくとも **各コンテンツを跨いでの計測誤差** が発生しなくなります。

![対応付けできた！](https://qiita-image-store.s3.amazonaws.com/0/72466/303527e7-4dd3-c1e2-56a8-ef3a5627e1c3.png)


しかしこれではまだ不完全なのです。

## グループ化計測のポイントその２：相対値での計測
コンテンツの分割によって少なくとも要素を跨いだ誤差は解消できました。
しかし **要素内における誤差はまだ残り続けています。** 

コンテンツ１号とコンテンツ２号では概要、本文、補足について内容量が大幅に異なります。
要素を分割したとしてもそのまま絶対値で計測すると、待っているのはこんな悲しい結果です。
![はみ出し計測](https://qiita-image-store.s3.amazonaws.com/0/72466/d59dffac-1246-2e5d-8294-8f9793bcfc60.png)

ということで、絶対値計測を止めて **相対値計測** としました。
具体的には各計測エリアに対し **どの点が見られていたではなく、どれ位の割合の領域が見られていたのか** という方針に変えました。
こうすることで、各要素の内容の多寡での誤差を減らすことを狙いました。
![相対的な計測](https://qiita-image-store.s3.amazonaws.com/0/72466/342dc8a3-edde-3f81-66c8-889763ec327c.png)

これで同種別のページをグループ化しての計測ができるようになりました。めでたしめでたし。

## 実は他にもあった課題
そんなこんなでページをグループ化しての計測はできそうな気配が出てきました。しかし自分はもっと根本的な課題に気付いていなかったのです。
そもそもヒートマップは訪問者がコンテンツのどの要素に着目していたかを表すものです。
自分はその**「要素を見た」とはどうやって判断すべきか** について明確な回答を持っていませんでした。
また **それらをトラッキングするタイミングはいつが最適であるか** についても検討する必要がありました。
これらについて未だ正解は分かりませんが、当時自分が決めた内容を紹介します。

## 「要素を見た」判定
以下の2点で判断するようにしました。

1. 要素が画面内に表示されている
2. 一定時間スクロールが発生していない（またはごく微量である）

どちらも当たり前のような内容ですが解説をいれます。

1.についてはスマホWebということもあり単純化しました。
厳密にはタップなども考慮する必要があるかもしれませんが、そこから「見た」判定は厳しいと考え切り捨てました。
（PC版ではマウスカーソルを意識してなんやかんやするらしく大変そうですね）

2.については「見た」とするなら一定時間画面を制止させているはずだ、という点から入れました。
しかし画面に指を置きっぱなしにする人がいることを想定し、ある程度の微量のスクロールであれば許容するようにしました。

## トラッキングするタイミング
これは他社のツールを参考に以下のように決めました。

1. 要素を見た最初は短い時間（数秒）で一旦トラッキングする
2. 同じ画面内容の場合、以降は広めの間隔でトラッキングする

こちら側の都合としては常に小まめにトラッキングをするのが理想ではありますが、それでは通信回数が増えて訪問者への負担となります。
それらの折衷案として、まず1.で挙げたように訪問者が **特定の要素を見始めたタイミングで早めにトラッキング** します。
その後同じところを見続けているのであれば、2.にあるように広めの間隔でトラッキングします。
厳密な計測はし辛くなりますが、スマホで一度に表示される情報量を考慮すると **ある要素を30秒見た場合と35秒見た場合を厳密に区別する必要性は薄い** と判断しました。

今のところこの二つの合わせ技で上手くいっているようです。


## 終わりに
ホントは記事に挙げた内容のコードを張った方が分かりやすいんですが、執筆してる環境内にソースがないのでご容赦ください。
また **「お前のヒートマップはここが間違っている」** といったご指摘は大歓迎です。
お手間でなければバッサリとマサカリで切り倒していただけると嬉しく思います。

それでは良いクリスマスイブの夜をお過ごしください。
![クリスマス](https://qiita-image-store.s3.amazonaws.com/0/72466/66932767-edb2-708d-f4ea-fcc50a34dc92.png)