[[FrontPage]]

#contents

2016/02/16からのアクセス回数 &counter;

ここで紹介したSageワークシートは、以下のURLからダウンロードできます。

http://www15191ue.sakura.ne.jp:8000/home/pub/61/

また、私の公開しているSageのサーバ(http://www15191ue.sakura.ne.jp:8000/)にユーザIDを作成することで、ダウンロードしたワークシートを
アップロードし、実行したり、変更していろいろ動きを試すことができます。

** オープンデータ解析例1 [#k120014b]

高岡市の公開しているオープンデータを使ってデータ解析をしてみます。

今回は、平成27年7月地区別世帯数及び人口集計表の「全体」シートを使って、高岡市を2つのグループに分類してみます。         


*** 必要なユーティリティの準備 [#rd844635]

データを簡単に処理するために、ggplot2等のライブラリーを読み込みます。


sageへの入力:
#pre{{
# RとPandasで必要なユーティリティ
# Rの必要なライブラリ
r('library(ggplot2)')
r('library(jsonlite)')
# RUtilにRとPandasのデータフレームを相互に変換する関数を追加+GgSaveを追加(2015/07/12)
load(DATA + 'RUtil.py')
}}


** 平成27年7月地区別世帯数及び人口集計表 [#y88d0032]

それでは、オープンデータとして公開されている高岡市の「平成27年7月地区別世帯数及び人口集計表」のデータを
読み込んでPandasのデータフレームに取り込みます。

平成27年7月地区別世帯数及び人口集計表のデータは、以下の様に入力されています。

&ref(Excel_format.jpg);



ヘッダは、4,5行目にあり、カラムは、2列目から始まっています。

pd.read_excel関数に読み込むExcelファイルのあるURLを指定します。
ヘッダは、3(0始まり)とし、カラムは1(0始まり)にあることを指定するため、header=3, index_col=1を引数にセットします。


sageへの入力:
#pre{{
# 高岡市のオープンデータから平成27年7月の人口データ(h270731.xlsx)を取り込みます
d = pd.read_excel('http://www.city.takaoka.toyama.jp/joho/shise/opendata/documents/h270731.xlsx', header=3, index_col=1)
d.head()
}}
#pre{{
      頁  地区\nコード  地区名  人  口 Unnamed: 4 Unnamed: 5   世帯数
NaN NaN      NaN  NaN     男          女          計   NaN
 2  NaN  01 平米地区  NaN  1573       1807       3380  1591
 3  NaN  02 定塚地区  NaN  4428       4967       9395  3977
 4  NaN  03 下関地区  NaN  4439       4629       9068  3971
 5  NaN  04 博労地区  NaN  5317       5833      11150  4582
}}


*** カラム名のセットと不要なカラムの削除 [#pde60d63]

カラム名が日本語のままだと操作しにくいので、カラム名をd.columnsに英語名でセットします。
頁の後にNaNという変な値が入っているので、not_usedというカラムとしました。
地区名はセル結合されて、codeの欄に地区コード+地区名でセットされていました。


sageへの入力:
#pre{{
# カラム名を付け直し、最初の5個を表示
d.columns = ['not_used', 'code', 'region', 'male', 'female', 'population', 'household']
d.head()
}}
#pre{{
     not_used     code  region  male female population  household
NaN       NaN      NaN     NaN     男      女          計        NaN
 2        NaN  01 平米地区     NaN  1573   1807       3380       1591
 3        NaN  02 定塚地区     NaN  4428   4967       9395       3977
 4        NaN  03 下関地区     NaN  4439   4629       9068       3971
 5        NaN  04 博労地区     NaN  5317   5833      11150       4582
}}


博労地区と佐野地区は木津地区を除いた集計がされているので、indexがNaNとなっており、
d.index > 1とすることで不要なデータを取り除きます。


sageへの入力:
#pre{{
# 最初の番号(index)が1より大きなcode, population, householdを抽出します。
d1 = d[d.index > 1][['code', 'male', 'female', 'population', 'household']]
d1.head()
}}
#pre{{
      code  male female population  household
2  01 平米地区  1573   1807       3380       1591
3  02 定塚地区  4428   4967       9395       3977
4  03 下関地区  4439   4629       9068       3971
5  04 博労地区  5317   5833      11150       4582
6  05 横田地区  2839   3070       5909       2286
}}


*** データの可視化 [#w55636ee]

各要素にどのような関係があるかを、Rのpairsプロットでみてみましょう。

SageのPandaで作られたデータフレームをRのデータフレームに変換します。
残念ながら、日本語のデータが正しくRに変換できないため、ここでは地域
codeを除いてRに渡しています。


sageへの入力:
#pre{{
# d2をRのデータフレームd1に変換する
# 日本語が上手く渡せない
d2 = d[d.index > 1][['male', 'female', 'population', 'household']]
d2 = d2[d2.index <36]
PandaDf2RDf(d2, "d2")
}}


