初老のボケ防止日記

おっさんのひとりごとだから気にしないようにな。

スポンサーリンク

Python Pandasをさわってみる(終)



Pandasを使ってデータを加工して地図にマッピングしてみたら面白そうだ。

前回の記事まではPandasを色々と試してみたけれど、実は今回のことを一番最初にやろうと思っていたのだ。ぶっちゃけEXCELからPandasなしでゴリゴリしてもよかったんだけど、流行りモノには手を出したかったのでずいぶんと回り道をしてきたさ。

osa030.hatenablog.com

ということで、マッピングしやすい形式にして地図上に表示してみようじゃないか。

動作環境

環境

OS Windows 8.1(64bit)
Python 2.7.9(32bit)
Pandas 0.16.2
その他 Google Chart API

対象データ

政府が公開している統計データEXCELファイルを変換したCSVファイル。フォーマットは以下。

year 西暦
prefecture 都道府県名
young 0-14歳の人口
adult 15-64歳の人口
elder 65歳以上の人口

詳細は、以下の記事を参照

osa030.hatenablog.com


Pandasの詳細は以下を参考にした。

pandas: powerful Python data analysis toolkit — pandas 0.16.2 documentation


Google Chartsの詳細は以下を参考にした。

developers.google.com

JSONに吐く

こんな感じのシンプルな構成。都道府県をキーにしてるのは地図マッピングで利用するGoogle Charts APIの為。

KEY VALUE
都道府県名 1970-2014の高齢化率の配列

以下がJSON出力のスクリプト。

  • to_json.py
#! /usr/bin/python
# -*- coding: utf-8 -*-

import pandas as pd
import re
import codecs
import json

df = pd.read_csv("./1970-2014.csv", na_values='-')
df['aging'] = df.elder / (df.young + df.adult + df.elder) * 100

prefectures = {}
for prefecure in df.prefecture.unique():
    agings = df[df.prefecture == prefecure].sort(columns='year')['aging'].tolist()
    if re.match('(\S*)[都府県]$', prefecure):
        prefecure = unicode(prefecure)[:-1].encode('utf-8')

    prefectures[prefecure] = [0 if pd.isnull(x) else round(x, 2) for x in agings]

ymin = int(df.year.min())
ymax = int(df.year.max())
outfile = '%s-%s-aging.json' % (ymin, ymax)
output = {}
output['duration'] = {'from': ymin, 'to': ymax}
output['prefectures'] = prefectures
with codecs.open(outfile, "w", "utf-8") as f:
    json.dump(output, f, indent=2, ensure_ascii=False)

都道府県名をユニークで取得して、都道府県名をキーとして該当都道府県の高齢化率を配列で突っ込んでいる。この時、都道府県名から「都府県」を削除している。
ここでハマったのは、正規表現で日本語はマッチするんだけど、re.sub()で削除したり、そのまま文字列の末尾1文字を削除するとその後エラーがでてしまう。これはstrのまま扱うことが問題のようなので一度unicodeクラスにして処理をしてからまたstrに戻している。Python2.x系は日本語の扱いは色々とやっかいだ*1
なぜそのような処理をしているのかはGoogle Chartsに合わせるため*2。あと、例の沖縄県のNaNのデータがそのままNANと出力されてしまうので、そこは0補正。"duration"はなくてもベタでいい気がしたけどさみしいので付けてみた。
本当はPandasを使ってJSONに保存したったんだけど、今回みたいな形式にするような柔軟性はAPI仕様をみても現時点はなさげだったので、そこは美しくないのだけれど堪忍してくだされ。

