Table.Group

官方说明:

按为每行指定的列key中的值对table的行进行分组。对于每个组,将构造一条记录,其中包含键列(及其值)以及由aggregatedColumns指定的任何聚合列。注意,如果多个键与比较器匹配,将返回不同的键。此函数无法保证返回固定的行顺序。或者,也可以指定groupKind和comparer。
Table.Group( table as table, key as any, aggregatedColumns as list, optional groupKind as nullable number, optional comparer as nullable function) as table
 

解读:

从英文字面来看,这个函数是用来对表进行分组的,即菜单栏里的"分组依据"。
函数语法:Table.Group(表,分组的依据字段,对分组的子对象进行定义,分组的模式,对分组依据字段自定义比较形式)
 
以上是我对这个函数的粗糙理解,具体说明如下:
第一参数是一张表,可以是中间的步骤转换得到或是其他函数构造而来;
第二参数的分组依据指的就是把这张表按哪些列来分类,当只是按一个列分类时,直接第二参数可以写为双引号内的文本字段名单值形式,如"班级",而要按多列分类时才用到列表形式,如{"班级","性别"}。当用空列表{}作第二参数时,代表的是整个表。
第三参数用来指定分组后的子对象计算产生的列列名称和对子对象的计算方式,如{"班级总人数",Table.RowCount},当然我们也可以对这个分组子对象进行多种计算方式,如{{"班级总人数",Table.RowCount},{"班内最高分",each List.Max([分数])}},每个列表里面定义了计算对象的列名称、计算方式,甚至还可以定义数据的类型,不同的计算方式分别用列表定义放在大的列表内。说明一下,当我的函数计算对象是当前整个对象,且不需要引入其余参数时,可以直接写函数名即可,它会默认用该函数针对对象进行计算,我称它为计算对象的隐式传递。若不是计算当前整个对象而是对象内的元素或子集时计算形式写为each fx(对象子集),还有一种情况,如一对多文本合并each Text.Combine(子对象列,"连接符"),需要引入其他参数时不能直接就写一个函数名称。当然你也可以写为(x)=>fx(x的子对象)这种形式,不要以为这里看上去没必要,在上下文转换时你可能需要这种形式。估计有人已经有点晕了,上案例:

求每个班的人数:= Table.Group(成绩表,"班级",{"班级总人数",Table.RowCount})
注意此时函数Table.RowCount计算的对象是分组后的每个子表,你也可以写为each Table.RowCount(_)
如果此时我不是求每个班级人数,而是每个班最高分就可以直接引用,此时_代表的分组后子表,用_[列名]来引用子表的某一列写为Table.Group(成绩表,"班级",{"班级最高分",each List.Max(_[分数])})。考虑上下文环境,此时就是在子表环境下_可以省略,可以直接写为each List.Max([分数])
回过头来看分组结果,我们会看到分组的依据每一个组合均是不重复的,那么是否我可以拿这个函数做类似删除重复项的效果?答案是肯定的,我们只需要写为Table.Group(表,需要删除重复项的列名,{}),当有多列组合删除重复项时,第二参数写为字段名的列表形式即可。对比Table.Distinct我们会发现这样产生的结果会干净没有其余不需要的列存在,能够较好的剔除多列组合重复值项,且为表格形式结构。
小节一下我理解的第三参数:时刻牢记此时针对的是分组后的子表进行的计算,可以直接对这个分组表进行聚合操作、或是该子表的部分结构进行引用转换运算,只要记得针对什么对象做什么转换、对应用什么类函数就好。
 
