2019年6月5日水曜日

横浜市保育所&幼稚園MAPのSPARQLクエリについて

このたび公開させていただいた 横浜市保育所&幼稚園MAP は、横浜市オープンデータポータルのWEB API(SPARQLエンドポイント)から必要なデータを取得している。

・横浜市WEB APIの説明は こちら
・横浜市SPARQLエンドポイントは こちら

このAPIデータは、共通語彙基盤の語彙とデータ構造が用いられている。保育所等のオープンデータを共通語彙基盤対応のLODとしたのはおそらく全国初であり、とても先駆的な取り組みといえる。

やや残念な点は、施設の座標情報(緯度経度)データがないところ。自分の通勤経路にマッチした保育施設を探すには、やはり座標情報が欲しい!

さて本稿では、横浜市APIで使えるSPARQLクエリと、それに対する考察を紹介する。

1 全ての施設のID、名称、区、住所を取得するクエリ

select *
where {
 ?s ic:ID [ic:識別値 ?id ]  ;
    ic:住所 [ ic:区 ?ku ; ic:表記 ?jusho ].
 OPTIONAL{?s rdfs:label ?name .}
}ORDER BY xsd:decimal(?id)

【備考1】
施設の「名称」データが不存在のインスタンスがいくつかある。
(データ作成時の何らかのミスが原因?)
不存在に対応するため、名称を取得する部分はOPTIONAL句を付けている。
OPTIONAL{?s rdfs:label ?name .}

【備考2】
結果を「施設ID」順に並べるのにORDER BYを利用するが、このIDは「桁が揃っていない文字列」なので、数値型に変換してから並べ直す必要がある。
ORDER BY xsd:decimal(?id)
ID番号は、桁数を揃えることが重要だよね。


2 保土ケ谷区の施設を抽出するクエリ

select *
where {
 ?s ic:ID [ic:識別値 ?id ]  ;
    ic:住所 [ ic:区 "保土ケ谷区" ; ic:表記 ?jusho ].
  OPTIONAL{?s rdfs:label ?name .}
}


3 「認可保育所」を抽出するクエリ

select *
where {
 ?s ic:ID [ic:識別値 ?id ]  ;
    ic:住所 [ ic:区 ?ku ;  ic:表記 ?jusho ].
  OPTIONAL{?s rdfs:label ?name .}
  FILTER CONTAINS(STR(?s),"保育所型/10")
}

【考察】
すべての施設は、「保育所型」か「幼稚園型」のいずれかのクラスに属している。
ただし、保育所型クラスに属する施設の、さらに詳細な施設種別は、クラスではなくプロパティで分類されている。よって、詳細な施設種別で分類するのは条件分岐がやや煩雑となる。(そもそもプロパティ設定自体が怪しい施設もあるし…)
そこで着目したのが対象施設のSubject URI(各施設の主語URI。?s のこと)。
施設種別で分類するには、?s に対し、下記の文字列を含むかどうかでフィルタリングするのが一番手っ取り早い。
FILTER CONTAINS(STR(?s),"保育所型/10")

幼稚園型施設   → 「幼稚園型」文字列が含まれる
認可保育所    → 「保育所型/10」文字列が含まれる
幼保連携型認定こども園 → 「保育所型/20」文字列が含まれる
小規模保育事業  → 「保育所型/30」文字列が含まれる
家庭的保育事業  → 「保育所型/40」文字列が含まれる
事業所内保育事業 → 「保育所型/50」文字列が含まれる
横浜保育室    → 「保育所型/60」文字列が含まれる
届出済認可外保育施設 → 「保育所型/70」文字列が含まれる


4 1歳児の入所が可能な施設を抽出

select *
where {
 ?s ic:ID [ic:識別値 ?id ]  ;
    ic:住所 [ ic:区 ?ku ;  ic:表記 ?jusho ];
    dsv:入所可能人数 [ic:種別 "1歳児" ; ic:数値 ?h_kanou_ninzu ].
   OPTIONAL{?s rdfs:label ?name .}
   FILTER (xsd:decimal(?h_kanou_ninzu) > 0 )
}


5 「認定こども園 日野幼稚園」の全情報を取得

