Dossiri - Kamaeru

建築・都市・生活の領域に関する知識の体系化、技術に対する考察、書籍レビュー等

まちのクラスタリングをやりたい(1)

「【まち】を北から順に網羅的に整理したいな」

 

ということで、いろいろ試してみることにした。

 

・まず、都道府県レベルで。データ元はこちら。

uub.jp

 

これを見た感じでは、「東京の位置」を妥当に示しているのは、北端だけということになる。ということで、北端の緯度経度を採用。

経度順位   経度 緯度
1 北海道 148.75222 45.55722
2 岩手県 141.6825 40.45055
3 宮城県 141.50861 39.00277
4 青森県 140.91166 41.55611
5 秋田県 140.87694 40.51111
6 茨城県 140.58611 36.94527
7 福島県 140.28 37.97666
8 栃木県 139.95277 37.155
9 千葉県 139.78666 36.10388
10 山形県 139.55055 39.20861
11 新潟県 139.54944 38.55333
12 神奈川県 139.13361 35.67277
13 埼玉県 139.12972 36.28333
14 群馬県 139.09694 37.05861
15 東京都 139.01833 35.89833
16 長野県 138.52361 37.03027
17 山梨県 138.37305 35.97166
18 静岡県 138.22833 35.64583
19 富山県 137.635 36.98027
20 岐阜県 137.27888 36.465
21 愛知県 136.99 35.425
22 石川県 136.92194 37.85527
23 三重県 136.52833 35.25777
24 福井県 136.24416 36.29555
25 滋賀県 136.17472 35.70361
26 奈良県 135.71138 34.78138
27 和歌山県 135.62 34.38444
28 大阪府 135.35166 35.05138
29 京都府 135.22361 35.77916
30 兵庫県 134.76472 35.67472
31 徳島県 134.60583 34.25194
32 鳥取県 134.37 35.61444
33 香川県 134.34666 34.56472
34 岡山県 133.99777 35.35277
35 高知県 133.64416 33.88333
36 愛媛県 133.27472 34.30166
37 広島県 132.88833 35.10555
38 島根県 131.865 37.24416
39 宮崎県 131.76666 32.83888
40 大分県 131.66777 33.74055
41 山口県 131.13083 34.79861
42 熊本県 131.05972 33.19527
43 鹿児島県 130.25027 32.31055
44 福岡県 130.11194 34.25
45 佐賀県 129.8625 33.61916
46 長崎県 129.45694 34.7275
47 沖縄県 128.22222 27.88555

経度順に、東→西に並べた*1

北海道地域→東北と関東→中央高地と東海と北陸→近畿圏→四国中国→九州と並んでいるような気もする。

せっかくだからまとまりも見ておこう

久しぶりにプログラミング言語を利用する

 

#!/usr/bin/python
# coding: UTF-8
import os
os.getcwd()

os.listdir('C:\\Users\\*****\\Desktop')

バックスラッシュを2つにしなかったら、エスケープと間違えられて怒られた。自分の開きたいファイルがあることを確認。

 

os.chdir('C:\\Users\\*****\\Desktop')

 

f = open('ward.txt', mode='r', encoding='utf-8')

text = f.readlines()
print(text)

 

ここでデコードエラー。仕方のないので調べて、

 

import pandas as pd
import codecs

with codecs.open("****.txt", "r", "Shift-JIS", "ignore") as file:
df = pd.read_table(file, header=None)
print(df)

 

反応した。おけ。

パンダのオブジェクトを、普通のリストに戻す。

 

dfstr=str(df)
d = dfstr.split('\n')

 

a=
for x in d:
    a.append(x.split())

 

a.pop(0)
print(a)

 

これで、出てきたプリントが、

