Power Query workout - Table.ReplaceValue

指定する列の値を置換するときに使うよね。普通に使っていれば何の問題もない。ただ、動作や性能の考慮をしたほうがよいねと。クエリの評価パフォーマンスの低下に気を使わないのはよくないと思うのです。

https://docs.microsoft.com/en-us/powerquery-m/table-replacevalue

```text
Table.ReplaceValue(
    table as table,
    oldValue as any,
    newValue as any,
    replacer as function,
    columnsToSearch as list
) as table
```
構文には複雑さはない。ボタンポチポチでも利用できるし。

# 動作を知る
たとえば、Column1 / Column2 / Column3 に含まれる値のうち 0 を 2 に置換するときのクエリは次の通り。

```text:Power&#x20;Query
let
    Source = SourceTable,
    ReplacedValue = Table.ReplaceValue(
        Source,
        0, 2,
        Replacer.ReplaceValue,
        {"Column1", "Column2", "Column3"}
    )
in
    ReplacedValue
```
同じ結果を得るために Table.TransformColumns による別の記述も可能。

https://docs.microsoft.com/en-us/powerquery-m/table-transformcolumns

```text:Power&#x20;Query
let
    Source = SourceTable,
    TransfromColumns = Table.TransformColumns(
        Source,
        {
            {"Column1", each if _ = 0 then 2 else _, type number},
            {"Column2", each if _ = 0 then 2 else _, type number},
            {"Column3", each if _ = 0 then 2 else _, type number}
        }
    )
in
    TransfromColumns
```
また、Table.AddColumn による記述でも同じ結果を得ることができる。

https://docs.microsoft.com/en-us/powerquery-m/table-addcolumn

```text:Power&#x20;Query
let
    Source = SourceTable,
    Added_Column1 = Table.AddColumn(
        Source, "_Column1",
        each if [Column1] = 0 then 2 else [Column1],
        type number
    ),
    Select_Columns1 = Table.SelectColumns(
        Added_Column1,
        {"_Column1", "Column2", "Column3"}
    ),
    Renamed_Column1 = Table.RenameColumns(
        Select_Columns1,
        {"_Column1", "Column1"}
    ),
    Added_Column2 = Table.AddColumn(
        Renamed_Column1,
        "_Column2",
        each if [Column2] = 0 then 2 else [Column2],
        type number
    ),
    Select_Columns2 = Table.SelectColumns(
        Added_Column2,
        {"Column1", "_Column2", "Column3"}
    ),
    Renamed_Column2 = Table.RenameColumns(
        Select_Columns2,
        {"_Column2", "Column2"}
    ),
    Added_Column3 = Table.AddColumn(
        Renamed_Column2,
        "_Column3",
        each if [Column3] = 0 then 2 else [Column3],
        type number
    ),
    Select_Columns3 = Table.SelectColumns(
        Added_Column3,
        {"Column1", "Column2", "_Column3"}
    ),
    Renamed_Column3 = Table.RenameColumns(
        Select_Columns3,
        {"_Column3", "Column3"}
    )
in
    Renamed_Column3
```
同じ結果を得ることができるこれら 3つクエリは、クエリフォールディングを等しく適切に利用することが可能。Table.ReplaceValue は Table.AddColumn → Table.SelectColumns → Table.RenameColumns の動作をするから、クエリの評価パフォーマンスも同じで差は発生しない。ステップが増えるから、列の追加を利用しているから、という理由でパフォーマンスが低下することはない。

# 評価パフォーマンスが低下する使い方
確かめれば明白。
Table.ReplaceValue をボタンポチポチで使うとき、`newValue` パラメータには "値" が指定されるわけだけど、引数を評価式にすることで便利に使おうとしているのかもだ。同じ行に含まれる列の値に置換したいといっても、確実に評価パフォーマンスの低下が発生するので使わない。クエリフォールディングが期待できないっていう時点で使わないのである。

**17.0 ~ 18.0 sec**
```text:Power&#x20;Query
let
    Source = SourceTable,
    ReplacedValue = Table.ReplaceValue(
        Source,0,
        each [Column1],Replacer.ReplaceValue,
        {"Column2"}
    ),
    ChangedType = Table.TransformColumnTypes(
        ReplacedValue,
        {"Column2", type number}
    )
in
    ChangedType
```
**17.0 ~ 18.0 sec**
```text:Power&#x20;Query
let
    Source = SourceTable,
    ReplacedValue = Table.ReplaceValue(
        Source,0,
        each [Column1],Replacer.ReplaceValue,
        {"Column3"}
    ),
    ChangedType = Table.TransformColumnTypes(
        ReplacedValue,
        {"Column3", type number}
    )
in
    ChangedType
```
**1.7 ~ 2.0 sec**
```text:Power&#x20;Query
let
    Source = SourceTable,
    AddedConditionalColumn = Table.AddColumn(
        Source, "_Column2",
        each if [Column2] = 0 then [Column1] else [Column2],
        type number
    ),
    RemovedOtherColumns = Table.SelectColumns(
        AddedConditionalColumn,
        {"Column1", "_Column2", "Column3"}
    ),
    RenamedColumns = Table.RenameColumns(
        RemovedOtherColumns,
        {"_Column2", "Column2"}
    )
in
    RenamedColumns
```
**1.7 ~ 2.0 sec**
```text:Power&#x20;Query
let
    Source = SourceTable,
    AddedConditionalColumn = Table.AddColumn(
        Source, "_Column3",
        each if [Column3] = 0 then [Column1] else [Column3],
        type number
    ),
    RemovedOtherColumns = Table.SelectColumns(
        AddedConditionalColumn,
        {"Column1", "Column2", "_Column3"}
    ),
    RenamedColumns = Table.RenameColumns(
        RemovedOtherColumns,
        {"_Column3", "Column3"}
    )
in
    RenamedColumns
```

type any から type number への変換が必要 : 1 sec 程度 追加されるものの、明らかに評価パフォーマンスが低下する。条件列の追加 → 列の選択 → 列名の変更 はGUI操作でできるのだから、こっちのほう使った方がよいのでは？と。

# 思ったこと🙄
どのようなクエリ記述するかはご自由にどうぞ。でもさ、利用できるリソースには限りがあるのだから細かいことであっても気にすべきだと思うね。

https://docs.microsoft.com/en-us/power-query/querydiagnostics

観察すべきことがどこまで計測できるかというこだわりはないけれども、比較するには充分な状況を確認することができる。


```text:Power&#x20;Query
// RowCount
2000000 meta [IsParameterQuery=true, Type="Number", IsParameterQueryRequired=true]

// SourceTable
Table.FirstN(
    Table.FromColumns(
        {
            List.Generate(()=>1, each true, each 1),
            List.Generate(()=>0, each true, each 0),
            List.Generate(()=>0, each true, each List.Max({0,1}, null, _))
        },
        type table [Column1 = number, Column2 = number, Column3 = number]
    ),
    RowCount
)
```
# その他
https://powerbi.connpass.com/

- [Power BI 勉強会 - connpass](https://powerbi.connpass.com/)
- [Power BI Desktop を使い始めるには](http://qiita.com/PowerBIxyz/items/6c4735cb5b5712e70981)
- [Excel で Power Query を使い始めるには](http://qiita.com/PowerBIxyz/items/52e563037b855a099ca9)