GAミント至上主義

Web Monomaniacal Developer.

Googleスプレッドシートを使って政府統計のデータをChart.jsで使えるJSONに変換する

シニアジョブで、サイトのコンテンツとして年代別就業状況の統計データを使うためにいろいろやったので流れをメモ。

データは政府の政府統計ポータルサイトe-Statのものを使いました。リンクは都道府県別のもの。
www.e-stat.go.jp

利用の際は出典の明記が必要です。
利用規約 | 政府統計の総合窓口

いろいろ探してエクセル形式でダウンロードしてGoogleスプレッドシートに読み込み、必要なものをコピペしていきます。

ちなみに私のスプレッドシートやエクセルのスキルは実務経験でいうと3週間ぐらいです。

コンテンツとしてデータがどんなものがいいかは、数字の羅列を見てもわかりません。
まずはグラフにするため、必要そうなデータをどんどん列に追加していきます。

行は年と都道府県にして、列にシニア世代を50代、60代、70歳以上という3つに分けていろいろ追加していきました。

f:id:uyamazak:20200818152939p:plain

唯一知っていると言っても過言ではない機能、ピボットテーブルを使って、データを整理してグラフにします。
知っているだけで使い方はわからないので、いい感じになるまで設定をいじりまくります。
いくつかできたら、どれがいいかなというのを周りに相談しながら決めました。
最終的には全就業者数における、各シニア世代の就業者数の割合のパーセントにしました。

f:id:uyamazak:20200818153519p:plain

グラフとデータが決まったのでChart.jsに移ります。
いい感じにグラフを表示してくれます。
www.chartjs.org


今回はVueのコンポーネントとして実装するため、ラッパーであるvue-chartjsをインストールしておきます。
vue-chartjs.org

Chart.jsで使うためには、スプレッドシートのデータがこんな感じになると嬉しいです。

  {
    "labels":[1980,1985,1990,1995,2000,2005,2010,2015],
    "datasets":[
      {"label":"50代","data":[16.69,18.53,19.08,19.95,22.89,23.11,20.34,20.07]},
      {"label":"60代","data":[7.33,7.61,8.87,10.18,10.17,11.53,14.5,15.51]},
      {"label":"70歳以上","data":[2.33,2.55,2.63,3.27,3.78,4.53,4.97,5.99]}
    ]
  }

なるべくスプレッドシートはデータだけに集中させたいので、backgroundColorなど色なんかはあとでVue側でlabelを見てくっつけることにします。

スプレッドシートのメニューの「ツール」→「スクリプトエディタ」を開いてGASを書きます。
Null合体演算子が使えない、アロー関数でインデントが狂う、StringにreplaceAllがないなど最近のJavaScriptとの微妙な違いと戦いながら雑に書いていき、こんな感じになりました。
都道府県別のも書きましたが複雑なので単純な年代別のものだけ。

function ageJson() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const rows = sheet.getSheetValues(1, 1, sheet.getLastRow(), sheet.getLastColumn());
  const labels = [];
  const dataset = [];
  const data50 = [];
  const data60 = [];
  const data70 = [];
  const dataAll = [];
  
  rows.forEach(
    function(cols, index) 
    {
      if (index === 0){
        return;
      }
      // 0が全国
      if (cols[1] !== 0) {
        return;
      }
      // 列変えたら数字変える
      year = cols[0];
      worker50 = cols[6];
      worker60 = cols[7];
      worker70 = cols[8];
      workerAll = cols[12];
      labels.push(year);
      data50.push(worker50);
      data60.push(worker60);
      data70.push(worker70);
      dataAll.push(workerAll);
    }
  );
  function toPercent(val, index) {
    return round(val / dataAll[index]*100, 2);
  }
  const result = {
    labels: labels,
    datasets: [
      {label: '50代', data: data50.map(toPercent)},
      {label: '60代', data: data60.map(toPercent)},
      {label: '70歳以上', data: data70.map(toPercent)}
    ]
  };
  
  Browser.msgBox(JSON.stringify(result));
}

// https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Math/round
function round(number, precision) {
  var shift = function (number, precision, reverseShift) {
    if (reverseShift) {
      precision = -precision;
    }  
    var numArray = ("" + number).split("e");
    return +(numArray[0] + "e" + (numArray[1] ? (+numArray[1] + precision) : precision));
  };
  return shift(Math.round(shift(number, precision, false)), precision, true);
}

他でも使えるテンプレートとしては下記のような感じ。

