まちのクラスタリングをやりたい(1)
「【まち】を北から順に網羅的に整理したいな」
ということで、いろいろ試してみることにした。
・まず、都道府県レベルで。データ元はこちら。
これを見た感じでは、「東京の位置」を妥当に示しているのは、北端だけということになる。ということで、北端の緯度経度を採用。
経度順位 | 経度 | 緯度 | |
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)への拡張だ。
これは次の記事で扱う。