読者です 読者をやめる 読者になる 読者になる

空談録

http://artfulplace.net/blogs/ からひっこしつつ

Power Queryでキーを基にテーブルを結合する

なんか前にやった気がする、しない
あともっと簡単にできる気がする。知らない

Power QueryでTableを拾って来たらTableが3つの要素に分かれていた!なんてことがあるかもしれません。あるのか
話が進まないので 【都道府県】人口・面積・人口密度ランキング みたいなテーブルを拾ってきたときに、データを基に都道府県別の人口、面積、人口密度のテーブルを作成したいとします
こういうときはどうするかという話です

Table.Joinとかその辺をうまく使えばできる気もするのですが、説明を読んだことがありません。ゴリゴリ回します

拾ってきたTableからまず順位のためのインデックス列をすべて削除します。
すると6列になります。これらの列名を面倒なので、"p1","v1","p2","v2","p3","v3"に変更します。
とりあえず準備は完了

次にテーブルを分解します
現状だとテーブルの一つの要素に全く関係性のないデータが入っているので、3つのテーブルに分解しましょう

こんな感じ

baseTable = Table.RenameColumns(...),
t1 = Table.SelectRows(baseTable, {"p1","v1"}),
t2 = Table.SelectRows(baseTable, {"p2","v2"}),
t3 = Table.SelectRows(baseTable, {"p3","v3"}),

Table.SelectRowsで列名のlistを引数に指定することで、列名を使ったTableの抽出が可能です
今回は列名が厳密なのでハードコーディング感バリバリ

とりあえずt1,t2,t3にそれぞれ都道府県名と値が入った状態です
これをt1の一致する都道府県名のところに値を追加してみます

t1にAddColumnしてその値のところで一致する要素を拾ってきます
t1,t2の結合がこんな感じ

t12 = Table.AddColumn(t1, "v2", (x)=> Table.SelectRows(t2, (y)=> x[p1] = y[p2]){0}[v2])

Table.Firstを使いたいのですが第2引数の使い方が分からず…
追記:壮大な勘違いをしていたのですが、LINQみたいにFirstでcondition指定できません。Firstはnull対策ができる{0}と同義です。
またFirstNもconditionが指定できますが、「インデックス0から、conditionがtrueを返さなくなるまでの範囲」を返します。最初の要素がconditionを満たさなければその時点で空のTableが返ります
おとなしくSelectRowsするしかなさそうです

しょうがないのでp1とp2が一致する行をSelectRowsで拾ってきて、その結果の位置0の要素を選択、そこからフィールドv2の値を拾ってくる形です
このとき要素が存在しないとおそらく{0}のところでErrorが返ります。ReplaceErrorValuesか何かで対処してください
今回は同名の要素が存在することがわかっているのでこんな書き方です

えっ?ソートしてくっつければいいじゃんって?
確かにt1,t2,t3をそれぞれpNでソートしてからインデックスで要素にアクセスして拾ってもよいのですが、t1にいったんインデックス列を追加して削除する必要があるので手間が増えます。あと同名の要素が存在しなかった瞬間に使えません

とりあえずクエリの全文を載せといて終了

let
    ソース = Web.Page(Web.Contents("http://uub.jp/rnk/p_j.html")),
    Data0 = ソース{0}[Data],
    ヘッダーとしての先頭行 = Table.PromoteHeaders(Data0),
    変更された型 = Table.TransformColumnTypes(ヘッダーとしての先頭行,{{"#", Int64.Type}, {"都道府県", type text}, {"人 口", Int64.Type}, {"面 積", type number}, {"人口密度", type number}, {"#_1", Int64.Type}, {"都道府県_2", type text}, {"#_3", Int64.Type}, {"都道府県_4", type text}}),
    #"名前が変更された列 " = Table.RenameColumns(変更された型,{{"都道府県", "p1"}, {"人 口", "v1"}, {"都道府県_2", "p2"}, {"面 積", "v2"}, {"都道府県_4", "p3"}, {"人口密度", "v3"}}),
    delCol = Table.RemoveColumns(#"名前が変更された列 ",{"#", "#_1", "#_3"}),
    t1 = Table.SelectColumns(delCol, {"p1", "v1"}),
    t2 = Table.SelectColumns(delCol, {"p2", "v2"}),
    t3 = Table.SelectColumns(delCol, {"p3", "v3"}),
    t12 = Table.AddColumn(t1, "v2", (x) => Table.SelectRows(t2,(y)=> x[p1] = y[p2]){0}[v2]),
    t123 = Table.AddColumn(t12, "v3", (x) => Table.SelectRows(t3,(y)=> x[p1] = y[p3]){0}[v3]) 
in
    t123

追記:Table.Joinの場合keyにつかったprefまで追加されます。なのでprefを削除する必要が出ます。とはいえちゃんと並び替えられますし上ほど変な処理はないのですが
Table.AddJoinColumnが近いかなーと思ったらこれはTableを追加してくれるので結局ExpandTableColumnして削除する必要があります。面倒…
NestedJoinも同様です。うーん
JoinKindがいまいちわかりません。今度調べましょう…

やっぱりデフォルトの変数名センスないと思います
センスないっていうよりいちいち書きたくない…


ということでなんとなくやったことを書いただけです

関数のあれも需要があるところから埋めたいという感じもありつつ
いまいちやる気が…

この辺で