QGISのテーブルをファジー結合してみる
はじめに
OSGeo Japan Discussの質問で、「文字列が一致していない場合に、テーブル結合が出来るのか」といった質問があったので、探してみました。QGIS 3.8以降に実装された機能でできそうだというこことで、説明を作ってみました。
以下のページの記述を少し変えて、作りました
Fuzzy Table Joins in QGIS
使用データ
国土数値情報ダウンロードサービスから入手した、H31年時点の茨城県の行政界(GeoJSON形式)と、茨城県Webページからコピペした、茨城県市町村別人口データです。データは、ここから入手できます。
GeoJSONのデータは、以下の通り
N03_004 というカラムに市町村名が入っています。
人口データは、CSVにしました。テキストエディタでみると、以下の通り。わざと、市町村名の後に全角スペース、名前と市の間に全角や半角のスペース、市の削除、市町村名の間に半角スペース挿入等々、色々やっています(テストという意味も含めて)
このデータをQGISに読み込んで、GeoJSONのN03_004とCSVの市町村をkeyにして結合、人口を元に色分けすると以下の通りです。
全然結合しません。
さてここで、CSVファイルの方がおかしいので、こちらを修正し、ちゃんと結合できるかどうか、ためします。まず、csvファイルをQGIS上の属性テーブルとして開き、フィールド計算機をクリックします。
ここで、「新しいフィールドを作る」、「仮想フィールド作成」にチェックを入れ、式の部分に以下のテキストをコピペします。
array_first(aggregate(
layer:= 'ibaraki_admin',
aggregate:='array_agg',
expression:=N03_004,
filter:=levenshtein(N03_004, attribute(@parent, '市町村')) <= 4,
order_by:=levenshtein(N03_004, attribute(@parent, '市町村'))))
細かく説明すると、
layer:= 'ibaraki_admin',
の部分は、csvと結合させたいターゲットレイヤ名
expression:=N03_004,
の部分は、ターゲットレイヤの中の、結合ターゲットとなるフィールド名
filter:=levenshtein(N03_004, attribute(@parent, '市町村')) <= 4, order_by:=levenshtein(N03_004, attribute(@parent, '市町村'))
の部分で、ターゲットのN03_004に含まれる市町村名と、csvファイルの市町村名を比較し、半角で4文字(全角だと2文字)文字があってれば、その合っている文字を結果として返す、という処理を行っているとおもいます、多分 1。
これでOKをクリックし、処理が終了すると、結合対象のadminの市町村名と同じ記述が、fuzzyのカラムに入力されます。
ここで、結合がすんだcsvをエクスポートして、別のcsvファイルとして保存して下さい。
出来上がったcsvファイルを確認すると、以下の様のfuzzyという列が新たに作成され、正しく文字が入っていることが確認できます。
なお、保存したファイルをそのまま読み込むだけでは、数字の部分が文字列として認識されてしまいます。そのため、ibaraki_pop_fuzzy.csvt というファイルを作り、人口を数字として認識させます。その上で、fuzzyとN03_004をkeyにして結合し、人口別に表示すると、以下の通りになります。
処理した結果のファイルは、こちらからダウンロードできます。
おわりに
ということで、こんな感じで文字列が完全に一致してない場合でも、QGIS上でテーブル結合ができました。ちょっとスクリプトをいじる必要があるのと、正規化をしたカラムをcsvファイルの方に入れる手順を取ったため、少し手間がかかりますが、別々の機関から入手したファイルや、エクセルでの表示のために空白等が含まれる場合など、データの間での表記揺れがあるファイルを結合させるには、便利に使える場合もあると思います。
また、正規化したカラムは、csvの方でなくgisのデータの方に作成することも可能なので、その場合はちょっとだけ、手間を省くことができます。
一方で、こうした表記揺れの修正等には、OpenRefine等のツールを使って、データを修正する方がいい場合もあると思います。以下の資料等も、参考にして下さい
-
正確な説明は、誰かお願い(汗 ↩