naoのブログ ~エンジニアスキルに生成演算子を作用させたい~

日々アウトプットしていくことを目標に、だらだらと書いていきます。

アルゴリズムの基礎: クイックソート

クイックソート

フローチャートは、以下の本を参考に作成しました。

丁寧な解説は上記の本など参考してもらうとして、 クイックソートの基本方針は以下のとおりです。

  • 手前から基準値よりも大きい値をさがす
  • 後ろから基準値よりも小さい値をさがす
  • それらを交換する

フローチャート

上記の基本方針をもとに、実行されています。 また、最後に基準値を正しい場所に移動する処理も追加しています。

f:id:nao_000:20190630170954p:plain

以下、上記のフローチャートpythonで実行した例です。

import random

N = 9
# get random array
arr = list(range(1, N+1))
random.shuffle(arr)
print("inital array: {0}".format(arr))

# initial value
left = 0
right = N -1

def quick_sort(arr, left, right):
  i = left + 1
  k = right
  _ref_value = arr[left] # reference value
  _tmp = None
  print("start: left={0}, right={1}, arr[left:right+1]={2}".format(left, right, arr[left:right+1]))
  print("\t _ref_value={0}".format(_ref_value))

  while (i < k):
    # looking for a value bigger than _ref_value
    while (arr[i] < _ref_value and i < right):
      i = i + 1
    # looking for a value smaller than _ref_value
    while (arr[k] >= _ref_value and left < k):
      k = k - 1

    if (i < k):
      print("\t i={0}, arr[i]={1}, k={2}, arr[k]={3}".format(i, arr[i], k, arr[k]))
      _tmp = arr[i]
      arr[i] = arr[k]
      arr[k] = _tmp

  # replace refrence value
  if (arr[left] > arr[k]):
    _tmp = arr[left]
    arr[left] = arr[k]
    arr[k] = _tmp
    print("\t left={0}, arr[left] = {1}, k={2}, arr[k]={3}".format(left, arr[left], k, arr[k]))

  print("\t result: {0}".format(arr))

quick_sort(arr, left, right)

以下、実行例です。 実行すると、基準値5を境にして、[3,4,1,2], [5], [7,8,9,6]のようにソートされます。

inital array: [5, 4, 8, 7, 3, 2, 1, 9, 6]
start: left=0, right=8, arr[left:right+1]=[5, 4, 8, 7, 3, 2, 1, 9, 6]
   _ref_value=5
   i=2, arr[i]=8, k=6, arr[k]=1
   i=3, arr[i]=7, k=5, arr[k]=2
   left=0, arr[left] = 3, k=4, arr[k]=5
   result: [3, 4, 1, 2, 5, 7, 8, 9, 6]

最後にarr[k]に基準値を入れています。 そのため、以下の図のようになっています。

  • [left, k-1]は基準値よりも小さい値の配列
  • kの位置には実行時に決定された基準値
  • [k+1, right]は基準値よりも大きい値の配列

しかし、基準値以外は正しく順番にソートされていません。 残りの部分に対して、正しくソートするために、再び[left, k-1]および[k+1, right]の 配列に対して、上記の操作を再帰的に行う必要があります。

f:id:nao_000:20190630171035p:plain

再帰的に実行

quick_sort()を再帰的に実行して、全体をソートします。 以下のフローチャートにて、オレンジ部分が新たに追加した箇所です。

このように再帰的に呼び出しを行うことで、配列のすべての値が正しくソートされるようになります。

f:id:nao_000:20190630171110p:plain

pythonでの実装例を示します。

import random

N = 9
# get random array
arr = list(range(1, N+1))
random.shuffle(arr)
print("inital array: {0}".format(arr))

# initial value
left = 0
right = N -1

def quick_sort(arr, left, right):
  i = left + 1
  k = right
  _ref_value = arr[left] # reference value
  _tmp = None
  print("start: left={0}, right={1}, arr[left:right+1]={2}".format(left, right, arr[left:right+1]))
  print("\t _ref_value={0}".format(_ref_value))

  while (i < k):
    # looking for a value bigger than _ref_value
    while (arr[i] < _ref_value and i < right):
      i = i + 1
    # looking for a value smaller than _ref_value
    while (arr[k] >= _ref_value and left < k):
      k = k - 1

    if (i < k):
      print("\t i={0}, arr[i]={1}, k={2}, arr[k]={3}".format(i, arr[i], k, arr[k]))
      _tmp = arr[i]
      arr[i] = arr[k]
      arr[k] = _tmp

  # replace refrence value
  if (arr[left] > arr[k]):
    _tmp = arr[left]
    arr[left] = arr[k]
    arr[k] = _tmp
    print("\t left={0}, arr[left] = {1}, k={2}, arr[k]={3}".format(left, arr[left], k, arr[k]))

  print("\t result: {0}".format(arr))

  # call quick_sort() recursively
  if (left < k - 1):
    quick_sort(arr, left, k - 1)
  if (k + 1 < right):
    quick_sort(arr, k + 1, right)

