Table.SelectColumns()

Table.SelelctColumns()可以抽象地概括为function(table as table, columns as any, optional missingField as nullable MissingField.Type) as table,大概的意思为:该函数的第一个参数为需要筛选列的表格。第二个参数对数据的类型没有限制,可以选择"筛选的唯一列"或者{"选择的列之一".."选择的列之一"}等形式填写第二个参数。第三个参数为可选参数,当这个参数没有填写时系统默认该值为MissingField.Error(当第二个参数填写的列名称并不是第一个参数的列之一时报错),如果填写的值为MissingField.Ignore,则会忽略第二参数填写的所有不存在于第一个参数的列,如果填写的为MissingField.UseNull,则会使用Null填充第二个参数填写的所有不存在于第一个参数的列。Table.SelectColumns()结果中的列在表格中的相对位置是由这个函数的第二个参数决定的,与该列对应的元素在串列中的相对位置保持一致。这个函数一般依赖使用者的意志决定留下来的列,但是也可以通过代码的逻辑决定最后留下来的列。

上图为记录了10位同学9个科目成绩的表格(Score),现在需要移除总得分排名4-10的列,可以通过以下代码实现:

let
    DHeaders = Table.DemoteHeaders( Score ),

    ToCols = Table.ToColumns( DHeaders ) ,

    SkipFirst = List.Skip( ToCols ),

    Top3=
        List.MaxN(
            SkipFirst,
            3,
            each
                List.Sum(
                    List.Skip( _ )
                )
        ),

    TargetColNames=
        List.Zip(
            List.Combine(
                {
                    List.FirstN( ToCols, 1 ),
                    Top3
                }
            )
        ){ 0 },

    Outcome = Table.SelectColumns( Score, TargetColNames )
in
    Outcome

以上代码的DHeaders步骤把Score表格的标题降级为首行,然后ToColumns步骤把DHeaders的结果以列为单位分解为{{}..{}}形式的复合串列,这个复合串列里的每一个元素对应分解前同一相对位置的列。由于第一列不参与筛选,SkipFirst步骤移除了上一个步骤所得串列的第一个元素。接着,Top3步骤挑选出剩下元素中总得分最多的前三位,结果为{{总得分前三之一}, {总得分前三之一}, {总得分前三之一}}。TargetColNames步骤先把SkipFirst步骤移除的科目列重新放置于上一步骤所得串列之首,然后使用List.Zip取出每一列的标题。这些标题传入Table.SelectColumns()后就完成移除排名4-10的同学的列的要求。如果不使用Table.SelectColumns()也可以达到同样的效果:

let
    DHeaders = Table.DemoteHeaders( Score ),

    ToCols = Table.ToColumns( DHeaders ) ,

    SkipFirst = List.Skip( ToCols ),

    Top3=
        List.MaxN(
            SkipFirst,
            3,
            each
                List.Sum(
                    List.Skip( _ )
                )
        ),

    ToTable=
        Table.FromColumns(
            List.Combine(
                {
                    List.FirstN( ToCols, 1 ),
                    Top3
                }
            )
        ),

    PHeaders = Table.PromoteHeaders( ToTable ),

    Outcome=
        Table.TransformColumnTypes(
            PHeaders,
            {
                { "科目", type text },
                { "同学6", Int64.Type },
                { "同学7", Int64.Type },
                { "同学8", Int64.Type }
            }
        )
in
    Outcome

以上代码并没有使用List.Zip()取出标题,而是在ToTable步骤中使用Table.FromColumns把复合串列还原成表格,之后还需要把首行升级为标题以及修改列的数据类型。借鉴M爱好者西瓜在108式的餐厅星评排序的思路,也可以达到同样的目的:

let
    ColNames = Table.ColumnNames( Score ),

    TargetColNames=
        List.Zip(
            List.MaxN(
                List.Transform(
                    List.Skip( ColNames ),
                    each
                    { 
                        _,
                        List.Sum(
                            Table.Column( Score, _ )
                        )
                    }
                ),
                3,
                each _{1}
            )
        ),

    Outcome=
        Table.SelectColumns(
            Score,
            List.Combine(
                {
                    List.FirstN( ColNames, 1 ),
                    TargetColNames{ 0 }
                }
            )
        )
in
    Outcome

以上代码的TargetColNames步骤中利用了List.Transform()构造了{{"同学N", "对应总得分"}..{"同学N", "对应总得分"}}的复合串列,然后通过List.MaxN以总得分为依据保留这个串列中总分前3的同学所对应的元素,最后使用List.Zip()提取剩下的元素里的标题凑成一个新的简单串列。在这个串列的最开头加上科目列的名字,传入Table.SelectColumns()就可以得到只有总分前3的同学的表格。因为总分前三相当于平均分前三,所以这个问题还可以这样解答:

let
    Profiling = Table.Profile( Score ),

    Top3 = Table.MaxN( Profiling, "Average", 3 ),

    Outcome = Table.SelectColumns( Score, { "科目" } ﹠ Top3[Column] )
in
    Outcome

通过Profiling步骤,可以得到以上对Score表格的各种列汇总。有了这些汇总列,就很容易找到总分前三的同学,再把这些同学对应的列的名称传入Table.SelectColumns()就可以成功筛选出只有总分前三的列。

附件

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注