Table.Profile()可以抽象地概括为function(table as table, optional addtionalAggregates as nullable list) as table,大概的意思为:(1)该函数的第一个参数为需要列汇总的表格。(2)第二个参数的性质为可选的,当使用者没有填写第二个参数时,函数默认产生的列汇总为最小值,最大值,平均值,标准差,数字个数,空值个数以及唯一值个数,如果需要除此之外的列汇总,需要以{{}..{}}形式的复合串列填写第二个参数(本文所给出的填写方法来自Chris Webb的文章)。
本案例假设上表(DB)记录的每一个行业最多只有9间企业,该表的每一列记录了处于该行业的每一间公司的市场份额,现在要求基于每一列对应的HHI(市场份额平方之和)大小对表格进行分割,HHI大于0且小于等于1500的为一组,大于1500且小于等于2500为另外一组,剩下的列归类为一组。转换后的效果如下图:
以下为以Table.Profile为中心实现转换的代码:
let Profiling= Table.Profile( DB, { { "HHI", each Type.Is( _, type nullable number), each List.Sum( List.Transform( _, (x) ⇒ Number.Power( x, 2 ) ) ) } } ), Partition= Table.Partition( Profiling, "HHI", 3, each List.Count( List.RemoveFirstN( { 1500, 2500 }, (x)⇒ x ‹ _ ) ) ), Outcome= List.Buffer( List.Transform( Partition, each Table.SelectColumns( DB, Table.Column( _, "Column" ) ) ) ) in Outcome
由于Table.Profile()是不会自动生成每一列的HHI,所以需要填写第二参数建立名称为HHI的列汇总。第二个参数的要点:(1)紧随第一个each的函数通过列的数据类型识别第二个each后面跟着的函数需要进行汇总的对象(特定数据类型的列),不符合特定数据类型的列在汇总列中的对应值为null。(2)第二个each传入的是DB按列分解所得的串列。Partition步骤的要点:(1)由于分组的组数为3,所以填入hash参数的函数结果只能为0/1/2。(2)List.RemoveLastN()第二个参数的条件判断过程类似米诺骨牌,每一个元素对应一张骨牌,如果第一个元素的结果为true它所对应的骨牌倒下,然后轮到下一个元素,整个过程直到第一张不倒下的骨牌出现为止,然后函数会把所有倒下的骨牌对应的元素移除。(3)如果HHI大于2500,经过List.RemoveLastN处理后的{1500, 2500}将会变成{},代入List.Count()为0;如果1500<HHI<=2500,经过List.RemoveLastN处理后的{1500, 2500}将会变成{2500},代入List.Count()为1;如果HHI<=1500,经过List.RemoveLastN处理后的{1500, 2500}将不变,代入List.Count()为2。分好组后,只需要把每一组的Column列传入Table.SelectColumns()就完成需要的转换。笔者最初的思路是使用Table.Group()分组,以上的Outcome步骤与Partition步骤就可以合二为一,但是并没有成功,希望熟悉Table.Group()的各位爱好者能分享一下使用Table.Group()的解法。
Table.Group分组最后是同一个表吧,这里要分成三个表的话怎么样不都得用Table.Partition.
let
Profiling=
Table.Group(
Table.AddColumn(
Table.Profile(
DB,
{
{
"HHI",
each Type.Is( _, type nullable number),
each
List.Sum(
List.Transform(
_,
(x) => Number.Power( x, 2 )
)
)
}
}
)
,"组别"
,each if [HHI]>=2500 then 3 else if [HHI]>=1500 then 2 else 1
),
"组别",
{"column",each Table.SelectColumns(DB,[Column])}
)
in
Profiling