【需求】
这是一个小例子,需求很简单:
1.统计截止目前,所有我负责的班级数量和学员数量;
2.统计所有已经结束的班级的数量,以及应该结束但是尚未结束的班级数量;
3.统计本月开班的班级数量,并列出明细;
4.统计应在本月结束的班级数量;
5.两周汇报一次以上数据。
【思路】
因为每两周就要汇报一次统计结果。因此我希望实现自动化——我只需要导出原始数据,然后打开报告,刷新,就获得最新结果。为了实现这个目标,我需要同时用到PowerQuery和PowerPivot。
PowerQuery的作用是引入数据源,并获取本月开班明细;PowerPivot的作用是借助DAX来计算并得出其他数据。
【步骤】
1.建立单独的项目文件夹
原始信息是从系统导出的数据,不作任何更改,也不重命名。如果没有单独的项目文件夹,由于系统导出的数据命名都是一样的,所以很容易就和其他导出的数据文件混淆了,除非我重命名——作为懒人来讲,重命名也很讨嫌啊。
因此我需要单独为此工作建立一个项目,我用文件夹的方式来统一管理原始数据和统计结果报表。
为什么要单独建立一个“原始信息”文件夹呢?这是处女座强迫症——原始数据和结果数据混在一起总是让人纠结,是不?
2.导出原始数据,放入项目文件夹的子文件夹“原始信息”文件夹中,不作任何改动,也不重命名。系统中导出来的是什么样,就是什么样。
3.新建一个Excel文件,重命名为“班级统计结果”。
4.使用PowerQuery的参数表格建立参数表格及其函数。
如果只是我一个人用,其实参数表格没必要。但是我有个野心——想让其他同事拷贝我这个工程文件夹到ta电脑后,ta不需要做任何修改就可以直接使用模板。
参数表格建立方法见《PowerQuery的参数表格用法》。参数表格只有一个参数,其值是一个公式:
=LEFT(CELL("filename",A1),FIND("[",CELL("filename",A1),1)-1)&"原始信息\"
这个公式的目的就是为了让此工程文件能在别的电脑上无障碍使用,不用修改数据源的路径。
5.用PowerQuery对数据源进行引入和整理,代码如下:
let
源 = Folder.Files(fnGetParameter("班级汇总报表")),
删除的其他列 = Table.SelectColumns(源,{"Content"}),
合并的二进制 = Binary.Combine(删除的其他列[Content]),
#"导入的 CSV" = Csv.Document(合并的二进制,[Delimiter=",", Columns=14, Encoding=936, QuoteStyle=QuoteStyle.None]),
提升的标题 = Table.PromoteHeaders(#"导入的 CSV"),
更改的类型 = Table.TransformColumnTypes(提升的标题,{{"序号", Int64.Type}, {"班级名称", type text}, {"起止日期", type text}, {"所属域", type text}, {"承办单位", type text}, {"班级管理员", type text}, {"应到人数", Int64.Type}, {"实到人数", Int64.Type}, {"参加率", type text}, {"班级状态", type text}, {"整体满意度", Int64.Type}, {"预算费用", Int64.Type}, {"实际费用", Int64.Type}, {"", type text}}),
用分隔符分列 = Table.SplitColumn(更改的类型,"起止日期",Splitter.SplitTextByDelimiter("至", QuoteStyle.Csv),{"起止日期.1", "起止日期.2"}),
更改的类型1 = Table.TransformColumnTypes(用分隔符分列,{{"起止日期.1", type date}, {"起止日期.2", type date}}),
筛选的行 = Table.SelectRows(更改的类型1, each not Text.Contains([班级名称], "提报")),
重排序的列 = Table.ReorderColumns(筛选的行,{"序号", "班级名称", "实到人数", "起止日期.1", "起止日期.2", "所属域", "承办单位", "班级管理员", "应到人数", "参加率", "班级状态", "整体满意度", "预算费用", "实际费用", ""}),
删除的其他列1 = Table.SelectColumns(重排序的列,{"班级名称", "班级状态","实到人数", "起止日期.1", "起止日期.2"}),
重命名的列 = Table.RenameColumns(删除的其他列1,{{"起止日期.1", "线上学习开始日期"}, {"起止日期.2", "线上学习结束日期"}})
in
重命名的列
忽略多余的字段和代码格式吧。我也没有对每一个步骤进行有意义的命名。
注意“源=……”这一行,使用了参数表格函数引入数据源路径。
注意“用分隔符分列”这个步骤,这是因为原始数据的班级开班和结束日期是在同一个字段里,因此需要将其拆开为“开始日期”和“结束日期”两个字段。
6.将PowerQuery查询结果加载到数据模型,仅创建链接。
7.获取本月开班明细。
这里我是直接引用了前面创建好的查询,然后用公式筛选,代码如下:
let
源 = 原始信息,
筛选的行 = Table.SelectRows(源, each Date.IsInCurrentMonth([线上学习开始日期]))
in
筛选的行
这里也可以用PowerQuery的Evaluate来创建查询,但是比较麻烦,还不如直接引用查询然后筛选。
将其加载到表,并重命名为“本月明细”。
注意用Date.IsInCurrentMonth()的目的也是为了偷懒,要不然可以直接筛选具体的月份来实现——但那样太麻烦,不是?
8.进入PowerPivot,创建需要的度量值:
总班级数=COUNTROWS('原始信息')
总参加学员数=SUM('原始信息'[实到人数])
已结束的班级=CALCULATE(COUNTROWS('原始信息'),FILTER('原始信息','原始信息'[班级状态]="已结束"))
本月=MONTH(NOW())
本月开始线上学习的班级数=CALCULATE(COUNTROWS('原始信息'),FILTER('原始信息',MONTH('原始信息'[线上学习开始日期])=[本月]))
本月参加学员数=CALCULATE(SUM('原始信息'[实到人数]),FILTER('原始信息',MONTH('原始信息'[线上学习开始日期])=[本月]))
本月应结束班级数=CALCULATE(COUNTROWS('原始信息'),FILTER('原始信息',MONTH('原始信息'[线上学习结束日期])=[本月]))
上月应结束班级数=CALCULATE(COUNTROWS('原始信息'),FILTER('原始信息',MONTH('原始信息'[线上学习结束日期])=[本月]-1))
9创建基于数据模型的数据透视表,将需要的字段拖入数据透视表,最后结果如下:
很简陋的一个表,因为是内部使用,我就偷懒了。
公式和统计结果中,我塞入了一个“上月应结束班级数”,这个是个bonus,可以无视。
【共享方法】
将工程文件夹全部拷给需要的人,然后ta导出自己需要的班级数据,放到“原始信息”文件夹,打开统计结果文件,刷新,即可获取最新结果。
有个前提:最好是Excel 2016。如果是Excel 2013之类的老版本,确保PowerQuery和PowerPivot插件启用。
【反思】
其实最后建立度量值时,我可以不必那么麻烦,利用好切片器和筛选,可以获取到更动态的统计结果。但是考虑到不见得使用此报表的人员会使用这些复杂的数据透视表功能,所以我直接把对方想要的数据写死呈现出来。