TOP 15 LOD expressions 案例,是 Tableau 9.0版本推出的LOD 功能经典案例,也可以作为评价 BI 工具优劣的标准之一。
【Tableau】15大详细级别表达式-思路解读版 (3) 2018/11
本文,尝试用详细级别和表逻辑,讲解 DAX 的实现,让读者更加坚信 Tableau 多么的易用!
📊 编号 | 难点 | 更新日期
| 编号 | 难点 | 更新日期 |
|---|---|---|
| LOD1-Customer Order frequency 客户频次分析 | 聚合表的二次聚合;“上下文转换” | 2024/10/06 |
| LOD2- Cohort Analysis 客户阵列分析 | 聚合表和底表的世系;“上下文转换” | 2024/10/07 |
| LOD3- Daily profit KPI 每日的利润贡献及分类汇总 | 预先聚合的二次判断 | 2024/10/13 |
| LOD15-4 Percent of total 不同层级的合计百分比 | 多个层次的预先聚合和计算优先级。 | 2024/10/26 |
⚠️ 未经授权,请勿将本文用于企业内部的培训、文档等商业活动。请各位国产 BI 同行尊重他人的劳动成果,要么付出你的金钱,要么表达你的尊重!二不相沾,非奸即盗。
LOD1 预先聚合作为维度:“不同订单频次的客户数量”
2024/10/05 调整次序,补充方法2 . 参考 https://dax.do/KSy9ReSx5rPtqm/
方法1:使用 Addcolumns 构建中间表
比如使用AddColumn函数创建一个table,如下:
这里包含了常见的字段。它会独立于原来的明细表创建一个聚合中间表,甚至可以在关系模型哪里看到。
之后在这种表的基础上,就可以创建分布了。
个人不建议使用“参数表”这样的复杂设置,参数表是“反分析”的,在需要交互的情况下才优先考虑。
方法2:使用 SummarizeColumns 创建聚合中间表
2024/10/05 更新
上面的 addcolumns 相当于构建了一个近乎物化的中间表,如果跳过这个阶段,仅仅在分析中创建临时中间表,可以如下:
而后,在 mid 聚合中间表基础上,再次使用 SummarizeColumns 完成二次聚合——以 mid 的聚合值"order_cnt"为最终问题的维度,以此前的CustomerKey 聚合成为度量。
两个数据表的结果预览
方法3:使用计算列和上下文转换
创建column(计算列),使用calculate计算订单频次,结合ALLexcept使聚合依据指定到客户ID字段,从而获得tableau中{ FIXED [客户ID]: COUNTD([ORDER ID])} 的效果。
DAX计算如下:
在视图中,分类是:客户订单频次,答案是“客户ID不同计数”,没有筛选范围。结果如下所示:
视频讲解与 DAX 代码
视频仅仅保留了 dax 的部分,完整部分查看《DAX 别裁新解》。
完整 CODE: 欢迎访问 https://dax.do/KSy9ReSx5rPtqm/
LOD2 预先聚合作为分类:“客户阵列分析 cohort analysis”
这就也需要“计算列”(calculated column)出场,计算列的性质等价于数据表字段,在数据表刷新时先于任意可视化视图而完成计算。典型的计算列字段有行级别的计算列和迭代计算列两种。
这个题目的难点是计算“每个客户的获客日期”,并与之前的数据表构建逻辑上的关系。如果单看获客日期,只需要如下即可:
先说错误的方法:世系错误导致分组失败
这里介绍一种行不通的方法,即尝试构建一个中间表,而后从底表和中间表分别获得最终需要的维度。
如下所示,构建一个中间表,获得每个客户的首次订单日期:
正确的方法1:创建一个包含所有要素的中间表
难点在于,如何把视图中所需要的所有字段(cohort_year、Order_year 以及聚合需要的 quantity——这里以 quantity 代替销售额,简化操作),都放在一个中间表中。如下所示:
以此为基础,就可以构建基于中间表的最终查询。
补充一个验证的图片:
基于上述的中间表(包含了最终需要的两个日期字段和一个聚合字段),就可以使用Summarizecolumns 完成最终的聚合了。
正确的方法2:使用逻辑字段和上下文转换
这个方法,相当于把上面临时表的字段逻辑,直接落在底表中,从而跳过了临时表阶段,所以更难以理解。
最终问题的两个年度字段都应该是明细中的 column:订单年度(order_year)、客户阵列年度(cohort_year),前者使用 year 函数计算,后者则需要 Calculate 表达式(包含聚合、指定详细级别,所谓的上下文转换)。
2.1 行级别的计算列:
为了理解方便,使用Year函数创建了 YEAR_ORDER 字段。
2.2 在明细行级别计算每个客户的阵列年度(cohort year)
特别注意,由于后续视图中会出现订单年度,它不能影响这里的MIN计算,因此使用ALL将其从external context中排除;又由于这里的min计算是以客户为依据的,但是这个context在外部不存在,因此通过allexcept将其加入计算的internal context。 这样,最终的计算,就是不受订单日期影响的、每个客户ID的首次订单日期。
(思考:这里是否可以删除 ALL(‘Global Superstore'[YEAR_ORDER]) 条件? 可以。)
2.3 基于上述两个计算列字段,完成最终计算
基于上述的 Order_year 和 Cohort_year 字段,再加上 Sales 表中已有的Quantity(这里替代销售额),就可以直接完成最终的视图聚合了。
计算结果如下:
相比于 LOD15-1,这个案例的难点是两个维度字段,都是计算而来,其中一个来自于明细,另一个来自于“预先聚合”,如何确保二者世系关系的一致性是难点。
视频讲解(DAX 部分):
视频讲解对应的 DAX 代码: 参见 https://dax.do/Fcx1S9EMmynQvN/
LOD15-3 Daily profit KPI
问题分析
How many days each month are highly profitable, profitable, or unprofitable?
标准问题结构:各年月、不同盈利状态(高盈利/盈利/非盈利)的天数?
1)维度*2:年月(计算可得)、不同盈利状态(预先聚合,作为维度)
度量*1:天数(可以直接聚合)
2)不同盈利状态:需要在指定详细级别聚合基础上判断
—— 基于 “每天的利润总和”的判断,即问题需要引用另一个详细级别的聚合计算
——只有fixed LOD能完成这个任务(引用其他层次的聚合,返回作为维度)
⚠️ 问题三个部分:分析范围、问题描述(维度)、问题答案(度量)
1、Tableau 的 LOD 方案
可以把 lod 理解为表,也可以理解为元组,甚至字段。
if { FIXED [Order Date]: SUM([Profit])} >1000
then 'highly profitable'
elseif { FIXED [Order Date]: SUM([Profit])} >0
then 'profitable'
else 'unprofitable'
END
2、DAX 方案
视频解说(简化版):
对应的代码 https://dax.do/SA9nu0P5h2AsAp/
3、SQL 方案
LOD5-4 Percent of total
问题描述:
For a given market (region), what is each country‘s percent of total global sales? 标准问题结构:在选定市场中,每个国家的销售额、在全球销售的占比?
这个题目的关键是:识别问题中存在的四个详细级别,以及它们对应计算的优先级。
四个详细级别分别是:
- L1:最高聚合度对应的详细级别,是 Percent of total 的 total,或者说百分比的“分母”部分。
- L2:Market 详细级别,仅次于最高聚合度,是筛选条件对应的级别。
- L3:Country 详细级别,是问题详细级别对应的级别,或者说主视图。
- Lx:数据表记录对应的详细级别,是所有聚合最终的起点。
优先级 的关键是:total应该在筛选之前。
4.1 Tableau 的 FIXED LOD 方案
在 Tableau 中,只有 fixed LOD 的结果在“market= EU”这样的行级别筛选器之前,指定最高聚合度对应的详细级别,可以直接用如下的表达式:
{fixed :SUM (quantity)} 或者 { sum (quantity) }
4.2 DAX 的方案
code: https://dax.do/cuvj0u2Rb70mCC/
4.2.1 DAX 方案之 Measure
如果一个 measure 想要在筛选器之前完成,需要借助于 ALL 调节符。
4.2.2 DAX 的方案之 columns
4.3 SQL 逻辑
使用子查询获得筛选之前(where 之前)的聚合。聚合的值可以在最终的查询中作为分类,也可以作为度量——看如何被引用。这一点类似于 DAX 的 column和 measure。
下面,这里子查询的结果在最终被视为分类(dimension)。
而在下面,我们使用avg 聚合避免重复,只需要 group 1和2即可。
视频省略,参考 B 站课堂。
No comments yet