2015-07-25

文字列の分割とリスト属性の展開

同一の地物が同じ属性項目について不特定数の値を持ちうる場合、属性データの格納方法としてテーブル形式しかサポートしていないフォーマットではカンマなどで区切って連結したひとつの文字列として記録せざるを得ないことがありますが、そのままでは、個々の値での検索を行いたい場合など不便なこともあります。

FMEには同じ名前で複数の値が格納できる「リスト属性」の仕組みがあり、ひとつの文字列を特定の文字で分割してリスト属性に変換することや、フィーチャーをリスト属性の要素数分コピーして各要素の値をコピー後のフィーチャーに配分すること(「リスト属性の展開」と呼ぶことにします)ができます。

ここでは、国土数値情報「医療機関」 Shapefile 形式データにおいて全角空白区切りで複数の診療科目が列挙されている文字列属性を個々の診療科目を要素とするリスト属性に変換した後、それを展開することによって、診療科目による医療機関の検索が容易なテーブルを作成するワークスペース例を掲げます。

FME 2015.1.1.0 build 15515

=====
国土数値情報「医療機関」 Shapefile 形式データの属性テーブル (1医療機関1レコード) に基づき、各医療機関の診療科目ごとのレコードで構成されるCSVテーブルに変換する。

ソースデータ:
国土数値情報ダウンロードサービスサイトよりダウンロードした「医療機関」データ (Shapefile 形式)

国土数値情報「医療機関」属性テーブル: FME Data Inspector による表示










次の3つのフィールドに診療科目名(複数ある場合は全角空白区切り, P04_005, P04_006 は空白の場合もある)が記録されている。
P04_004 (診療科目1)
P04_005 (診療科目2)
P04_006 (診療科目3)
=====

FMEワークスペース例



















[SHAPE] リーダー: 国土数値情報「医療機関」データを読み込む。
AttributeRenamer: 属性名を分かり易い名称に変更する (必須ではないが作業し易くするため)。
StringConcatenator: 診療科目1~診療科目3 を区切り文字 (全角空白) をはさんで連結する。
AttributeSplitter: 連結後の文字列を分割して個々の診療科目を要素とするリスト属性に格納する。
AttributeRemover: 不要な属性を削除する。
ListExploder: 診療科目を格納したリスト属性を展開する。
[CSV] リーダー: CSVファイルにテーブルを出力する。

結果: FME Data Inspector による表示 (所在地の詳細は隠してあります)


























国土数値情報「医療機関」データでは診療科目は 3 つのフィールドに記録されており、それぞれ全角空白区切りで複数の診療科目が記述されています。2番目、3番目のフィールドは空の場合もあります。
個別の診療科目名への分割が1回の処理でできるよう、事前に StringConcatenator によってそれらの 3 フィールドの文字列を全角空白をはさんで連結し、「診療科目連結」属性に格納しました。

AttributeSplitter は文字列を分割するために非常によく使われるトランスフォーマーです。

AttributeSplitter パラメーター設定画面



















Attribute to Split: 診療科目連結  (分割する文字列を格納している属性)
Delimiter or Format String: <全角空白>  (区切り文字。画面上では見えないが全角空白を入力)
Trim Whitespaces: Both  (分割後文字列の前後の空白を削除する)
List Name: 診療科目  (分割後の文字列を格納するリスト属性名)
Drop Empty Part: Yes  (空の要素は削除する)

これによって、「診療科目連結」に格納されている属性文字列が全角空白文字で分割され、分割後の文字列、すなわち個々の診療科目名が「診療科目{}」というリスト属性に格納されます。リスト属性の各要素には、次のように {} 内でインデクス (0から始まる連番) を指定することによってアクセスできます。
診療科目{0}
診療科目{1}
診療科目{2}
...

リスト属性を展開すると、そのリスト属性以外の属性やジオメトリはリストの要素数分コピーされて展開後のフィーチャーに引き継がれます。ここでは、以後の処理では使われない属性が無用にコピーされるのを防ぐため、AttributeRemover によってそれらを削除しました。

また、このワークスペース例では行っていませんが、ジオメトリも不要なので、GeometryRemover によって削除しておいた方がベターです。あるいは、最終的に属性しか使用しないので、[SHAPE] リーダーではなく、[DBF] リーダーによって最初から属性テーブルのみを読み込むこともできます。

ListExploder がリスト属性の展開を行います。

ListExploder パラメーター設定画面




















List Attribute: 診療科目{}  (展開するリスト属性)
他のパラメーターはデフォルトのまま。

この設定で実行すると、「診療科目{}」の要素数だけ各フィーチャーがコピーされ、それらにリスト属性の個々の要素が配分されてリスト属性名と同じ名前の属性(この場合は「診療科目」)に格納されます。

Conflict Resolution パラメーターがデフォルトの "Use Original" (オリジナルを使う) であるときは、フィーチャーがリスト名と同じ名前の属性をすでに持っていた場合に元の属性値の方が維持され、リストの要素は失われます。この例では、リスト属性を展開する前のフィーチャーには「診療科目」属性がなく属性名が衝突することはないのでデフォルトのままにしていますが、属性名が衝突することがある場合で、その属性値をリスト要素の値に置き換える必要があるときには、このパラメーターを "Use Incoming List" (リストの要素を使う) に変更してください。

[2017-04-11 追記] FME 2016 以降では、"Use Incoming List" の方がデフォルトになっています。

[2017-04-11 更新] 文字列の分割、リスト属性の展開だけでなく、リスト属性を利用することによって実現できる処理はたくさんあります。Transformer Gallery において、リスト属性に関する処理を行うトランスフォーマーは、FME 2016 以前では Lists カテゴリー、FME 2017 では Attributes カテゴリーに格納されています。


補足: 構造化リスト (structured list) について

リスト属性には、次のようにドットをはさんで連結された「メンバー名」を伴うものもあります。メンバー名を伴わないリスト属性と区別するときには、この形態のリスト属性を「構造化リスト (structured list)」または「複合リスト (complex list)」と呼ぶことがあります。
-----
_list{}.member1
_list{}.member2
-----

構造化リストには、同じインデクスの要素グループとして、メンバー名 (上の例では "member1", "member2") で区別される複数の値を格納することができます。

例えば、Aggregator によって複数のフィーチャーのジオメトリを集約してマルチパートのジオメトリを持つひとつのフィーチャーを作成するとき、List Name パラメーターでリスト名を指定することにより、元のフィーチャーが持っていた全ての属性を構造化リストに格納して集約後のフィーチャーに与えることができ、そのメンバー名は元の属性名と等しくなります。例えば元のフィーチャーが "ID", "Name" という属性を持っていたならば、集約後のフィーチャーに与えられる構造化リストは次のようになります。
-----
_list{}.ID
_list{}.Name
-----

要素数はそのフィーチャーに集約されたフィーチャーの数に等しく、インデクスが同じ要素グループが集約前のひとつのフィーチャーが持っていた属性の集合と対応します。

構造化リストを ListExploder によって展開したときは、各要素の値はメンバー名と同じ名前の属性としてコピーされたフィーチャーに配分されます。上の例の構造化リスト "_list{}" (ListExploder の List Name パラメーター選択リストではメンバー名の部分は表示されません) を展開すると、展開後のフィーチャーに配分されたリストの要素の格納先の属性名は "ID", "Name" になります。メンバー名を持たないリストとの違いに留意してください。

0 件のコメント:

コメントを投稿