quick_sort(arr, left, right)

以下に実行結果の一例です。

inital array: [7, 4, 9, 2, 5, 6, 1, 3, 8]
start: left=0, right=8, arr[left:right+1]=[7, 4, 9, 2, 5, 6, 1, 3, 8]
   _ref_value=7
   i=2, arr[i]=9, k=7, arr[k]=3
   left=0, arr[left] = 1, k=6, arr[k]=7
   result: [1, 4, 3, 2, 5, 6, 7, 9, 8]
start: left=0, right=5, arr[left:right+1]=[1, 4, 3, 2, 5, 6]
   _ref_value=1
   result: [1, 4, 3, 2, 5, 6, 7, 9, 8]
start: left=1, right=5, arr[left:right+1]=[4, 3, 2, 5, 6]
   _ref_value=4
   left=1, arr[left] = 2, k=3, arr[k]=4
   result: [1, 2, 3, 4, 5, 6, 7, 9, 8]
start: left=1, right=2, arr[left:right+1]=[2, 3]
   _ref_value=2
   result: [1, 2, 3, 4, 5, 6, 7, 9, 8]
start: left=4, right=5, arr[left:right+1]=[5, 6]
   _ref_value=5
   result: [1, 2, 3, 4, 5, 6, 7, 9, 8]
start: left=7, right=8, arr[left:right+1]=[9, 8]
   _ref_value=9
   left=7, arr[left] = 8, k=8, arr[k]=9
   result: [1, 2, 3, 4, 5, 6, 7, 8, 9]

今後は、実際にpostgresqlでのクイックそーその実装部分に関して勉強して記事にしたいです。

アルゴリズムの基礎: 挿入ソート

アルゴリズムの基礎

勉強がてらに挿入ソートの復習とpythonでの実装をしてみたことのメモです。

単純挿入法(挿入ソート)

整列済みと未整列のデータがあって、未整列のデータを整列済みのデータ列に一つずつ挿入していく方法です。 ここでは、N個の数字を昇順にソートするアルゴリズムとして、挿入ソートを考えてみます。

なお、フローチャートは以下の本を参考に作成しました。

アルゴリズムを、はじめよう

アルゴリズムを、はじめよう

今回のソートでは、リソースは限定しており、データを交換するためのtmp(1つのデータが入るだけ)と元のデータ配列でやりくりします。 そのため、値の入れ替え時にはバブルソートのように一つずつずれていくような動きをします。

ソート例

例として、以下のような5つの数字がランダムに並んでいる場合を考えます。 (tmpは挿入ソート時にデータの格納先として利用します)。 これを挿入ソートに従って、途中まで並べてみます。

f:id:nao_000:20190602121551p:plain

1順目 - ソート対象の2つめの箱の値「2」をtmpに退避する - 1つめの箱の値「3」と2つめの箱の値「2」を比較 - 1つめの箱の値のほうが大きいため、値を入れ替える - 1つめの箱の値 → 2つめの箱の値, tmp(2つめの箱の値) → 1つめの箱の値 - 結果: 2 , 3, 5, 1, 4

f:id:nao_000:20190602121634p:plain

2順目 - 2つめの箱の値「3」と3つめの箱の値「5」を比較 - 2つめの箱の値のほうが小さいため、既に昇順にソートされているので入れ替えは不要 - 結果: 2 , 3, 5, 1, 4

f:id:nao_000:20190602121711p:plain

3順目 - 3つめの箱の値「5」と4つめの箱の値「1」を比較 - 3つめの箱の値のほうが大きいため、入れ替える - 3つめの箱の値 → 4つめの箱の値, tmp(4つめの箱の値) → 3つめの箱の値 - 結果: 2 , 3, 1, 5, 4

