通过List.Transform(Many())实现行列互换

除了Table.Transpose()与List.Zip()能够完成行列互换,List.TransformMany()也可实现这一动作,不过代码相对没有那么简洁。

如果需要为上表添加行加总与列加总,使之转化为:

需要定义以下fnAddSum()函数:

( input as list ) as list ⇒
    let
        Outcome=
            List.Transform(
                input,
                each
                    List.Combine(
                        {
                            _,
                            { List.Sum( _ ) }
                        }
                    )
            )
    in
        Outcome

该函数通过List.Transform()历遍input里的每一个串列(list),并在这些串列的结尾处添加一个元素,这个元素为串列原先所有的元素之和。

为实现行列转换,需要定义以下fnTranspose()函数:

( input as list ) as list ⇒
    let
        Outcome=
            List.TransformMany(
                { input },
                each
                    List.Numbers(
                        0,
                        List.Count( _{0} )
                    ),
                ( x, y ) ⇒ List.Transform( x, each _{y} )
            )
    in
        Outcome

fnTranspose()的轴心骨为List.TransfromMany(),这个函数产生的串列如果代入List.Count(),其产生的结果将会等于该函数的第一个参数和第二个参数分别代入List.Count()的结果之乘积。为了构造出Table.FromRows()或者Table.FromColumns的第一个参数所需要的结构,List.TransformMany()的第一个参数和第二个参数这个两个之中需要有一个满足代入List.Count()的结果为1,而另外一个代入List.Count()的结果为表格的行数或者列数。在fnTranspose()中List.TransformMany()的第一个参数由于在自变量input外面套一个{},第一个参数代入List.Count()的结果为1。第二个参数构造的串列代入List.Count()后的结果刚好为行的总数或者列的总数,为在第三个参数历遍处于同一行或者同一列的元素作准备。值得一提的是,其实fnTranspose()也可以围绕着List.Transform()进行构造:

( input as list ) as list ⇒
    let
        Outcome=
            List.Transform(
                List.Numbers(
                    0,
                    List.Count( input{0} )
                ),
                (y) ⇒
                    List.Transform(
                        input,
                        (x) ⇒ x{y}
                    )
            )           
    in
        Outcome

使用List.Transform()构造fnTranspose()其实十分类似VBA的For..Next里再套For..Next,外层的List.Transform()控制的是行数或者列数,而内层的List.Transform()则是控制对应的列数或者行数。

最后,为还原丢失的数据类型需要定义fnDataType()函数:

( input as table, datatype as type ) as table ⇒
    let
        Outcome=
            Table.TransformColumnTypes(
                input,
                List.TransformMany(
                    Table.ColumnNames( input ),
                    each { datatype },
                    ( x, y ) ⇒ { x, y }
                )
            )
    in
        Outcome

这个函数需要理解了List.TransformMany()里第一个参数调用了Table.ColumnNames的结果(该结果如果代入List.Count()将等于表格的列数),第二个参数由于串列里只有一个元素,所有List.TransformMany()的结果的元素的个数会与列数一样(列数=列数*1)。

以下为从列开始转化的代码:

let
    ToCols =  Table.ToColumns( DB ),
    SumByCols = fnAddSum( ToCols ),
    Transpose= fnTranspose( SumByCols ),
    SumByRows = fnAddSum( Transpose ),
    ToTable = Table.FromRows( SumByRows ),
    DataType= fnDataType( ToTable, Number.Type )
in
    DataType

以下为从行开始转化的代码:

let
    ToRows = Table.ToRows( DB ),
    SumByRows = fnAddSum( ToRows ),
       
    Transpose= fnTranspose( SumByRows ),
        
    SumByCols = fnAddSum( Transpose ),
    ToTable = Table.FromColumns( SumByCols ),
    DataType= fnDataType( ToTable, Number.Type )
in
    DataType

4 Replies to “通过List.Transform(Many())实现行列互换”

  1. 感谢老师的精彩分享!文中“( input as table, datatype as type ) as table ⇒”可改为“( input as table ) as table ⇒”,本人的观点是否妥当,请赐教!

发表回复

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