跳至正文

迭代计算(中):DAX迭代计算的分类

标签:

by 喜乐君 (敏捷BI)

在学习Power BI的DAX过程中,会发现很多常见于编程语言的概念,比如迭代iteration、赋值(var)、定义DEFINE等,这也是它不同于Tableau、SQL的关键特征——SQL中没有赋值和循环的处理,Tableau计算虽有参数但无显性的循环方法,Power BI既有参数(以数据表的形式出现),也有多种间接实现循环的方法(比如EALIER函数,虽然并不容易,但至少可操作)。

“SQL中没有赋值或者循环的处理,数据也不以记录为单位进行处理,而以集合为单位进行处理。SQL和关系数据库的思维方式更像是一种整体论的思维方式。”——SQL进阶教程

要完全理解DAX语法,理解DAX相对于SQL/Tableau在计算上的关键差异,就先要理解一些关键概念:迭代iterate、循环loop、遍历(traversal)、递归(recursion)等。它们既有差异,又相互联系,是计算背后接近算法的部分,是辅助理解复杂问题解决方案的关键概念。

本文的重点是迭代(iteration),这也是DAX“计算列”的关键。

先从最简单的计数来理解迭代(iteration)的过程,之后介绍迭代的分类及其组合形式,这是理解各种迭代器(iterator)函数的基础,比如SUM、SUMX和FILTER。

  • 迭代函数必然包含两个参数:数据表、表达式——即扫描哪个数据表、执行什么计算
  • DAX中,SUMX和FILTER是典型的迭代函数,前者聚合数据,后者过滤数据

一、迭代iteration过程及其分类

何为迭代(iteration)?迭代既是程序设计中的关键概念,指程序设计中的重复计算,也被引用到社会科学领域,比如说管理理念或课程体系“迭代升级”。理解迭代的关键是重复,这种重复又不同于简单的循环,而是前后依赖的重复,直至最后一个值;同时,每次迭代,都要执行相同的程序计算。

在Power BI中,DAX作为近似编程语言的分析工具、函数式分析语言,也引入了程序设计的“迭代”概念,指按照指定顺序、逐个访问数据集中的每一项。分析中的“指定顺序”,默认为线性次序。

理解迭代的关键是“逐个访问“(one by one),也可以理解为重复(repetition)。

Iteration: repetition of a mathematical or computational procedure

这里以简单迭代(计数)辅助帮助理解迭代iteration的过程;而后介绍两类“迭代器”(iterator)和函数。

1、计数:自上而下、输出唯一值的业务分析

迭代其实就在我们身边,体育课出勤计数、老师收作业、定投基金,这些场景中都有迭代的影子。

举个形象的例子,体育课时很多人人站为一个队列,老师想要知道有多少人来上课了,就会让大家从头到尾、依次报数。报数自动止于最后一人,最后一人的报数,既是他在队列中的标记,也是队列的总人数。分析中,把这个过程称之为计数(COUNT)。

计数,可以说是最简单的迭代。计数迭代的关键是访问整个队列,队列中的元素依次+1。

我们既可以可视化地把多个人想象成为一个队列,也可以用数学的方法记作一个数据集。不管外在的展现形式如何,想要知道有多少人(多少个元素),必须依次、逐个标记数字,这个过程就是迭代数据集。

添加图片注释,不超过 140 字(可选)

对数据库中特定数据表的计数也是同理。区别在于,常见数据表是多行多列构成的关系结构(relational schema)。数据表的每一行是一个元组(tuple),元组中可以包含不同类型的多个数据值,比如(王五,10,0.4),换个方式理解,元组就是行(row)、记录(record)。多个行构成了数据表。

对数据表的计数计算,就是计算有多少个元组。参考之前的逻辑,相当于逐行扫描数据表中的每一行(scan table row by row),然后标记1、2、3……的过程,扫描到最后一行对应的数字,就是数据表的行数。

数据表中的计数

产品经理、产品工程师把计数计算整合、封装为COUNTX函数,分析师就可以直接引用了。

比如计算客户表中客户数量:

COUNT ( Customer[Customer Name] )
COUNTX ( Customer, Customer[Customer Name] )
COUNTROWS('table name')

[勘误]由于COUNTROWS是在视图中的聚合函数,而非迭代函数,因此更正为COUNTX。

在分析中,还有很多与之类似的迭代函数,典型的有SUMX求和、AVERAGEX算术平均、MAXX最大值。站在迭代的角度看,这些函数就是生成迭代的“迭代器”(iterators);而从明细到抽象的计算角度看,这些函数都是由多变少的“聚合函数“(aggregators)。

迭代函数中的表达式可以是多个字段构成的计算,比如先逐行计算每一行的数量*单价,再跨行聚合。

Sales[Sales Amount]=        SUMX(Sales,Sales[Quantity]*Sales[Net Price])

从分析的角度看,可聚合的迭代函数,又称之为分析函数。

2.日期计算:自左到右、输出数据表的数据准备

还有一种迭代计算,迭代过程也是自上而下、row by row,只是计算过程是自左到右,每一行的输出各不相同、相互独立。

比如,在数据表明细行,计算每一行订单日期对应的订单年度,在Excel或者Power BI中,都可以用YEAR函数计算而来。相比前面的计数函数最终只返回一个值,这里的YEAR则会返回很多值(理论上每一行都可以截然不同),单看输出结果,可以视为是一列多行的数据表。

