【Power Query】JSONの型ゆれ|文字列と数値の混在を吸収する

【Power Query】JSONの型ゆれ|文字列と数値の混在を吸収するのアイキャッチ Power Query

外部APIのJSONを読み込むと、同じ項目が "12"(文字列)と 12(数値)の両方で返ってくることがあります。こうした型ゆれは、見た目が同じ数字でも、Power Queryでは別の型として扱われます。そのまま集計すると、エラーになります。

この記事では、混在した値を数値にそろえる書き方と、変換できない値が混ざったときの挙動を、実測した結果で紹介します。サンプルのJSONは自作したもので、実際のデータではありません。

想定読者:Power Queryで外部APIやJSONを取り込み、集計や並べ替えをしている人です。

JSONの型ゆれで何が起きるか

次のJSONを例にします。Qty と Price で、行ごとに文字列と数値が混ざっています。

[
  {"OrderId":"A001","Qty":"12","Price":1200},
  {"OrderId":"A002","Qty":5,"Price":"800"},
  {"OrderId":"A003","Qty":"7","Price":950.5}
]

Json.Document で読み込むと、Qty は1行目が文字列、2行目が数値になりました。Price も同じように混ざります。この列を List.Sum で合計すると、エラーになりました。

混在したまま昇順に並べ替えると、5, 12, 7 の順になりました。今回の1例だけの結果で、この並びになる理由は調べていません。

解決コード:try Number.Fromで数値にそろえる

try Number.From(_) otherwise null を、列ごとの変換に使います。

let
    Raw = "[{""OrderId"":""A001"",""Qty"":""12"",""Price"":1200},{""OrderId"":""A002"",""Qty"":5,""Price"":""800""},{""OrderId"":""A003"",""Qty"":""7"",""Price"":950.5}]",
    Src = Table.FromRecords(Json.Document(Text.ToBinary(Raw))),
    Fixed = Table.TransformColumns(Src, {
        {"Qty", each try Number.From(_) otherwise null, type nullable number},
        {"Price", each try Number.From(_) otherwise null, type nullable number}
    })
in
    Fixed

Raw の部分を、実際のAPIの取得結果に置き換えて使います。Web.Contents で取得した場合は、今回は検証していません。変換後の合計は、Qty が24、Price が2950.5になりました。

Power Queryで文字列と数値が混在した値をNumber.Fromとtry Number.Fromで変換した比較表と、変換後のQty列・Price列
再現用の最小コードの実行結果。左は変換の比較、右は解決コードで変換した結果

4つの書き方を比べた結果

同じ値を、4つの書き方で変換して比べました。「型を判定して変換」は、Value.Is で型を調べて分岐する書き方です。

ToNum = (v as any) as nullable number =>
    if v = null then null
    else if Value.Is(v, type number) then v
    else if Value.Is(v, type text) then
        (let t = Text.Trim(Text.Replace(v, ",", "")) in
         if t = "" then null else try Number.FromText(t, "en-US") otherwise null)
    else null
値Number.From型を判定して変換try Number.FromText.From経由
“12”12121212
55555
” 7 “7777
“1,200”1200120012001200
“”(空文字)nullnullnullnull
nullnullnullnullnull
“abc”エラーnullnullエラー

「Text.From経由」は、Number.FromText(Text.From(v, "en-US"), "en-US") の形です。差が出たのは "abc" だけでした。空白やカンマ付きの文字列も、Number.From でそのまま数値になりました。型を判定する書き方は、try Number.From と、表の7つの値に950.5を加えた8つの値で、同じ結果でした。短い1行で足ります。

この結果は、日本語環境のExcel(Windows版、バージョン16.0)のものです。"1,200" の読み方は地域設定で変わる可能性があり、他の環境は未検証です。引数や戻り値の仕様は、Number.Fromの公式リファレンスを参照してください。

変換できない値が混ざったとき

"12"、"abc"、5 の3行で、Table.TransformColumnTypes を使って数値型に変換しました。

  • "abc" のセルだけがエラーになる
  • 行は消えず、列の合計はエラーになる
  • エラーの行を除くと、2行が残る

一方、try Number.From(_) otherwise null なら、"abc" は null になり、合計は17でした。集計は止まりませんが、不正な値がエラーとして表に出ません。

なお、空白・カンマ・空文字・nullを含む列は、Table.TransformColumnTypes に "en-US" を指定するだけでも数値になりました。12、" 7 "、"1,200"、""、null、5 の列で、合計は1224でした。

よくある失敗

1. 変換せずに集計する

混在した列を List.Sum に渡すと、エラーになります。

2. tryで不正な値に気づかなくなる

"abc" が null になるだけで、エラーとして表に出ません。変換後に null の件数を数えると、気づけます(先ほどの3行では1件でした)。元のデータに null がある場合は、その分も数えられる点に注意してください。

3. 全角数字や単位付きの値を想定していない

"12"(全角)と "12円" は、Number.From でエラーになりました。こうした値は前処理が必要ですが、前処理の方法は今回は検証していません。

4. 文字列のまま並べ替える

文字列の "12"、"5"、"7" を昇順に並べると、12, 5, 7 の順です。数値にそろえてから並べ替えると、5, 7, 12 になりました。

応用例

結合のキーが数値と文字列で混ざる場合は、【Power Query】キーの型がバラバラで結合できない時の対処法も参照してください。JSONの時刻が文字列で届く場合は、【Power Query】時刻が9時間ズレる原因とタイムゾーン変換で扱っています。

まとめ

  • JSONでは、同じ項目の文字列と数値が混在することがある
  • 混在のまま集計すると、エラーになる
  • try Number.From(_) otherwise null で、数値にそろえられる
  • 変換できない値は null になるので、変換後に件数を確認する

コメント

タイトルとURLをコピーしました