sageへの入力:
#pre{{
# 分布を可視化
g_pairs = preGraph("pairs.pdf")
r('pairs(d2, main="Basic Scatter Plot Matrix")')
postGraph(g_pairs, fac=0.6)
}}

&ref(th_pairs.png);


世帯数(household)と人口(population)の関係を散布図でみてみましょう。

世帯数と人口は、概ね直線上にのっており、地区によるおおきな変化はみられません。


sageへの入力:
#pre{{
# 世帯数(household)と人口(population)の関係をみる
df = d1[d1.index <36]
ggplot(df, aes(x='household', y='population')) + geom_point()
}}
#pre{{
<repr(<ggplot.ggplot.ggplot at 0x7f7943c6a6d0>) failed: KeyError: 0.0>
}}

sageへの入力:
#pre{{
GgSave('fig_1.png', fac=0.8)
}}
#pre{{
Saving 11.0 x 8.0 in image.
}}

&ref(th_fig_1.png);


** グループ分け [#sefb380b]

高岡市の地区を2つのグループに分けてみましょう。
グループ分けに使用するデータは、男性、女性の人数と世帯数のデータとしました。

分析に使用する手法は、混合正規分布(GMM)を使用します。
ここでは、sklearnのmixtureパッケージのGMMを使っています。
n_components=2で2つのグループに分けることを指定しています。


sageへの入力:
#pre{{
# 混合正規分布
from sklearn import mixture
# 分類器の生成
classifier = mixture.GMM(n_components=2, covariance_type='full')
}}


sageへの入力:
#pre{{
# GMMを求める
# GMMについては、http://www15191ue.sakura.ne.jp:8000/home/pub/57/ を参照してください。
data = df[['male', 'female', 'household']].values
# 混合正規分布を求める
classifier.fit(data)
}}
#pre{{
GMM(covariance_type='full', init_params='wmc', min_covar=0.001,
  n_components=2, n_init=1, n_iter=100, params='wmc', random_state=None,
  thresh=None, tol=0.001)
}}


*** 分析結果のプロット [#oe1932f1]

グループ分けされた結果は、classifier.predict関数で得ることができます。
この結果をデータフレームdfのpredにセットします。

predの値で散布図の点を色分けしてプロットしてみます。


sageへの入力:
#pre{{
# 分類結果をpredにセット
df['pred'] = classifier.predict(data)
# 世帯数(household)と人口(population)の関係をみる
ggplot(df, aes(x='population', y='household', colour='pred')) + geom_point()
}}
#pre{{
<repr(<ggplot.ggplot.ggplot at 0x7f79437b9810>) failed: KeyError: 0.0>
}}


sageへの入力:
#pre{{
GgSave('fig_2.png', fac=0.8)
}}
#pre{{
Saving 11.0 x 8.0 in image.
}}

&ref(th_fig_2.png);




*** 世帯数と分類の結果 [#lcb3ca4f]

データを世帯数でソートして表示してみます。
predがグループ分けの結果で、平米地区が世帯数が中田地区よりも少ないのに
グループ1に入っており、興味深い結果となっています。


sageへの入力:
#pre{{
# 世帯数でソート
# 平米地区に注目
df.sort('household')
}}
#pre{{
           code  male female population  household  pred
33  30 福岡町五位山地区   204    217        421        170     0
18      16 小勢地区   488    498        986        277     0
34   31 福岡町赤丸地区   577    612       1189        414     0
21      19 石堤地区   613    619       1232        443     0
32  29 福岡町西五位地区   938    975       1913        613     0
31   28 福岡町大滝地区  1197   1240       2437        796     0
25      23 太田地区  1221   1387       2608        889     0
17      15 福田地区  1309   1347       2656        900     0
23      21 守山地区  1259   1380       2639        933     0
10      09 二上地区  1270   1366       2636        999     0
29   26 福岡町福岡地区  1477   1616       3093       1153     0
19      17 立野地区  1578   1698       3276       1181     0
22      20 国吉地区  1719   1844       3563       1192     0
15      13 二塚地区  1770   1894       3664       1218     0
30   27 福岡町山王地区  1958   1980       3938       1244     0
8       07 川原地区  1554   1719       3273       1336     0
2       01 平米地区  1573   1807       3380       1591     1
20     18 東五位地区  2354   2546       4900       1700     0
35       木津地区 ※  2765   2966       5731       2135     0
28      25 中田地区  3025   3270       6295       2220     0
6       05 横田地区  2839   3070       5909       2286     0
16      14 佐野地区  3238   3530       6768       2399     0
7       06 西条地区  3427   3565       6992       2676     1
12      11 牧野地区  4678   4909       9587       3461     1
9       08 成美地区  3877   4414       8291       3512     1
4       03 下関地区  4439   4629       9068       3971     1
3       02 定塚地区  4428   4967       9395       3977     1
24      22 伏木地区  5368   5939      11307       4442     1
11      10 能町地区  5667   5874      11541       4449     1
5       04 博労地区  5317   5833      11150       4582     1
26      24 戸出地区  6716   6921      13637       4948     1
13      12 野村地区  8443   8996      17439       6907     1
}}


