各都道府県の「新型コロナウイルス感染症対策サイト」公開データを集約して Googleデータポータルで可視化する

国内での新型コロナウイルス感染症が拡大を続け、収束がなかなか見通せない中、日々刻々と変化する状況を把握して先々の予測を立てるには、報告される最新情報の分析が欠かせません。

そんな中、東京都が公開した「[東京都新型コロナウイルス感染症対策サイト](https://stopcovid19.metro.tokyo.lg.jp/)」は、感染動向を分かりやすい表現で提供すると共に相談窓口など対策方法についての情報も掲載しています。このサイトはオープンソースソフトウェアとして[ソースコード](https://github.com/tokyo-metropolitan-gov/covid19)が公開されており、有志のボランティアや行政機関の手により東京都以外の各地域にも展開されています。

感染者数や感染者の属性については、各自治体が公開している情報を元にそれぞれの地域版が対策サイトシステムにインポートしているわけですが、元々同じコードベースからforkしていることから、データの構造は共通（少なくともかなり近いもの）となっているはずです。ということは、各地域版が保持しているデータを集約すれば共通のフォーマットで全国的な情報を扱え、可視化して分析可能ではないかと考えました。

ここでは、各地域版の患者情報を収集して1枚のスプレッドシートにリストアップした上で、グラフでの可視化を行ってみます。

## Googleスプレッドシートに各地域版のデータを集約する

### 手順

#### 1. Googleスプレッドシートで、スプレッドシートを新規作成する

#### 2. 「ツール→スクリプト エディタ」を開く

<img width="523" alt="qiita2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528506/0b3d02e2-a8c6-c9e2-0df8-3315a6b7e1e4.png">

#### 3. スクリプトエディタに以下のコードを記述する

```javascript:コード.gs
function myFunction() {
  var dataSources = [
    { pref: "東京都", dataURL: "https://github.com/tokyo-metropolitan-gov/covid19/raw/development/data/data.json" },
    { pref: "北海道", dataURL: "https://codeforsapporo.github.io/covid19hokkaido_scraping/patients.json" },
    { pref: "青森県", dataURL: "https://github.com/CodeForAomori/covid19/raw/aomori_development/data/data.json" },
    { pref: "岩手県", dataURL: "https://github.com/MeditationDuck/covid19/raw/development/data/data.json" },
    { pref: "宮城県", dataURL: "https://github.com/code4shiogama/covid19-miyagi/raw/development/data/data.json" },
    { pref: "秋田県", dataURL: "https://github.com/asaba-zauberer/covid19-akita/raw/development/data/data.json" },
    { pref: "山形県", dataURL: "https://github.com/yamaserif/covid19/raw/development/data/data.json" },
    { pref: "福島県", dataURL: "https://github.com/donuzium/covid19/raw/development/data/data.json" },
    { pref: "茨城県", dataURL: "https://github.com/a01sa01to/covid19-ibaraki/raw/development/data/data.json" },
    { pref: "栃木県", dataURL: "https://github.com/covid19-tochigi/covid19/raw/development/data/data.json" },
    { pref: "群馬県", dataURL: "https://github.com/SatoshiRC/covid19-gunma/raw/master/data/data.json" },
    { pref: "埼玉県", dataURL: "https://github.com/codefortoda/covid19-saitama/raw/development/data/data.json" },
    { pref: "千葉県", dataURL: "https://github.com/civictechzenchiba/covid19-chiba/raw/development/data/data.json" },
    { pref: "神奈川県", dataURL: "https://www.pref.kanagawa.jp/osirase/1369/data/data.json" },
    { pref: "新潟県", dataURL: "https://github.com/CodeForNiigata/covid19/raw/development/data/data.json" },
    { pref: "富山県", dataURL: "https://github.com/Terachan0117/covid19-toyama/raw/development/data/data.json" },
    { pref: "石川県", dataURL: "https://github.com/Retsuki/covid19-ishikawa/raw/development/data/data.json" },
    { pref: "福井県", dataURL: "https://github.com/westar7/fukui-covid19/raw/master/data/data.json" },
    { pref: "山梨県", dataURL: "https://github.com/covid19-yamanashi/covid19/raw/development/data/data.json" },
    { pref: "長野県", dataURL: "https://github.com/kanai3id/covid19/raw/development/data/data.json" },
    { pref: "岐阜県", dataURL: "https://github.com/CODE-for-GIFU/covid19/raw/development/data/data.json" },
    { pref: "静岡県", dataURL: "https://github.com/hiroyuki-ichikawa/covid19/raw/development/data/data.json" },
    { pref: "愛知県", dataURL: "https://github.com/code4nagoya/covid19/raw/development/data/data.json" },
    { pref: "三重県", dataURL: "https://github.com/FlexiblePrintedCircuits/covid19-mie/raw/develop/data/data.json" },
    { pref: "滋賀県", dataURL: "https://github.com/Shiga-pref-org/covid19/raw/development/data/data.json" },
    { pref: "京都府", dataURL: "https://github.com/stopcovid19-kyoto/covid19/raw/development/data/data.json" },
    { pref: "大阪府", dataURL: "https://github.com/codeforosaka/covid19/raw/development/data/data.json" },
    { pref: "兵庫県", dataURL: "https://github.com/stop-covid19-hyogo/covid19/raw/development/data/patients.json" },
    { pref: "奈良県", dataURL: "https://github.com/code4nara/covid19/raw/development/data/data.json" },
    { pref: "和歌山県", dataURL: "https://github.com/sys-cube/covid19/raw/development/data/data.json" },
    { pref: "鳥取県", dataURL: "https://github.com/tottori-covid19/covid19/raw/master/data/data.json" },
    { pref: "島根県", dataURL: "https://github.com/TaigaMikami/shimane-covid19/raw/shimane/data/patients.json" },
    { pref: "岡山県", dataURL: "https://github.com/stopcovid19-okayama/covid19/raw/development/data/patients.json" },
    { pref: "広島県", dataURL: "https://github.com/tatsuya1970/covid19/raw/development/data/data.json" },
    { pref: "山口県", dataURL: "https://github.com/nishidayoshikatsu/covid19-yamaguchi/raw/development/data/data.json" },
    { pref: "徳島県", dataURL: "https://github.com/yuta-kusuno/covid19-tokushima/raw/development/data/data.json" },
    { pref: "香川県", dataURL: "https://github.com/i15317/covid19/raw/master/data/data.json" },
    { pref: "愛媛県", dataURL: "https://github.com/ehime-covid19/covid19/raw/master/data/data.json" },
    // 高知県の分は「新型コロナウイルス感染症関連オープンデータ（https://www.pref.kochi.lg.jp/soshiki/111301/2020041300141.html）」を参照
    { pref: "高知県", dataURL: "https://www.pref.kochi.lg.jp/soshiki/111301/files/2020041300141/390003_kochi_covid19_patients.csv" },
    { pref: "福岡県", dataURL: "https://github.com/Code-for-Fukuoka/covid19-fukuoka/raw/development/data/data.json" },
    { pref: "佐賀県", dataURL: "https://github.com/codeforsaga/covid19/raw/development/data/data.json" },
    // 長崎県の分は「長崎県新型コロナウイルス感染症陽性患者発表情報（https://data.bodik.jp/dataset/420000_covidpatients）」を参照
    { pref: "長崎県", dataURL: "https://data.bodik.jp/api/action/datastore_search?resource_id=de7ce61e-1849-47a1-b758-bca3f809cdf8" },
    { pref: "熊本県", dataURL: "https://github.com/codeforkumamoto/covid19/raw/development/data/data.json" },
    { pref: "大分県", dataURL: "https://github.com/covid19-oita/data/raw/master/json/data.json" },
    { pref: "宮崎県", dataURL: "https://github.com/covid19-miyazaki/covid19/raw/development/data/data.json" },
    { pref: "鹿児島県", dataURL: "https://github.com/codeforkagoshima/covid19/raw/development/data/data.json" },
    { pref: "沖縄県", dataURL: "https://github.com/Code-for-OKINAWA/covid19/raw/development/data/data.json" }
  ];
  var cellValues = [
    ["都道府県", "日付", "居住地", "年代", "性別", "退院"]
  ];

  // 各地域版のデータを処理
  dataSources.forEach(function(dataSource) {
    var response = UrlFetchApp.fetch(dataSource.dataURL);
    var patientData;

    if (dataSource.pref === "高知県") {
      var contentText = response.getContentText("Shift_JIS");
      var rows = Utilities.parseCsv(contentText);

      patientData = [];
      for (var i in rows) {
        if (i === "0") { continue; }

        patientData.push({
          '公表_年月日': rows[i][4],
          '患者_居住地': rows[i][7],
          '患者_年代': rows[i][8],
          '患者_性別': rows[i][9]
        })
      }
    } else {
      var contentText = response.getContentText();
      var jsonString = contentText
      .replace("　", " ")
      .replace(/[０-９]/g, function(s) { return String.fromCharCode(s.charCodeAt(0) - 0xfee0); });
      var json = JSON.parse(jsonString);

      switch(dataSource.pref) {
        case "北海道":
        case "兵庫県":
        case "岡山県":
          patientData = json.data;
          break;
        case "高知県":
          break;
        case "長崎県":
          patientData = json.result.records;
          break;
        default:
          patientData = json.patients.data;
          break;
      }
    }

    // 患者情報を配列に格納する
    for (var i in patientData) {
      var patient = patientData[i];
      var date = (patient['date'] || patient['日付'] || patient['公表_年月日'] || patient['判明日'] || '').replace('市25例目:', '') || (patient['リリース日'] || patient['発表日'] || patient['発生判明日']).replace(/T.*/, '');
      var place = patient['居住地'] || patient['住居地'] || patient['患者_居住地'];
      var age = (patient['年代'] || patient['患者_年代'] || '').replace("歳代", "代").trim().replace(/^(\d+)$/, "$1代");
      var gender = patient['性別'] || patient['患者_性別'];
      var discharged = patient['退院'] || patient['退院済フラグ'];

      date = date.replace(/^2021/, '2020');

      if (patient['年代・性別']) {
        [age, gender] = patient['年代・性別'].split('代');
        if (age.match(/^(\d+)$/)) {
          age += '代';
        }
      }

      cellValues.push([
        dataSource.pref,
        date,
        place,
        age,
        gender,
        discharged
      ]);
    }
  })

  // スプレッドシートに反映する
  var sheet = SpreadsheetApp.getActiveSheet();
  sheet.clear();
  sheet.getRange(1, 1, cellValues.length, cellValues[0].length).setValues(cellValues);
}
```

#### 4. ツールバーの「実行」ボタンでコードを実行する

しばらく待つと、スプレッドシートに各地域版の患者情報がリストアップされます。

<img width="776" alt="qiita1.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528506/0ee2ea58-9f9f-86b8-a077-94224cd77d05.png">

### 収集スクリプトについて

各地域版のサイトURLとリポジトリーは、[こちらの派生サイト情報](https://github.com/tokyo-metropolitan-gov/covid19/blob/development/FORKED_SITES.md)を参照しました（同じ都道府県で複数ある場合は、いずれか一つ）。各リポジトリーまたは公開サイトから、患者情報が格納されているJSONファイルを取得しています。

オープンソースということもあって独自に改良を行っている地域もあり、オリジナルとは以下の点が異なる場合がありました（外部からデータを取得しにくい形式になっている県は今回省略しました）。

* JSONファイルの位置（パス名）が異なる
* JSONファイルが分割されている
* JSONファイルのデータ構造（階層構造）が異なる
* プロパティー名が異なる
* 2のプロパティーが1つに統合されている
* 値に表記揺れがある（例: 「20代」「20歳代」「２０歳代」「20」）

この違いを吸収するよう、読み込み時に修正を施すようにしてあります。

## Googleデータポータルで可視化する

収集したデータの活用例として、Googleデータポータルで可視化してみましょう。

[Googleデータポータル](https://datastudio.google.com/)を開き、「空のレポート」を選択して新規作成します。

<img width="1181" alt="qiita3.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528506/120a4853-5c2c-4f97-9884-12a9dabc733d.png">

「データのレポートへの追加」画面で「Googleスプレッドシート」を選択し、

<img width="1155" alt="qiita4.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528506/ad22739a-4dd3-ee99-394f-ce743d5a33f6.png">

前節でデータを集約したスプレッドシートを指定して「追加」ボタンを押します。

<img width="853" alt="qiita5.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528506/114ceeec-6fc3-a040-42d8-60bd6036bbbb.png">

これで、スプレットシートのデータが紐付けられ、レポートの作成に利用できるようになりました。

レポートの編集画面では、「グラフの追加」ボタンのメニューから様々な形式のグラフを貼り付けられます。

<img width="743" alt="qiita6.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528506/86847b7e-e8d4-65a3-c367-c4afed5b51c5.png">

以下はレポートの一例です。

男女別・年齢別の割合の円グラフと、感染者数累計の棒グラフ
<img width="1199" alt="qiita7.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528506/092508b8-bb27-2edc-72d8-51a078b52f1e.png">

感染者数累計の推移（都道府県別上位の積み上げ）
<img width="1197" alt="qiita8.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528506/be601bb8-bf85-3c84-e82b-faf3dd3b784c.png">

感染者数累計の推移（年齢別の積み上げ）
<img width="1196" alt="qiita9.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528506/d9a03595-8d11-bac1-e40c-cb36e6d6ef0e.png">

以上、各都道府県の「新型コロナウイルス感染症対策サイト」公開データの集約と、グラフでの可視化を行う方法をご紹介しました。