指定する列の値を置換するときに使うよね。普通に使っていれば何の問題もない。ただ、動作や性能の考慮をしたほうがよいねと。クエリの評価パフォーマンスの低下に気を使わないのはよくないと思うのです。
Table.ReplaceValue(
table as table,
oldValue as any,
newValue as any,
replacer as function,
columnsToSearch as list
) as table
構文には複雑さはない。ボタンポチポチでも利用できるし。
動作を知る
たとえば、Column1 / Column2 / Column3 に含まれる値のうち 0 を 2 に置換するときのクエリは次の通り。
let
Source = SourceTable,
ReplacedValue = Table.ReplaceValue(
Source,
0, 2,
Replacer.ReplaceValue,
{"Column1", "Column2", "Column3"}
)
in
ReplacedValue
同じ結果を得るために Table.TransformColumns による別の記述も可能。
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 による記述でも同じ結果を得ることができる。
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
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
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
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
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操作でできるのだから、こっちのほう使った方がよいのでは?と。
思ったこと🙄
どのようなクエリ記述するかはご自由にどうぞ。でもさ、利用できるリソースには限りがあるのだから細かいことであっても気にすべきだと思うね。
観察すべきことがどこまで計測できるかというこだわりはないけれども、比較するには充分な状況を確認することができる。
// 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
)
その他