Google Analytics APIの結果をGoogle Charts APIでグラフに表示する


どうも、WACULでフロントエンドエンジニアをしている @Quramy です。

## はじめに
WACULで運営している[AIアナリスト](https://wacul-ai.com)はGoogle Analytics(以下GA)からデータを取得し、さまざまな解析を行うサービスです。
なので、日々GAのAPIやデータ可視化と戯れているわけですが、今日はGAのデータを可視化する方法について書きたいと思います。

## どうやってやるか？
GAのAPI結果をグラフなどで表示する方法は、いくつか考えられます:

1. [GA Embed API](https://developers.google.com/analytics/devguides/reporting/embed/v1/) を利用する
2. APIの結果を[Google Charts API](https://developers.google.com/chart/)や[chart.js](http://www.chartjs.org/), [d3.js](http://d3js.org/) を使って自分で可視化する

大半の場合においては 1.で事が足りると思います。
ただし、Embed APIは提供されるAPIがハイレベルであり、「APIを実行する」部分と「可視化を行う」部分が密に結合している感があります。
このため、「GAのAPIは既に実行済みであり、すでに結果のオブジェクトを持っている」場合や、「GA APIのリクエストに独自のproxyを経由しているため、APIのエンドポイントが異なる」場合は利用が難しくなります。
このようなケースでは、2.を選ぶことになるでしょう。

このエントリでは、2.の方法を説明していきます。

## やってみよう
GAのAPIを可視化するにあたり、手っ取り早く使えるライブラリはGoogle Charts APIです。

まずはPieChartを表示してみましょう。 [Charts APIのPie Chartのガイド](https://developers.google.com/chart/interactive/docs/gallery/piechart) に"CODE IT YOURSELF ON JSFIDDLE" というリンクがあるので、こいつを叩いてJS Fiddleの画面を出しておきます。

```js
google.setOnLoadCallback(drawChart);
function drawChart() {

  var data = google.visualization.arrayToDataTable([
    ['Task', 'Hours per Day'],
    ['Work',     11],
    ['Eat',      2],
    ['Commute',  2],
    ['Watch TV', 2],
    ['Sleep',    7]
  ]);

  var options = {
    title: 'My Daily Activities'
  };

  var chart = new google.visualization.PieChart(document.getElementById('piechart'));

  chart.draw(data, options);
}
```

単純にPieChartを描画するサンプルコードですね。今回はこれをカスタマイズして、GAのAPIの結果を描画出来るようにしてみたいと思います。

続いて、[GAのQuery Explorer](https://ga-dev-tools.appspot.com/query-explorer/) から、下記のようにクエリを組んで実行してみましょう。
チャネルグループ毎のセッション数を取得するシンプルなクエリです。
余談ですが、今回題材にしているGAのViewは @QuramyのQiitaのViewです。[Qiitaのアカウント設定画面](https://qiita.com/settings/analytics) から, GA連携できるので、やってみることをオススメします。

![Query_Explorer_—_Google_Analytics_Demos___Tools.png](https://qiita-image-store.s3.amazonaws.com/0/8808/98b5cbfb-2a5f-e477-77a8-17431a5946ce.png)

"Run Query"をクリックすると、APIが実行されるので、このときのレスポンスを見てみましょう。
UI上は表示されていませんが、Query Explorerで実行するQueryには必ず `output=dataTable`というパラメータが付与されます。
このため、レスポンスデータには`dataTable`というObjectがぶら下がります(下図):

![Query_Explorer_—_Google_Analytics_Demos___Tools 2.png](https://qiita-image-store.s3.amazonaws.com/0/8808/f9010787-0865-73c6-2552-15790a88acf3.png)

[GAのcore reporting API](https://developers.google.com/analytics/devguides/reporting/core/v3/reference?hl=ja#output)にも、

> dataTable - Data Table オブジェクトを含み、dataTable プロパティをレスポンスで出力します。この Data Table オブジェクトは、Google グラフのビジュアル表示で直接使用できます。

とあります。

一方、[Google Charts APIのDataTable Classのリファレンス](https://developers.google.com/chart/interactive/docs/reference#constructor_://developers.google.com/chart/interactive/docs/reference#constructor_1)を読むと、以下のように引数にオブジェクトリテラルを渡すことで、DataTableのインスタンスが簡単に作れる、と記載されています:

> Examples
> The following example demonstrates instantiating and populating a DataTable with a literal string, with the same data as shown in the JavaScript example above:
> 
```
var dt = new google.visualization.DataTable({
    cols: [{id: 'task', label: 'Task', type: 'string'},
           {id: 'hours', label: 'Hours per Day', type: 'number'}],
    rows: [{c:[{v: 'Work'}, {v: 11}]},
           {c:[{v: 'Eat'}, {v: 2}]},
           {c:[{v: 'Commute'}, {v: 2}]},
           {c:[{v: 'Watch TV'}, {v:2}]},
           {c:[{v: 'Sleep'}, {v:7, f:'7.000'}]}]
    }, 0.6);
```

なるほどね、APIの実行結果をDataTableに食わせれば瞬殺じゃん！

先ほどのJS Fiddleのコードを下記のように書き換えれば良さそうです。

```js
google.setOnLoadCallback(drawChart);

function drawChart() {
  var queryUrl = /* ここにQuery Explorer のAPI Query URI を貼り付ける*/
  $.ajax({
    url: queryUrl + '&output=dataTable'
  }).done(function(response) {
    var data = new google.visualization.DataTable(response.dataTable);
    var options = {
      title: 'Channel Group Access'
    };

    var chart = new google.visualization.PieChart(document.getElementById('piechart'));

    chart.draw(data, options);
  });
}
```

実行してみると、、、、

![Pie_Chart_Example_-_JSFiddle.png](https://qiita-image-store.s3.amazonaws.com/0/8808/cfa52deb-5e7b-cb28-c141-b0d03dc5fa61.png)

いや、これは違う。何かがおかしい。

原因は至極単純で、`ga:sessions` は数値型ですが、APIの戻り値上はStringとなってしまっているため、計算が出来てなくなってしまうからです。「直接使用できます」が聞いて呆れるぜ...

実際、Embed APIのWeb Component実装のコードを紐解いてみると、[https://github.com/GoogleWebComponents/google-analytics/blob/master/google-analytics-chart.html#L389](https://github.com/GoogleWebComponents/google-analytics/blob/master/google-analytics-chart.html#L389) にレスポンスの型変換を明示的にやっている部分が見つかりました。

```js
    /**
     * The analytics api erroneously return some values as strings that are
     * supposed to be numbers. This function fixes that.
     * @param {Object} dataTable - The dataTable data.
     */
    function ensureProperDataTableTypes(dataTable) {
      for (var i = 0; i < dataTable.rows.length; i++) {
        var row = dataTable.rows[i];
        for (var j = 0; j < row.c.length; j++) {
          if (dataTable.cols[j].type === 'number') {
            row.c[j].v = Number(row.c[j].v);
          }
        }
      }
    }
```

Embed APIにならって、先ほどのFiddleにこの`ensureProperDataTableTypes`を仕込んで再実行してみると、、、

```js
google.setOnLoadCallback(drawChart);

function ensureProperDataTableTypes(dataTable) {
  for (var i = 0; i < dataTable.rows.length; i++) {
    var row = dataTable.rows[i];
    for (var j = 0; j < row.c.length; j++) {
      if (dataTable.cols[j].type === 'number') {
        row.c[j].v = Number(row.c[j].v);
      }
    }
  }
  return dataTable;
}

function drawChart() {
  var queryUrl = /* ここにQuery Explorer のAPI Query URI を貼り付ける*/
  $.ajax({
    url: queryUrl + '&output=dataTable'
  }).done(function(response) {
    var data = new google.visualization.DataTable(ensureProperDataTableTypes(response.dataTable));
    var options = {
      title: 'Channel Group Access'
    };

    var chart = new google.visualization.PieChart(document.getElementById('piechart'));

    chart.draw(data, options);
  });
}
```

おー、ようやくPieChartが描画された([JS Fiddleにも同じものを置いてます](https://jsfiddle.net/Quramy/890ajtg7/))。

![Google_Analytics_and_Charts_example_-_JSFiddle.png](https://qiita-image-store.s3.amazonaws.com/0/8808/b7138082-c52e-4c8e-8c12-88a95212b747.png)


ここまでをまとめると、下記となります：

* GAのAPIに `output=dataTable` を付与するとdataTable風のレスポンスが貰える
* APIの結果のdataTableオブジェクトについて、適切な型変換を実施する必要がある
* Charts APIのDataTable Classのコンストラクタに食わせると、chart描画用のDataTableが作れる

ちなみに、上述の`ensureProperDataTableTypes` ですが、DataTableで利用可能な型には`number`, `string` 以外にも `date`, `datetime`, `dayOfWeek`, `boolean`があるので、真面目にやるのであれば、こいつらも変換する必要があります。


## DataTableとDataView
このエントリではPieChartを例にして説明してきましたが、他のChartTypeでは、より複雑なDataTableを使うこともあるでしょう。

例えば、BubbleChartはData Formatとして、"名前, 数値(x座標値), 数値(y座標値), ラベル, 数値(円の大きさ)" のパターンを要求します
一方、GAのAPIの結果は、"demension1, demension2, ..., metric1, metric2,..." という列の並びになってしまうため、直接DataTableを渡してもグラフの描画が出来ないパターンがあります。 

このような時は、DataViewクラスと`setColumns` を利用して、元のDataTableに対する列のマッピングを変更すると良いです:

```js
    var data = new google.visualization.DataTable(ensureProperDataTableTypes(response.dataTable));
    var view = new google.visualization.DataView(data);
    view.setColumns([0,2,3,0,1]); // dataTableとDataViewの列マッピングを指定
    //:
    chart.draw(view, options);
```

## おまけ

このエントリのおまけとして、GAのQuery Explorer上に直接グラフ表示を行う[Chrome Extension](https://chrome.google.com/webstore/detail/google-analytics-charts/kiohndocpaamknofhkcaoiibjabgenij?hl=ja)を作ってみました。

![Query_Explorer_—_Google_Analytics_Demos___Tools 4.png](https://qiita-image-store.s3.amazonaws.com/0/8808/d48b8511-62e1-7b0b-f4d1-5f5b3a9f8d39.png)

このExtensionを作るにあたり、

* Chrome Extension(webRequest, sandBox)
* Charts API
* Analytics core reporting API
* AngularJS

というGoogleどっぷりスタックを組んだのですが、Charts APIがChrome Extensionに組み込み辛すぎて泣けました。
この辺の苦労話もそのうち書きたいけど、今日はもう疲れたので、ここいらで筆を休めたいと思います。