[['0', '[北海道]', '148.75222', '45.55722'], ['1', '[岩手県]', '141.68250', '40.45055'], ['2', '[宮城県]', '141.50861', '39.00277'], ['3', '[青森県]', '140.91166', '41.55611'], ['4', '[秋田県]', '140.87694', '40.51111'], ['5', '[茨城県]', '140.58611', '36.94527'], ['6', '[福島県]', '140.28000', '37.97666'], ['7', '[栃木県]', '139.95277', '37.15500'], ['8', '[千葉県]', '139.78666', '36.10388'], ['9', '[山形県]', '139.55055', '39.20861'], ['10', '[新潟県]', '139.54944', '38.55333'], ['11', '[神奈川県]', '139.13361', '35.67277'], ['12', '[埼玉県]', '139.12972', '36.28333'], ['13', '[群馬県]', '139.09694', '37.05861'], ['14', '[東京都]', '139.01833', '35.89833'], ['15', '[長野県]', '138.52361', '37.03027'], ['16', '[山梨県]', '138.37305', '35.97166'], ['17', '[静岡県]', '138.22833', '35.64583'], ['18', '[富山県]', '137.63500', '36.98027'], ['19', '[岐阜県]', '137.27888', '36.46500'], ['20', '[愛知県]', '136.99000', '35.42500'], ['21', '[石川県]', '136.92194', '37.85527'], ['22', '[三重県]', '136.52833', '35.25777'], ['23', '[福井県]', '136.24416', '36.29555'], ['24', '[滋賀県]', '136.17472', '35.70361'], ['25', '[奈良県]', '135.71138', '34.78138'], ['26', '[和歌山県]', '135.62000', '34.38444'], ['27', '[大阪府]', '135.35166', '35.05138'], ['28', '[京都府]', '135.22361', '35.77916'], ['29', '[兵庫県]', '134.76472', '35.67472'], ['30', '[徳島県]', '134.60583', '34.25194'], ['31', '[鳥取県]', '134.37000', '35.61444'], ['32', '[香川県]', '134.34666', '34.56472'], ['33', '[岡山県]', '133.99777', '35.35277'], ['34', '[高知県]', '133.64416', '33.88333'], ['35', '[愛媛県]', '133.27472', '34.30166'], ['36', '[広島県]', '132.88833', '35.10555'], ['37', '[島根県]', '131.86500', '37.24416'], ['38', '[宮崎県]', '131.76666', '32.83888'], ['39', '[大分県]', '131.66777', '33.74055'], ['40', '[山口県]', '131.13083', '34.79861'], ['41', '[熊本県]', '131.05972', '33.19527'], ['42', '[鹿児島県]', '130.25027', '32.31055'], ['43', '[福岡県]', '130.11194', '34.25000'], ['44', '[佐賀県]', '129.86250', '33.61916'], ['45', '[長崎県]', '129.45694', '34.72750'], ['46', '[沖縄県]', '128.22222', '27.88555']]

 

いい感じ。

 

ward法の実装は少し困ったけど、以下のようにやった

 

def DSfor(C):
    x = 0.0
    y = 0.0
    c = 0.0
    for i in C:
        x = x + i[2]
        y = y + i[3]
        c = c + 1.0
    xmean = x/c
    ymean = y/c
    Ax = 0.0
    Ay = 0.0
    for i in C:
        Ax = Ax + (i[2] - xmean)**2.0
        Ay = Ay + (i[3] - ymean)**2.0
    return (Ax,Ay)

 

def J(g,h,U):
    C = U[g]+U[h]
    RevS =
    DSRevX = 0.0
    DSRevY = 0.0
    d = 0
    for i in U:
        if(not (d == g or d == h)):
            RevS.append(d)
            DSRevX = DSRevX + DSfor(i)[0]
            DSRevY = DSRevY + DSfor(i)[1]
            d = d + 1
    DSsumX = DSRevX + DSfor(C)[0]
    DSsumY = DSRevY + DSfor(C)[1]
    return (DSsumX,DSsumY)

 

inta =
for i in a:
    inta.append([i[0],i[1],float(i[2]),float(i[3])])
print(inta)

・型がstrのままだったので、少数に直す。

INTA =
for i in inta:
    INTA.append([i])
print(INTA)

・Jの定義に合わせて、一段階リストを深くする。

 

def updateU(U):
    minDS = J(0,1,U)
    ug1 = 0
    ug2 = 1
    for i in range(len(U)):
        for j in range(len(U)):
            if(not i == j):
                if(J(i,j,U) < minDS):
                minDS = J(i,j,U)
                ug1 = i
                ug2 = j
                print*2
    print(minDS)
    V = []
    for i2 in range(len(U)):
        if(not (i2 == ug1 or i2 == ug2)):
            V.append(U[i2])
            V.append(U[ug1]+U[ug2])
    return V

 

