跳至正文

【DAX 别裁新解】低阶和高阶“表操作“:理解分析聚合和投影、筛选的关键差异

标签:

注:本文是《DAX 别裁新解》小册子的讲义内容。视频参见: DAX别裁新解:通识框架解读 V3 (图文+视频)

为了帮助读者理解,这里从大家熟悉的一些场景说起。

低阶和高级表操作

在魔法的世界中,用“大扫把”变出来一只“小拖把”只能是低级的把戏,而用扫把飞上了教堂就非常高级;在文学的世界中,用几千个常用字写一篇日记是小学生的题目,而用它来证明“哥德巴赫猜想”就很高级;在《黑客帝国》中,理解“现实世界很真实”很容易,但体会“看似真实的现实世界只是抽象机器世界的虚拟和感觉”就很高级。

古人说,“衣食足而知荣辱”,这年头“衣食足”相对容易,但要让大家“知荣辱”却非常困难。

也常常有人问我说,“喜老师,你叫什么名字”,这个问题简单(如同从档案中查找想要的值);但要问我“吴老师,你去年去过多少个城市”,这个问题就很高级,因为我无法直接“找到答案”,而要先从差旅记录中找到“去年的明细行”,再“计数聚合”才能回答——代表答案的聚合值(比如2024年去过20个城市,这里的20)在原始数据中没有。

如果要用一个统一的尺度来描述,我们说具有可比性的多个实体或行为在“抽象程度”上是有高低差异的。正如老子在《道德经》中所言“有无相生、难易相成、长短相形、高下相倾、音声相和、前后相随”,数据查询和分析因聚合分难易,计算因其抽象程度不同而有高下。

图:抽象程度是我们比较“具有可比性”的事物或行为的通用尺度

具体而言,在数据世界中,从一个数据表中筛选行或者限制列获得一个“子集”是毫无难度的低阶操作,而分组聚合获得了原来数据表中没有的“度量”就很高级,反映了问题思考和抽象计算的水平。

正因为此,学习 SQL 数据查询时,掌握 SELECT FROM和 WHERE总是比SUM+Group By 组合要容易理解的多。笔者为此按照抽象程度将 SQL 的主要子句排列如下:

  • 低阶抽象:SELECT a, b, c from  table
  • 低阶抽象+1:SELECT a, b, c from  table WHERE a=”A”
  • 高阶抽象:SELECT a, b,  SUM(x), COUNT( y )  from  table  GROUP BY a,b  
  • 高阶抽象+1: SELECT a, b,  SUM(x)  from  table  GROUP BY a,b Having  SUM(x)>0……

在第一章中,笔者讲“分析的本质是抽象,抽象的实现形式是聚合”,并介绍了Summarize分组表达式和SummarizeColumns分组聚合表达式;正是建立在上述的逻辑背景之上的。

3.1.1  低阶计算的常见类型与 EXCEL/SQL/DAX 示例

数据准备对应的“低阶抽象”,正是和问题代表的“高阶抽象”相对而生的。从计算的角度看,“数据准备”性质的表操作可以分为两大类:

  • 从已有数据表中获得少量数据获得“子集”的过程,可称之为“限制”(restrict)
  • 在已有数据表基础上,增加新的数据行或者字段列,可称之为“扩展”(expand)

在实践中,上述的“限制”和“扩展”又可以进一步展开。“限制”的典型形式是“投影”(project)和“筛选”(filter);而“扩展”的典型形式是扩展行和扩展列。如下图所示:

图:四种类型

在 SQL、DAX 等面向数据的语言中,都有与之相对应的语句或表达式可供快速实现。

 ExcelSQLDAX
投影手动隐藏不需要的列SELECT a, b, c FROM table SelectColumns
筛选明细行使用筛选条件SELECT * FROM table
WHERE a>0
FILTER
扩展行Ctrl+C 与 Ctrl +V粘贴向下合并UNIOIN  / UNION ALLUNION
扩展列Vlookup 多表查找匹配SELECT *
FROM table  t
JOIN table_2 t2 on   t.fk = t2.pk
关系模型