$ python to_json.py
$ ls 1970-2014-aging.json
1970-2014-aging.json
$ head -n 10 1970-2014-aging.json
{
  "duration": {
    "to": 2014,
    "from": 1970
  },
  "prefectures": {
    "北海道": [
      5.77,
      5.96,
      6.21,

それっぽいデータが入っているようだ。

JSONを読み込んで地図上にプロットする。

本当は全部Pythonでやりたいところであったが、グラフの描画結果を画像で取得するAPIは廃止されているので、しょうがないHTMLでやるか。

  • index.html
<!DOCTYPE html>
<html>
<head>
    <link href='http://fonts.googleapis.com/css?family=Inconsolata' rel='stylesheet' type='text/css'>
    <style>
        #title {
            position: absolute;
            top: -10px;
            left: 10px;
            z-index: 2;
            font-size: 32px;
            font-family: 'Inconsolata';
            font-weight: bold;
            color: #37474F;
        }
    </style>
    <script type="text/javascript" src="https://www.google.com/jsapi"></script>
    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
    <script type="text/javascript">
        google.load("visualization", "1", {packages: ["geochart"]});

        var year_start;
        var agingdata;
        $(function () {
            $.getJSON("1970-2014-aging.json", function (data) {
                var year_from = data['duration']['from'];
                var year_to   = data['duration']['to'];
                $('#year').text(year_from);
                $('#slider').attr('max', year_to - year_from);

                year_start = year_from;
                agingdata  = data['prefectures'];
                drawRegionsMap();
            });

            $('input[type=range]').on('input', function () {
                $('#year').text(year_start + parseInt($(this).val()));
            });
            $('input[type=range]').change(function () {
                var val = $(this).val();
                updateRegionsMap(parseInt(val));
            });

        });

        var dataTable;
        var geoChart;
        var chartOptions = {
            region: 'JP',
            resolution: 'provinces',
            datalessRegionColor: '#FFFFFF',
            backgroundColor: '#ECEFF1',
            colorAxis: {
                minValue: 0,
                maxValue: 35,
                colors: ['#448AFF', '#FF5252']
            },
        };
        function drawRegionsMap() {
            var data = [];
            data.push(['prefecture', 'agingrate'])
            for (key in agingdata) {
                var agingrate = agingdata[key][0];
                data.push([key, agingrate]);
            }

            dataTable = google.visualization.arrayToDataTable(data);
            geoChart  = new google.visualization.GeoChart(document.getElementById('regions_div'));
            geoChart.draw(dataTable, chartOptions);
        }

        function updateRegionsMap(index) {
            for (var y = 0; y < dataTable.getNumberOfRows(); y++) {
                var prefecture = dataTable.getValue(y, 0);
                var agingrate  = agingdata[prefecture][index];
                dataTable.setValue(y, 1, agingrate);
            }
            geoChart.draw(dataTable, chartOptions);
        }
    </script>
</head>
<body>
<div id="title"><p id="year"></p></div>
<div id="regions_div"></div>
<input type=range id="slider" min=0 value="0" autofocus>
</body>
</html>

pythonスクリプトで生成した"1970-2014-aging.json"を読み込んでGoogle Chartsの「GeoChart」でグラフ表示させている。スライダーをいじると異なる年代の高齢化率を都道府県上に色付けしたグラフが表示される。

  • 1970年の高齢化率

f:id:osa030:20150625190000j:plain

  • 2014年の高齢化率

f:id:osa030:20150625190002j:plain

で、これをgifにしたらどうかと。

  • 1970年-2014年の高齢化率の推移

f:id:osa030:20150625190033g:plain

高齢化率だと全国まんべんなく真っ赤になるので怖いだけでした。
もう少し地域毎に差がつくようなデータを使って時系列で色分けしたら信長の野望っぽくなったのになあと思ったけれど、そういうおいしいデータは無料で公開されていないのだよね。


2050 老人大国の現実―超高齢化・人口減少社会での社会システムデザインを考える

2050 老人大国の現実―超高齢化・人口減少社会での社会システムデザインを考える

*1:特にWindowsの場合、Pythonインストール時のデフォルトエンコーディングがutf-8じゃないしコンソールはcp932で出力しないと文字化けするのでさらに面倒

*2:じゃあ何故"北海道"は"北海"にしないでいいのかは謎だ

スポンサーリンク