function myFunction() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const rows = sheet.getSheetValues(1, 1, sheet.getLastRow(), sheet.getLastColumn());
  const result = {}
  rows.forEach(
    function(cols, index) {
      // いろいろやってresultにいれてく
    }
  );
  Browser.msgBox(JSON.stringify(result));

出力結果を他のシートの書き出す方法なんかもあるんですが、今回は手軽にBrowser.msgBoxを使いました。

f:id:uyamazak:20200818155441p:plain

実行してシートに戻るとこんな感じで出力されてるので、コピペして使います。

まだ途中だけどこんな感じでChart.jsで表示できました。
f:id:uyamazak:20200818160022p:plain

色パターンはデフォルトだとグレーなので、一旦こちらのを使いました。あとでデザイナーの方にいい感じにしてもらいます。
colorbrewer2.org


microCMSのリッチテキスト内のimgタグをamp-imgに変換する

7/1に公開したシニアタイムズでは、NuxtJSとamp-moduleを使ってAMP対応を行いました。

でもmicroCMSのリッチテキストで保存している記事本文は自分で対応する必要があります。

基本的に生成されるHTMLはきれいなのでほとんど問題ないのですが、imgタグだけはamp-imgに変換する必要がありました。

ドキュメント:<amp-img> - amp.dev

タグ名だけでよければ簡単ですが、難しいのが画像の高さを指定する必要があること。表示速度のためには仕方ない。

サムネイルに使うような画像でよければ大体横長で比率も固定しやすいですが、記事内なのでどんな画像が使われるかはわかりません。

今回はいろいろ試行錯誤の結果、画像APIで固定のサイズに変換し、余白は背景色で埋めるという対応をしました。

画像APIとは

HTMLで返ってくるので正規表現で置換するのが良さそうです。まずはimgタグを抽出し、中でsrcにパラメータをつける処理をしました。

で下記のように書いてみました。

ampページのvueファイル抜粋

<script lang="ts">
// 省略
  async asyncData({ $axios, error, params }) {
    // 省略
    // 記事データ取得
    const currentPost = await $axios.$get(API_URL + `magazines/${id}`)
    // imgをamp-imagに変換する heightの指定が必須なので200で固定する
    const ampBody = currentPost.body.replace(
      /<img([^>]*)>/gi,
      (_match, sub) => {
        sub = sub.replace(
          /src="(.+)"/,
          'src="$1?width=300&height=225&fit=fill&fill-color=fafafa"'
        )
        return `<amp-img ${sub} layout="intrinsic" width="300" height="225"></amp-img>`
      }
    return {
      ampBody,
    }
  },
</script>

layout属性については下記。
amp.dev


比率が違う画像を使うと余計な余白ができてしまうという暫定対応感は否めません。

きっちりやりたいのであれば画像APIでサイズなどのメタ情報を取得できるので、これを使って横幅を計算して入れれば良さそうです。
複雑さとAPIリクエスト数が増えるのが懸念事項でしょうか。

フォーマット

NuxtJSのAMPモジュールでamp-analyticsを使う方法

NuxtJS + AMPモジュールでGoogle Analyticsのタグを貼る際、ちょっと手コヅったのでメモ。

GitHub - nuxt-community/amp-module: AMP Module for Nuxt


このページを参考に
AMP のグローバル サイトタグ  |  グローバル サイトタグ(gtag.js)  |  Google Developers

普通にAMPのページテンプレートにタグを貼ると下記のようにエスケープされてしまいます。

<amp-analytics type="gtag" data-credentials="include"><script type="application/json">
      {
        &quot;vars&quot;: {
          &quot;gtag_id&quot;: &quot;UA-*******-1&quot;,
          &quot;config&quot;: {
            &quot;UA-********-1&quot;: { &quot;groups&quot;: &quot;default&quot; }
          }
        }
      }
</script></amp-analytics>

そのため、JSON部分をcomputedなどに書いて、

  computed: {
    gaJson(): string {
      return `{
          "vars": {
            "gtag_id": "${GOOGLE_ANALYTICS_TRACKING_ID}",
            "config": {
              "${GOOGLE_ANALYTICS_TRACKING_ID}": { "groups": "default" }
            }
          }
        }`
    },

v-htmlを使って表示すればエスケープされずに出すことができます。

    <amp-analytics type="gtag" data-credentials="include">
      <script type="application/json" v-html="gaJson"></script>
    </amp-analytics>

AMPモジュールはページ内で使ったamp-系のタグに応じて必要なタグを自動で読み込んでくれるので便利です。


タグの動作確認はGoogle謹製のChrome拡張、Tag Assistantが便利です。

chrome.google.com