f:id:nao_000:20190602121731p:plain

  • 続けて、2つめの箱の値「3」と3つめの箱の値「1」を比較
    • 2つめの箱の値のほうが大きいため、入れ替える
      • 2つめの箱の値 → 3つめの箱の値, tmp(3つめの箱の値) → 2つめの箱の値
      • 結果: 2 , 1, 3, 5, 4

f:id:nao_000:20190602121746p:plain

  • さらに、1つめの箱の値「2」と2つめの箱の値「1」を比較
    • 1つめの箱の値のほうが大きいため、入れ替える
      • 1つめの箱の値 → 2つめの箱の値, tmp(2つめの箱の値) → 1つめの箱の値
      • 結果: 1, 2 , 3, 5, 4

f:id:nao_000:20190602121803p:plain

4順目 - 4つめの箱の値「5」と5つめの箱の値「4」を比較 - 4つめの箱の値のほうが大きいため、入れ替える - 4つめの箱の値 → 5つめの箱の値, tmp(5つめの箱の値) → 4つめの箱の値 - 結果: 1, 2, 3, 4, 5

f:id:nao_000:20190602121820p:plain

フローチャート

上記の実行例からも分かるように、2つのループを持ちます。 未整列のデータを1つ選ぶためのループと 選んだデータをどこに挿入するか値を比較するためのループです。

f:id:nao_000:20190602121834p:plain

Pythonでの実装例

Pythonでの実装してみました。

import random

N = 5

# get random array
arr = list(range(1, N+1))
random.shuffle(arr)
print("inital array: {0}".format(arr))

_insert_value = None # a temporary variable for inserting the value
i = 1
k = None

while (i < N):
  _insert_value = arr[i]
  k = i
  print("_insert_value = {0}".format(_insert_value))
  while ( k > 0 and arr[k-1] > _insert_value):
    arr[k] = arr[k-1]
    k = k - 1
  arr[k] = _insert_value
  i = i + 1
  print("i={0}, sorted:{1} , not sorted: : {2}".format(i, arr[:i], arr[i:]))

print("result: {0}".format(arr))
# 実行結果例
inital array: [3, 2, 5, 1, 4]
_insert_value = 2
i=2, sorted:[2, 3] , not sorted: : [5, 1, 4]
_insert_value = 5
i=3, sorted:[2, 3, 5] , not sorted: : [1, 4]
_insert_value = 1
i=4, sorted:[1, 2, 3, 5] , not sorted: : [4]
_insert_value = 4
i=5, sorted:[1, 2, 3, 4, 5] , not sorted: : []
result: [1, 2, 3, 4, 5]

estatからデータを取得してみる

estatのデータの取得と整理

matplotlibで利用するためのデータを取得するために、 外部のサイトからデータをAPIで取得して、そのデータをもとに図を書くということをしてみたいと思いました。
(今回の内容はデータを取得して、必要な形に整理するところまでです。matplotlibでのグラフ作成までは一度に整理できなかったのでまた今度です。)

既に、以下のサイトの記事で、estatのAPIを取得してから データを取り出すために試行錯誤するところについて 丁寧に記載されていますが、 今回は自分用のメモとして記事にしようと思います。

今回は利用するオープンデータとして、estatを使用しました。 ここから国勢調査などの国が提供しているデータを利用できます。

初めてやることなので、estatの一番上にあるデータである国勢調査 / 平成27年国勢調査 / 人口等基本集計(男女・年齢・配偶関係,世帯の構成,住居の状態など)を取得してみます。
ちなみに、APIで利用するURLは、estatのマイページから確認できます。

APIで取得できるデータ構造

データはjson形式で階層構造を以下のようになっています。 この中で、データとして活用する機会があるのは CLASS_OBJとVALUEです。

VALUEには目的のデータが含まれており、 CLASS_OBJにはデータとして使用する値の定義情報が 含まれています。

GET_STATS_DATA
  |- PARAMETER
  |
  |- RESULT
  |
  |- STATISTICAL_DATA
       |
       |- CLASS_INF
       |    |
       |    |- CLASS_OBJ ☆ ここにあるデータを使う
       |
       |- DATA_INF
            |
            |- VALUE ☆ ここにあるデータを使う

APIからの取得は以下のように、上記サイトで指定されるURLを利用して、 urllib.request.urlopen(url)を使って取得します。
今回の場合は、urlにはestatが公開している「APIリクエストURL」を指定します。