*** 男性と女性の数の関係 [#te754baa]

同様に、男性と女性の関係をプロットしてみます。
世帯数よりも男性と女性の関係の方が、はっきりした直線の関係がみられます。


sageへの入力:
#pre{{
# 男性(male)と女性(female)の関係をみる
ggplot(df, aes(x='male', y='female', colour='pred')) + geom_point()
}}
#pre{{
<repr(<ggplot.ggplot.ggplot at 0x7f793f22b710>) failed: KeyError: 0.0>
}}

sageへの入力:
#pre{{
GgSave('fig_3.png', fac=0.8)
}}
#pre{{
Saving 11.0 x 8.0 in image.
}}

&ref(th_fig_3.png);




*** ペア・プロットにグループで色分け [#uc1a1dc1]

平米地区がグループ1に入っている理由をみるために、
ペア・プロットにグループで色分けしてみます。


sageへの入力:
#pre{{
# グループ分けの結果で色分けしてペア・プロットを表示
d2 = df[['male', 'female', 'population', 'household', 'pred']]
PandaDf2RDf(d2, "d2")
}}


sageへの入力:
#pre{{
# 分布を可視化
graph = preGraph("pairs2.pdf")
r('pairs(d2[, c(2, 3, 4, 5)], main="Takaoka Group", pch=21, bg=c("red", "blue")[factor(d2$pred)])')
postGraph(graph, fac=0.8)
}}

&ref(th_pairs2.png);



** クラス分けを地図に表示 [#x95d57fc]

*** 国土地理院の小学校区のshapeファイル [#l323446a]

クラス分けの結果を地図にプロットするために、国土地理院が提供している富山県の小学校区のデータ(A27-10_16_GML)を使用しました。

- [[国土交通省 GIS データのダウンロード>http://nrb-www.mlit.go.jp/kokjo/inspect/landclassification/download/index.html]]

小学校区は、A27-10_16-g_SchoolDistrict.shpに定義され、小学校名はA27-10_16-g_SchoolDistrict.dbfに定義されています。
国土地理院のデータはShift-JISコードであり、これをUTF-8に変換する必要があります。

以下のサイトを参考にUTF-8に変換しました。

- [[Google Earth の使い方~その2 道路や鉄道、将来人口情報などの表示>http://diy-ilands.com/2015/02/23/google-earth-usage-read-gis/]]

*** 地区と小学校の関係 [#ae43b3b4]

高岡市の地区と小学校区の関係をExcelシートに整理しました。市町村合併や小学校の統合によって、
分からなかったので、高岡市の防災マップを元に人に聞きながら作ったので、間違ってかも知れませんが、
このExcelファイルを修正すれば、地図はすぐに書き直せます。


sageへの入力:
#pre{{
# 高岡市地区の小学校区を読み込む
schoolDistrict = pd.read_excel(DATA + "schoolDistrict.xls")
# 予測結果に対する色と小学校をd2にセット
schoolDistrict.head()
}}
#pre{{
      code schoolDsitrict
0  01 平米地区          平米小学校
1  02 定塚地区          定塚小学校
2  03 下関地区          下関小学校
3  04 博労地区          博労小学校
4  05 横田地区          横田小学校
}}


つぎにcodeをキーにして、高岡市のクラス分け結果(df)と小学校区(schoolDistrict)をマージします。

sageへの入力:
#pre{{
# 小学校区と地区をマージ
df.set_index('code')
schoolDistrict.set_index('code')
df = df.merge(schoolDistrict)
df.head()
}}
#pre{{
      code  male female population  household  pred schoolDsitrict
0  01 平米地区  1573   1807       3380       1591     1          平米小学校
1  02 定塚地区  4428   4967       9395       3977     1          定塚小学校
2  03 下関地区  4439   4629       9068       3971     1          下関小学校
3  04 博労地区  5317   5833      11150       4582     1          博労小学校
4  05 横田地区  2839   3070       5909       2286     0          横田小学校
}}


地図の表示には、Rのmaptoolsを使用するため、
小学校区(schoolDsitrict)とクラス分け結果(pred)をCSVファイルとして出力します。