3.1.2  如何识别不同抽象程度的计算阶段和位置

在 DAX 中,不同的表达式代表不同的阶段,对应不同的抽象水平。作为一个整体出现时,不同抽象水平的计算、表达式就会程序特定的逻辑次序,“低阶”的计算是“高阶”计算的准备,为此,就有了两个清晰的阶段、层级,如下图所示。

低阶阶段就像一个人从幼儿园、小学、初中,再到高中的过程,这是人生训练的准备,是“初阶的成长阶段”,高阶阶段是从大学的自律、专业探究到“职场大学”的成长过程,是人生的跨越。

高下相倾、音声相和,此之谓也。

如何像老子一样简化对上述复杂逻辑的理解呢?最简单的方式是“名”,即创造相对的概念代表不同的阶段、层次。就像我们每个人都有自己的“名字”,还有各种“仁义道德”一样,而不管你面对的领导、敌人内心是多么的“复杂”,“名”简化了体系,但没有替代复杂性。

在 DAX 官方和主流体系中,为上述的两个阶段、层次取了名字:Row Context 和 Filter Context。并出现 一句“口头禅”来理解它们彼此的关键差异:

Row context iterates, while filter contexts filters.
“行上下文迭代、筛选上下文筛选”

如果初学者没有理解它的意思,这正说明“名并没有简化复杂性”,只是更诠释而已。当然,有时候“名”也会让理解误入歧途,比如上面出现的新概念“iterate”(迭代)。

这里的 Context 可以翻译为“背景、环境、上下文”,表示计算有效性的依据、条件和相对阶段。比如 YEAR ([order_date]) 计算是相对于数据表明细行而有效的,一行又一行、逐一计算而完成,相当于增加了一个辅助列(扩展列);而作为问题答案的 SUM([quantity])是相对于问题阶段而有意义的,它以问题分类字段为依据,分组、聚合,一组又一组、分别求和回答问题答案。

因此,笔者更倾向于把所谓的 Row context 和 Filter context 称之为两个级别,每一个计算,不是在“数据表行级别”执行的计算,就是从“数据表”阶段跨越到更抽象、更高级的“问题详细级别”的计算。两个级别是不同阶段计算的依据,是计算有效性的环境、背景、上下文。

具体到表达来看,DAX 的关键可以分为两组:

  • 数据表行级别计算表达式:投影 SelectColumns、筛选 Filter、扩展 AddColumns
  • 问题级别计算表达式:汇总 Summarize、分组聚合 SummarizeColumns

Summarize 和 SummarizeColumns 表达式在计算过程中获得了更抽象的结果表(或者叫聚合表),其抽象程度要高于计算依赖的原始明细表。如下所示,当从 Constos 销售明细表中计算而来“各个产品的销量总和”时,可以用“Product Key”代表结果表的详细程度,简称“问题详细级别”。问题详细级别由问题中的单个或多个分组字段构成。

相比之下,当从 Constos 中筛选明细时,“筛选结果表”的详细级别与原始表保持一致,只有数据量级的变化,而无抽象水平和“层次”的改变,故称“低阶”。低阶计算的基本特征是计算在数据表明细行的每一行中依次执行,故又称“行级别计算”。

行级别计算以“行”为有效性计算的单位,而问题级别计算以“分组”为有效性计算的单位。前者是量级的变化,后者则是层级的变化,故称“低阶”和“高级”。

换个角度,以问题为分界线,本书把投影、筛选和扩展列表达式称之为低阶的、数据明细表行级别的表操作类型,而把“汇总、分组聚合和(扩展)度量表达式”视为高阶的、问题详细级别的表操作类型。这是全书的关键之一,是理解第四章“上下文转换”的基础。