ここまでで実装。あとはデンドログラムを描くために何度もクラスターをまとめ上げていく。

import pprint
b = INTA
for i in range(46):
    b = updateU(b)
    pprint.pprint(b)

 

クラスター分析の結果。。

 

【北海道】北海道
【東北東日本】岩手・宮城・青森・秋田・茨城・福島
【関東】山形・新潟・栃木・千葉・東京・群馬・神奈川・埼玉
【東海】長野・山梨・静岡
【中部】愛知・石川・富山・岐阜
【近畿】三重・福井・滋賀・奈良・和歌山・大阪・京都
【中国四国】鳥取・香川・兵庫・徳島・岡山・高知・愛媛・広島
【九州中国】大分・島根・宮崎・山口・熊本
【九州】長崎・佐賀・鹿児島・福岡
【沖縄】沖縄

 

なるほど、九州と中国のあたりが地域的に混濁してる。

鹿児島の北端の獅子島が、長崎の方に近いので、西九州として佐賀・長崎に括られるのか。

茨城→東北クラスター、山形→関東クラスターなども、地図上では新鮮。

 

 

ここで、北端の位置座標ではなく、他のデータで同じようにクラスタリングをしてみる。

 

・都道府県庁の、所在地の経度緯度を利用したクラスタリング

都道府県庁の緯度経度は、以下のサイトより引用。

【みんなの知識 ちょっと便利帳】都道府県庁所在地 緯度経度データ - 各都市からの方位地図 - 10進数/60進数での座標・世界測地系(WGS84)

 

とりあえずやってみる。

【北関東北陸】茨木・群馬・栃木・石川・富山・長野
【首都山陰筋】埼玉・福井・千葉・東京・山梨・岐阜・鳥取・神奈川・島根
【南東北】宮城・山形・福島・新潟
【北海道】北海道
【北東北】青森・岩手・秋田
【沖縄】沖縄
【南九州】宮崎・鹿児島
【北九州】長崎・熊本・佐賀・大分・愛媛・高知・福岡
【瀬戸内】徳島・和歌山・山口・広島・香川
【近畿筋】三重・兵庫・大阪・奈良・岡山・愛知・静岡・滋賀・京都

 

すると、東北がかなり独立する。また、緯度が同じに推移している首都圏と山陰が同じグループになっている。明らかにおかしなクラスターができてしまった。

 

…なんかプログラムかアルゴリズムかどっちかミスってそう。

確認してみると、偏差平方和の定義がおかしくなっていることに気づいた。

そこでDSfor の出力をAx+Ayにする。緯度経度は単位長がほぼ同じなので、標準化する必要もないと判断。合わせて他の部分も調整した。

 

結果、修正版。

【北海道】北海道
【北部東北】青森・岩手・秋田
【南部東北】宮城・山形・福島
【関東】東京・埼玉・神奈川・千葉・茨城・栃木
【東海】山梨・静岡
【信越】新潟・群馬・長野
【中部】愛知・岐阜・三重・石川・富山・福井
【近畿】滋賀・京都・大阪・奈良・兵庫・和歌山
【瀬戸内】岡山・香川・徳島・鳥取・島根
【中国四国】広島・愛媛・高知・山口・大分
【九州北】福岡・佐賀・熊本・長崎
【九州南】宮崎・鹿児島
【沖縄】沖縄

かなり常識的なクラスタリングに近づいた。今までのは、直感的に違和感のあるクラスタリングだったが、今回はかなり納得度が高い。

 

ここまできて、このやり方で一応妥当な括り方はできると感じた。

もちろん課題は残っている。

・xyパラメータで考えているため、山間部・海上・平野部の地形的重みづけができていない。国土に割と道路が張り巡らされているために何とかなっているが、単純距離は改良の余地あり。

・理想でいうならば、国土交通網からなるグラフを描き、グラフ理論のやり方でクラスター分析したい、、、が、そのような形式のデータは準備できていない

・鉄道情報でも同様のことがいえる。

 

まあこれらの課題は置いておく。

次にやりたいのは、都道府県庁所在地となっているまち(N=47)から、人口5万人以上の都市(N=543)への拡張だ。

 

これは次の記事で扱う。

 

 

 

*1:ここに脚注を書きます

*2:i,j