また、取得したデータをファイルに保存したときに、 フォーマットが整った形でファイルに保存したい場合には、 json.dump()を使用します。

# coding: utf-8
import pandas as pd
import urllib.request
from io import StringIO
from IPython.core.display import display
import json

appid = "自分のIDを指定"
json_url = "https://api.e-stat.go.jp/rest/2.1/app/json/getStatsData?"
opt_url = "&lang=J&statsDataId=0003148500&metaGetFlg=Y&cntGetFlg=N&sectionHeaderFlg=1"
url = json_url + "appId=" + appid + opt_url
print(url)

def get_json_data(url, file_name):
  res_str = urllib.request.urlopen(url).read().decode('utf-8')
  res = json.loads(res_str)
  with open(file_name, mode="w", encoding='utf-8') as f:
    json.dump(res, f, ensure_ascii=False, indent=4, sort_keys=True, separators=(',', ': '))
    print("save data to {0}".format(file_name))
  return pd.read_json(file_name, encoding='utf-8')
json_data = get_json_data(url, "tmp.json")

取得されるjsonのデータは以下のものです。
※ 一部省略しているデータ部分は「// 省略」と記載しています。
このjson形式のデータを利用するには、ここから「GET_STATS_DATA → STATISTICAL_DATA → DATA_INF → VALUE」のようにkeyを指定していって、値を取り出す必要があります。

{
    "GET_STATS_DATA": {
        "PARAMETER": {
          // 今回は重要でないため省略
        },
        "RESULT": {
            "DATE": "2019-05-11T13:23:06.240+09:00",
            "ERROR_MSG": "正常に終了しました。",
            "STATUS": 0
        },
        "STATISTICAL_DATA": {
            "CLASS_INF": {
                "CLASS_OBJ": "[{'@id': 'tab', '@name': '表章項目..."  // ...: データが多いため一部省略
            },
            "DATA_INF": {
                "NOTE": [
                  // 今回は重要でないため省略
                ],
                "VALUE": "[{'@tab': '020', '@cat01': '00..." // ...: データが多いため一部省略
            },
            "RESULT_INF": {
                "FROM_NUMBER": 1,
                "TOTAL_NUMBER": 63530,
                "TO_NUMBER": 63530
            },
            "TABLE_INF": {
              // 今回は重要でないため省略
            }
        }
    }
}

STATISTICAL_DATA → DATA_INF → VALUE について

VALUEには、目的のデータが入れられています。 まずは、今回のAPIで得られたこれらのデータを確認していきます。

DATA_INFのVALUEには、データがlist ([...])として格納されています。 また、要素にはdict型{...}の形式で1つ1つデータが格納されています。

以下のものは2つの要素だけを取り出した結果です。 1つめの{...}の$には127,094,745の値が入っています。
(後でみるように日本の総人口の値を示していることが分かりますが)

[{'$': '127094745',
  '@area': '00000',
  '@cat01': '00710',
  '@tab': '020',
  '@time': '2015000000',
  '@unit': '人'},
 {'$': '116137232',
  '@area': '00001',
  '@cat01': '00710',
  '@tab': '020',
  '@time': '2015000000',
  '@unit': '人'}]

この値が具体的に何を示しているかについては、 @area、@cat01、@tab、@timeを見て、判断する必要があります。

これら(@area、@cat01、@tab、@time)の定義については、次のCLASS_OBJに含まれています。

ちなみに、json形式の上記のデータをDataFrameにいれて、 head()で先頭5つを取り出すと以下のようになります。

$ @area @cat01 @tab @time @unit
0 127094745 00000 00710 020 2015000000
1 116137232 00001 00710 020 2015000000
2 10957513 00002 00710 020 2015000000
3 5381733 01000 00710 020 2015000000
4 4395172 01001 00710 020 2015000000

STATISTICAL_DATA → DATA_INF → VALUEのデータをDataFrameに入れるには、 json_dataからkeyを順に指定していき、取り出した値をDataFrameに渡します。

data_VALUE = json_data['GET_STATS_DATA']['STATISTICAL_DATA']['DATA_INF']['VALUE']
df_VALUE = pd.DataFrame(data_VALUE)
display(df_VALUE.head())

STATISTICAL_DATA → CLASS_INF → CLASS_OBJ について