添加图片注释,不超过 140 字(可选)

在数据分析过程中,几乎所有的数据准备工作,都是类似的计算逻辑。比如销售单价*销售数量、销售额*折扣率、发货日期-订单日期(发货间隔)、从产品名称中拆分「品牌」字段等等。

这些计算的共同特征是:(1)在数据表明细行中完成,每一行的计算完全独立、互不影响;(2)返回的结果是数据表,即多值。

从分析的角度看,此类计算可以称之为数据准备类计算(Data Preparing Calculations);与之相对的则是则是以计数、求和为代表的分析型计算(Analytical Calculations)。这不仅仅是迭代计算的分类方式,也是函数的分类方式,更是计算的分类方式。

此为业务视角。

3、Python中的迭代:聚合和逐行输出

在Python或者其他编程语言中,迭代属于常见功能,比如for、while都可以实现迭代。

使用for可以逐行打印一个列表(list)中的所有成员,这就是最简单的迭代输出。比如

for value in  ts_code:
     print(value)

如果翻译一下,上述过程就是:对于ts_code中的每个值,执行打印print输出。

我在查询公募基金数据时,常常使用这样的方法。如下的for循环,就会把ts-code中每一个数据值依次打印一遍,逐个访问,如果已经在数据库中存在,那么就会跳过,否则就会执行特定命令。

添加图片注释,不超过 140 字(可选)

上述的Print输出是列表中每个元素逐一输出(one by one),也可以在这个过程中,增加聚合计算过程。比如计算列表中总共又多少个数据表(类似于分析中的COUNT函数)。 为此,只需要在for的迭代过程中增加一个累加计数器就好,如下所示:

#  iteration 
j=0 
for i in range [list]:
     print(i)
     j=j+1
     print(j)

在这里,提前定义了一个变量j,它会随着队列的逐行输出加1(j=j+1),最后输出的数字,就是整个队列的数量,即计数。而每一个元素都会输出一个值,可以视为是元素的编码;和体育课依次报数一样。

当然,和分析工具中一样,Python的工程师把计数这种简单、高频应用,封装为特定的功能,于是就有了shape,len等函数,以及pandas中的agg函数。

import numpy as np 
Df.groupby("species").size() 
Df.groupby("species").agg(["max",range_flowers])

可见,在Python中,迭代过程也可以分为逐一输出、逐一相加两种基本样式。迭代过程完全相同,差别只在于计算逻辑。

4、小结:迭代计算的两种类型

从上述的多个场景中,我们可以做出如下的总结:

  • 迭代函数的共性是逐行访问数据表(即多个元组)的迭代过程(iterate the table row by row);
  • 迭代的列表或数据表是迭代对象,这是迭代计算的基础;
  • 迭代对象的迭代次序,可以默认理解为都是线性的;在计算引擎中,也可能会有优化方案,比如排序时的二叉树方法,这些不属于应用层面控制的内容,可以忽略。
  • 迭代函数的差异,体现在与迭代过程相结合的计算和返回值上:
  • 部分迭代计算只返回一个唯一值,称之为标量值(scalar value),比如COUNT计数函数、Python的len函数等
  • 有些则是逐行计算、按条件输出,返回很多值构成数据表(table),比如Python中的Print输出,或者DAX的filter函数

为了方便理解不同迭代器的差异,可以把迭代函数、迭代器拆分为迭代对象、迭代计算两个部分。二者紧密结合,又相互独立,多个迭代函数的迭代对象可以是完全相同的,但区别在于计算不同、输出不同。

  • 迭代的对象可以是列表,也可以是数据表。而迭代次序默认是线性地逐个访问,对于列表LIST而言就是“逐一”one by one,对于数据表TABLE而言,就是“逐行”row by row 。直至迭代完整个迭代对象。
  • 迭代计算,就是在每个迭代元素处、执行的计算过程,可以形象的理解为“自上而下”和“自左到右”两种计算过程。它们对应两种迭代类型,如下所示:
添加图片注释,不超过 140 字(可选)

在Python中,典型的两类迭代函数是Len长度计算和Print打印,前者返回单一值,后者返回多个值。

在DAX中,典型的两类迭代函数是SUMX迭代求和,和FILTER迭代过滤函数,前者返回单一值,后者返回数据表。前者的重点是聚合,后者的终点是过滤;前者是结构上的变化,后者是数量上的变化。

💡聚合一定包含迭代过程,但迭代不一定是聚合计算。

POWER BI中,FILTER可以理解为for或者while判断,它扫描数据表、逐行判断筛选条件(filter condition),然后返回符合条件的数据表明细行,因此结果还是一个数据表。这里可以用列表来表示:

#  iteration 
for i in range(10): 
# print(i)
     if i > 5:
     print(i)

V1 Jan 9, 2023 喜乐君
V2 Jan 14, 2023 增加排序等
V3 Jan 18, 2023
V4 Jan 30, 2023 修改第一部分,发布知乎
V4.2 Mar 7, 2023 调整部分内容——仅限新博客


了解 喜乐君 的更多信息

订阅后即可通过电子邮件收到最新文章。

了解 喜乐君 的更多信息

立即订阅以继续阅读并访问完整档案。

Continue reading