一般我们用Table.Group这个函数只启用了前三个参数,那么这后面两个参数又是什么鬼、看上去那么晦涩?看了下英文的语法参数说明,第四参数貌似说的是叫全局模式与局部模式,在我看来他应该指的是计算对象时的数据搜索机制。平时默认用前三个参数时,他其实等价于启用第四参数的全局模式下进行的计算,那么全局模式又是什么鬼?
简单来说,就是在整个表去找,会把所有找到的对象进行统一按分组依据归类;而局部模式又是怎样的呢?很多时候我会听到有人感慨M没有一个模拟工作表函数Frequency的函数,这不科学啊!确实不科学,但是今天我们暂时用用这个局部模式看看它会给我们带来什么惊喜?
我们有时候的有一种统计场景是这样的:需要统计某列每个对象连续出现的次数。
= Table.Group(表,"对象列",{"连续次数",Table.RowCount},GroupKind.Local)
其中计算模式参数也可以简写为枚举常量0或1,0代表局部模式,1代表全局模式。
从返回的结果,我们反过来推测局部模式的搜索机制吧,是不是和那个分段计频函数Frequency很像?很像但不是,他是从上至下查找,相同的分为一组,遇到不同的截断重新分组。
= Table.Group(成绩表,"班级",{"连续次数",each _},0)使用前面的成绩表数据源,我们发现了结果如下图所示的分组情况,连续相同的分到了一起。

还有个小技巧顺便提一下,当我们很多时候搞不清参数当前转换的对象时直接写个each _ 用来返回对象,然后根据这个对象,我们再进行对象的扩展或对象子集的转换操作,例如这里的each _代表的就是一个个的分组表。那么我要求分组后的表的某个聚合结果或是对这个子表插入列重组什么的调整是不是思路就清晰多了!
 
至于第五参数,我目前探究的不是很具体,可以分享哈在局部分组模式下的自定义分组依据比较情况,以下例子仅供参考:


期待有大神总结哈全局模式下的自定义分组依据情况!

12 Replies to “Table.Group”

  1. 因为没懂第五参数的用法于是去Google搜索,发现一篇讲解的文章,文章里提到了一个中文名字但是是拼音,还给了另外一个文章的链接,我点击链接,居然就是您这篇文章。这个世界真是好巧啊。Something like this was what I’ve showed *** the other day, who asked me what the 5th parameter of this function was about (or so I understood). He then sent me a link to one of his articles, which demanded a good 2 hours for me to digest and understand: We can also use custom functions to create all different sorts of grouping behaviours here.

      1. 真是太谢谢你们了,最近学到了不少,鞠躬!!!qq群的二维码我一直显示不出来,能不能麻烦您给一下qq群的号码?谢谢啦

  2. “还有一种情况,如一对多文本合并each Text.Combine(子对象列,"连接符"),需要引入其他参数时不能直接就写一个函数名称。当然你也可以写为(x)=>fx(x的子对象)这种形式”

    这句话没有理解。
    例如:
    A= Table.Group(表, {"分组的依据字段"}, {"新字段名",each List.Sort(_[字段1],each Number.Abs(_[字段1]-List.Average(_[字段2]))){0}})”

    这里面第一个each指的是每个子对象,第二个each指的是每个子对象中的字段,那么each重复使用了,这时我该怎么表示子对象的字段?

    1. 其实each ...就相当于(x)=>处理参数的裸体函数,each _ 可以写为(x)=>x
      如果你要区别前后传递的变量将x改成其他就好,如(y)=>y[y中的字段]

  3. group分组。局部分组时,第五参数,(x,y)=>,里面y值可以理解,如何处理也基本知道,但这个x,绕不清楚,它指向哪里去了?

  4. 第五参数就是把第一个值给x,把剩余值的列表都给y,然后符合(x,y)的自定义函数时,就完成当前分组,符合的值作为下一个x的值,然后进入下一次分组。以此类推

  5. 第五个参数是先将第1行作为x,第二行作为y,然后将数值带入进行判断,如果返回结果为False,那么第二行就和第1行分到一组,然后y向下移动为第三行,再将第3行的y与第1行的x值带入进行判断,如果返回结果为False,那么就继续这个过程直到某一行结果为True;如果结果为True,那么就表示要在这一行处另起1组,x变为这一行,y变为其下一行,以此类推。

发表回复

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