今度はCLASS_OBJについて詳しく見ていきます。
CLASS_OBJには、データに使用する値の情報が入っています。
具体的には国勢調査 / 平成27年国勢調査 / 人口等基本集計(男女・年齢・配偶関係,世帯の構成,住居の状態など)に記載されている「表章項目、全域・人口集中地区(2015)、地域(2015)、時間軸(年次)」のデータが入っています。

@id @name CLASS
0 tab 表章項目 [{'@code': '020', '@level': '', '@name': '人口',...
1 cat01 全域・人口集中地区(2015) [{'@code': '00710', '@level': '1', '@name': '全...
2 area 地域(2015) [{'@code': '00000', '@level': '1', '@name': '全...
3 time 時間軸(年次) {'@code': '2015000000', '@level': '1', '@name'...

さらに、この中の「地域(2015)」のデータをのぞいてみると、 以下のようにデータを持っているようです。 codeはprimary keyのようで、levelで都道府県、市町村などの単位を分けているようです。
何と都道府県、市町村などが一緒に含まれるテーブル構造になっています...

@code @level @name @parentCode
0 00000 1 全国 NaN
1 00001 1 全国市部 NaN
2 00002 1 全国郡部 NaN
3 01000 2 北海道 00000
4 01001 3 北海道市部 01000
5 01002 3 北海道郡部 01000
6 01100 4 札幌市 01000
7 01101 5 札幌市 中央区 01100
8 01102 5 札幌市 北区 01100
9 01103 5 札幌市 東区 01100

上記のデータを確認するには、以下のようにします。 STATISTICAL_DATA → CLASS_INF → CLASS_OBJのデータをDataFrameに入れるには、 json_dataからkeyを順に指定していき、取り出した値をDataFrameに渡します。
CLASS_OBJ内にある「@id==areaのCLASS」を取得するには、行と列の値を指定します。
今回は、列の取得をdf['列名']でしてから、行の取得をloc('行数')でしています。

data_CLASS_OBJ = json_data['GET_STATS_DATA']['STATISTICAL_DATA']['CLASS_INF']['CLASS_OBJ']
df_CLASS_OBJ = pd.DataFrame(data_CLASS_OBJ)
display(df_CLASS_OBJ.head())

# @id==areaのCLASSの情報を確認
df_CLASS_OBJ_CLASS_area = pd.DataFrame(df_CLASS_OBJ['CLASS'].loc[2])
display(df_CLASS_OBJ_CLASS_area.head(n=10))

[補足] CLASS_OBJについて

ここでは、もう少しCLASS_OBJについてみておきます。
上でみたように、この中には@idの列でいうとtab, cat01, area, timeの4つが入っています。例えば、tabとareaは以下のようになっています。
tabではそれぞれの統計情報が一つにまとまっており、 areaではlevelに基づいて、都道府県のスケールや市町村のスケールで の結果が混ざり合っています。
なので、実際にデータを取り扱う際には、不要なデータには フィルターをかけて除外する必要があります。

[tab]について

@code @level @name @unit
0 020 人口
1 106 組替人口(平成22年)
2 107 平成22年~27年の人口増減数
3 108 平成22年~27年の人口増減率
4 103 面積 平方km
5 104 人口密度 NaN
6 109 世帯数 世帯
7 110 組替世帯数(平成22年) 世帯
8 111 平成22年~27年の世帯数増減数 世帯
9 112 平成22年~27年の世帯数増減率

[area]について

@code @level @name @parentCode
0 00000 1 全国 NaN
1 00001 1 全国市部 NaN
2 00002 1 全国郡部 NaN
3 01000 2 北海道 00000
4 01001 3 北海道市部 01000
5 01002 3 北海道郡部 01000
6 01100 4 札幌市 01000
7 01101 5 札幌市 中央区 01100

データのマージ

さて、DATA_INF内のVALUEに話は戻りますが、このままだと @area、@cat01、@tab、@timeの値がそれぞれID(数字)のままなので 具体的に何の値を示しているかわかりません。

そこで、ここではCLASS_OBJのデータとマージして具体的な値に置き換えていきます。 やることは、DataFrameのmerge()を用いて、それぞれをマージしていくことだけです。 (データの整理についてはあまり良い方法が見つからなかったので、drop()とrename()で強引に整えています。。。)。
これを実行すると、以下に意味のあるデータに変換できたものが取得できます。

なお、取得したデータは、df.to_csv()を使うことで、 csv形式のファイルとして保存できます

$ @area_level @area @cat01 @tab @time
0 127094745 1 全国 全域 人口 2015年
1 116137232 1 全国市部 全域 人口 2015年
2 10957513 1 全国郡部 全域 人口 2015年
3 5381733 2 北海道 全域 人口 2015年
4 4395172 3 北海道市部 全域 人口 2015年

以下の手順で上記のようにデータを整理できます。

# merge data
# もっと効率のよくて賢い方法があると思います。。。
def merge_data(df_VALUE, df_CLASS_OBJ):
  df_CLASS_OBJ_CLASS_tab = pd.DataFrame(df_CLASS_OBJ['CLASS'].loc[0])
  df_CLASS_OBJ_CLASS_cat01 = pd.DataFrame(df_CLASS_OBJ['CLASS'].loc[1])
  df_CLASS_OBJ_CLASS_area = pd.DataFrame(df_CLASS_OBJ['CLASS'].loc[2])
  df_CLASS_OBJ_CLASS_time = pd.DataFrame([df_CLASS_OBJ['CLASS'].loc[3]])
  # time: DataFrame にはdict型のデータを持つリストを渡す必要がある
  print("the original data is below")
  display(df_VALUE.head())

  df = df_VALUE.drop(columns=['@unit'])
  # merge '@area'
  df = pd.merge(df, df_CLASS_OBJ_CLASS_area, how='left', 
              left_on='@area', right_on='@code')
  df = df.drop(columns=['@code', '@parentCode']) # @level is used for classification
  df = df.drop(columns=['@area'])
  df = df.rename(columns={'@name':'@area', '@level':'@area_level'})
  # merge '@cat01'
  df = pd.merge(df, df_CLASS_OBJ_CLASS_cat01, how='left', 
              left_on='@cat01', right_on='@code')
  df = df.drop(columns=['@code', '@level'])
  df = df.drop(columns=['@cat01'])
  df = df.rename(columns={'@name':'@cat01'})
  # merge '@tab'
  df = pd.merge(df, df_CLASS_OBJ_CLASS_tab, how='left', 
              left_on='@tab', right_on='@code')
  df = df.drop(columns=['@code', '@level', '@unit'])
  df = df.drop(columns=['@tab'])
  df = df.rename(columns={'@name':'@tab'})
  # merge '@time'
  df = pd.merge(df, df_CLASS_OBJ_CLASS_time, how='left', 
              left_on='@time', right_on='@code')
  df = df.drop(columns=['@code', '@level'])
  df = df.drop(columns=['@time'])
  df = df.rename(columns={'@name':'@time'})

  print("the merged data is below")
  display(df.head())
  return df
df = merge_data(df_VALUE, df_CLASS_OBJ)

# save data
df.to_csv("organized_data.csv")

整理したデータについて

整理して得られたデータについて、1つ確認してみます。 上記でマージしたデータを使って、都道府県ごとの人口データを取得します。 @area_levelを2(都道府県)、@cat01を全域、@tabを人口となっている行だけを取得します。

必要なデータを抽出すると、以下の表の形の結果が得られます。

$ @area_level @area @cat01 @tab @time
0 5381733 2 北海道 全域 人口 2015年
1 1308265 2 青森県 全域 人口 2015年
2 1279594 2 岩手県 全域 人口 2015年
3 2333899 2 宮城県 全域 人口 2015年
4 1023119 2 秋田県 全域 人口 2015年
5 1123891 2 山形県 全域 人口 2015年
6 1914039 2 福島県 全域 人口 2015年
7 2916976 2 茨城県 全域 人口 2015年

統計情報を確認してみると、次のことが読み取れるので、 きちんと目的のデータになっていることがわかります。

  • count数より、47行分のデータが存在すること

  • @areaのuniqueの値より、47行すべてが異なる@areaであること

  • @tabのuniqueの値より、すべての行が人口に関するデータであること

$ @area_level @area @cat01 @tab @time
count 47 47 47 47 47 47
unique 47 1 47 1 1 1
top 2304264 2 鹿児島県 全域 人口 2015年
freq 1 47 1 47 47 47
# (1) 都道府県ごとの人口データを取得
_tmp_df = df[ (df['@area_level'] == '2') \
            & (df['@tab'] == '人口')     \
            & (df['@cat01'] == '全域')].reset_index(drop=True)
display(_tmp_df)
display(_tmp_df.describe())

次は、取得したデータをもとに、matplolibを用いてグラフを書いていこうと思います。