sageへの入力:
#pre{{
# 小学校区のクラス分け
schoolClass = df[['schoolDsitrict', 'pred']]. drop_duplicates()
schoolClass.head()
}}
#pre{{
  schoolDsitrict  pred
0          平米小学校     1
1          定塚小学校     1
2          下関小学校     1
3          博労小学校     1
4          横田小学校     0
}}


sageへの入力:
#pre{{
# 日本語を含むため、csvファイルを使ってRへ渡す
schoolClass_file = DATA + 'schoolCalss.csv'
schoolClass.to_csv(schoolClass_file)
}}


*** maptoolsで地図にプロット [#q6aab298]

Rのmaptoolsをロードし、富山県の小学校区のshapeファイルA27-10_16-g_SchoolDistrict.shpをshapeに読み込みます。

shape変数から高岡市立の小学校区のみを抽出するため、shape[shape@data$A27_006=="高岡市立",]の条件抽出を使います。

sageへの入力:
#pre{{
# maptoolsをロード
# r("install.packages('maptools')")
r("library(maptools)")
}}
#pre{{
 [1] "maptools"  "sp"        "jsonlite"  "ggplot2"   "stats"     "graphics"  "grDevices" "utils"   
 [9] "datasets"  "methods"   "base"    
}}


sageへの入力:
#pre{{
# 富山県の小学校区のshapeファイルを読み込み、高岡市立の小学校のみを取り出す
shape_file = DATA + "A27-10_16-g_SchoolDistrict.shp"
r('shape <- readShapePoly("%s")' % shape_file)
r('takaoka <- shape[shape@data$A27_006=="高岡市立",]')
r('takaoka@data$A27_007')
}}
#pre{{
 [1] 成美小学校     戸出東部小学校 戸出東部小学校 博労小学校     戸出西部小学校 伏木小学校   
 [7] 万葉小学校     福岡小学校     太田小学校     国吉小学校     牧野小学校     千鳥丘小学校 
[13] 西広谷小学校   古府小学校     能町小学校     西条小学校     野村小学校     川原小学校   
[19] 石堤小学校     定塚小学校     横田小学校     平米小学校     東五位小学校   木津小学校   
[25] 南条小学校     二塚小学校     中田小学校     下関小学校     下関小学校     成美小学校   
122 Levels: あさひ野小学校 さみさと小学校 井口小学校 井波小学校 宇奈月小学校 ... 立山北部小学校
}}


高岡市の小学校区のクラス分け結果のCSVファイルを読み込み、takaoka@data$A27_007に含まれる小学校のクラス結果predをセットします。

色分けを0=赤、1=青とし、Rのインデックスが1はじまりなので、pred+1としてtakaoka@data$A27_007に含まれる小学校の色をcolにセットします。

sageへの入力:
#pre{{
# shcoolClassを読み込む
r('shcoolClass <- read.csv("%s")' % schoolClass_file)
# クラス分けを変数predにセット
r('pred <- shcoolClass$pred[match(takaoka@data$A27_007 , shcoolClass$schoolDsitrict)]')
# クラスの色を配列にセット
r('cols <- c("red", "blue")')
# クラス分けを色に変換(Rのインデックスが1始まりなのでpred+1とする)
r('col <- cols[pred+1]')
}}
#pre{{
 [1] "blue" "blue" "blue" "blue" "blue" "blue" "red"  "red"  "red"  "red"  "blue" "red"  NA   
[14] "blue" "blue" "blue" "blue" "red"  "red"  "blue" "red"  "blue" "red"  "red"  "red"  "red"
[27] "red"  "blue" "blue" "blue"
}}


準備ができたので、takaokaの小学校区のクラス分けを地図にプロットします。

赤で囲まれた青の地区は、「川原地区」です。

sageへの入力:
#pre{{
graph = preGraph("schoolDistrict.pdf")
r('plot(takaoka, col=col)')
postGraph(graph, fac=0.8)
}}

&ref(th_schoolDistrict.png);




** データ替えて同じ計算をする [#f3df4804]

最初のデータの読み込みを以下のように6月のデータに替えて、ActionメニューからEvaluate Allを選択すると、
平成27年6月地区別世帯数及び人口集計表に対して同じ計算を行い、その結果をグラフに表示します。

#pre{{
     d = pd.read_excel('http://www.city.takaoka.toyama.jp/joho/shise/opendata/documents/h270630.xlsx', header=3, index_col=1)
}}

これが、Sageを使った「リプロデューシブルリサーチ」です。
Sageは、実行可能なドキュメントなので、入力のExcelファイルを替えて「Evaluate All」を実行すれば、
別のデータに同じような分析を実行することができるのです。


** コメント [#ve498b7a]
#vote(おもしろかった,そうでもない,わかりずらい)


皆様のご意見、ご希望をお待ちしております。
#comment_kcaptcha


トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
SmartDoc