PREFIX hoiku:<https://data.city.yokohama.lg.jp/lod/v2/保育所型/> 
PREFIX yochi:<https://data.city.yokohama.lg.jp/lod/v2/幼稚園型/> 
select *
where {
{
 hoiku:202060017 ic:メタデータ / ic:日付 [ic:標準型日付 ?a_joho_koshinbi]  .
}UNION{
 hoiku:202060017 ic:ID [ic:識別値 ?a_id ]  .
}UNION{
 hoiku:202060017 rdfs:label ?a_name  .
}UNION{
 hoiku:202060017 ic:種別コード [ic:種別 ?a_shubetsu ; ic:識別値 ?a_shubetsuData ] .
}UNION{
 hoiku:202060017 ic:住所 ?bn5 .
 ?bn5 ic:区 ?a_ku .
 ?bn5 ic:表記 ?a_jusho .
 OPTIONAL {?bn5 ic:郵便番号 ?a_yuubin.}
}UNION{
 hoiku:202060017 ic:アクセス ?bn5_2 .
 OPTIONAL {?bn5_2 ic:備考 ?h_ikikata .}
}UNION{
 hoiku:202060017 ic:連絡先 ?bn6 .
 OPTIONAL{ ?bn6 ic:電話番号 ?a_tel.}
 OPTIONAL{ ?bn6 ic:FAX番号  ?a_fax.}
 OPTIONAL{ ?bn6 ic:Webサイト ?a_Web.}
}UNION{
 hoiku:202060017 ic:関与 ?bn8 .
 OPTIONAL{ ?bn8 ic:関与者 [ic:名称 [ic:表記 ?a_secchisha]].}
 OPTIONAL{ ?bn8 ic:関与者 [ic:氏名 [ic:表記 ?y_encho]].}
}UNION{
 hoiku:202060017 ic:記述 ?bn10 .
 OPTIONAL{ ?bn10 ic:種別 ?a_sonotaL .}
 OPTIONAL{ ?bn10 ic:説明 ?a_sonotaD .}
}UNION{
hoiku:202060017 ic:利用可能時間 ?bn6_2 .
 OPTIONAL{ ?bn6_2 ic:種別 ?h_syubetsu }
 OPTIONAL{ ?bn6_2 ic:説明 ?h_setsumei }
}UNION{
 hoiku:202060017 ic:建物 ?bn7 .
 OPTIONAL{ ?bn7 ic:建物面積 [ic:数値 ?h_tatemono]. }
 OPTIONAL{ ?bn7 ic:敷地面積 [ic:数値 ?h_shikichi]. }
}UNION{
 hoiku:202060017 dsv:評価 ?bn9 .
 ?bn9 dsv:評価ステータス ?h_dai3sya .
 OPTIONAL{ ?bn9 dsv:結果公表日時 ?h_dai3date .}
 OPTIONAL{ ?bn9 ic:参照 [ic:参照先 ?h_dai3URL ] .}
}UNION{
 hoiku:202060017 ic:収容人数 [ic:種別 ?h_syuyou ; ic:数値 ?h_syuyouninzu ].
}UNION{
 hoiku:202060017 dsv:入所児童数 ?bn12 .
 OPTIONAL{ ?bn12 ic:種別 ?h_nyusyojidou .}
 OPTIONAL{ ?bn12 ic:数値 ?h_nyusyojidou_ninzu .}
 OPTIONAL{ ?bn12 ic:メタデータ / ic:日付 / ic:標準型日付 ?h_nyusyojidou_meta. }
}UNION{
 hoiku:202060017 dsv:入所可能人数 ?bn13 .
 OPTIONAL{ ?bn13  ic:種別 ?h_kanou .}
 OPTIONAL{ ?bn13 ic:数値 ?h_kanou_ninzu .}
 OPTIONAL{ ?bn13 ic:メタデータ / ic:日付 / ic:標準型日付 ?h_kanou_meta. }
}UNION{
 hoiku:202060017 dsv:入所待ち人数 ?bn14 .
 OPTIONAL{ ?bn14  ic:種別 ?h_machi .}
 OPTIONAL{ ?bn14 ic:数値 ?h_machi_ninzu .}
 OPTIONAL{ ?bn14 ic:メタデータ/ ic:日付 / ic:標準型日付 ?h_machi_meta. }
}UNION{
 hoiku:202060017 ic:備考 ?y_bikou .
}UNION{
 hoiku:202060017 ic:料金 [ ic:種別 ?y_ryokins ; ic:説明 ?y_ryokin ].
}UNION{
 hoiku:202060017 dsv:認可年月日 ?y_ninka .
}
}

【雑感】
上記が、ある施設が有している全プロパティの値を表示させるクエリだ。
施設によって、プロパティの存在・不存在がバラバラなので、UNION句とOPTIONAL句の組み合わせを工夫することによりそれに対応させている。

しかしながらいつも思うのが、共通語彙基盤のRDFをグラフ型データベースから抽出するには、このような複雑な呪文を詠唱する必要があるため、利活用のハードルがどーんと上がってしまうのが残念すぎること。呪文が長い分、抽出効率も悪いし。

一方、フラットな構造であるウィキデータから、例えば「横浜市役所」の持つ全部の情報を手に入れるのは、たった1行のクエリでできる。超かんたんで速い。スマート。
SELECT * WHERE { wd:Q11543018 ?p ?o .}

共通語彙基盤のデータ構造はマークアップ的でありXMLとは相性がいいが、それをグラフ構造(トリプル)にするとブランクノードが挟まりすぎて非常に扱いづらくなる。
共通語彙基盤対応データは、グラフデータベースに突っ込んで使うより、XMLを直接パーシングするなり、JSON-LDのままjavascriptで取り込んじゃったりするほうが向いてるのではないかと思えてきた。このあたり、IMI関係者や有識者の意見をじっくり聞いてみたいところ。

なお、横浜市の保育所等LODはURIの参照解決に対応しており、施設のSubject URIにアクセスするとJSON-LD形式で当該施設の全データが取り出せる。
https://data.city.yokohama.lg.jp/lod/v2/保育所型/202060017
(読みたいときはUnicode(UTF16)でデコードしてください。)
エンドポイントに呪文を送るより、素直に参照解決データを使うほうが簡単だったかも、とアプリを完成させてから思うのでありました。うーむ。。。

0 件のコメント:

コメントを投稿