这个问题在三两年前就2022年4月,2022年11月分别困扰过我。当时解决了,然后忘到脑后。后来又遇到同一次问题,虽然后来也解决了,然而解决之前又一次走上同一条弯路,这感觉令人不悦。
最近2023年7月,又一次遇到同一类型的问题。探索的顺序和以前又完全相同。我不禁想起我的本科导师李树杰老师教导过我的,“杨儿啊,你得做笔记啊。”我当时说什么来着,“那些失败的过程有啥好记的。”
得正经记录一下了,不然还会第四次错。贴出来是最好的记录,因此有了这个帖子。
先从问题的背景和故障现象说起。

1. 问题
如下表所示,是学生对教师授课效果的评价。例如,阿大同学认为,教师第一章讲得优,第二章讲得良,第三章讲得中。依此类推。

要求是,统计 每一章 分别 有多少优、多少良、多少中、多少差。即,想得到下面的表格。

按上表所示,我们可以看出,纵向看例如第一章;有7人优,2人良,1人中,共10人;横向看例如优的人数,第一章7,第二章4,第三章2,共13人。
另一个类似问题的场景,订餐-派送。如下表所示,我们希望按宿舍得到一张表,按宿舍-楼层再得到一张表。每张表都按 早餐每种菜品多少份、午餐每种菜品多少份、晚餐每种菜品多少份 计数。

需求/字段尚不稳定,可能多次随时变更。每次变更后都要求 快!
2. 为什么用透视表不行
统计数据最快的不是R或者python,更不是matlab。而是excel。这里的快不是指执行速度,而是指开发。响应快速,培训周期短,用户接受迅速,部署环境简单(经常已经安装过)。
Excel里看起来最像解决方案的是功能强大的 透视表。
然而不行。
关于透视表的使用,值得另用一篇博客才能说清楚。在此暂且略过,假设你已经会了。我尝试了每个字段在行或者在行,尝试了各种计数值的方法,都得不到我想要的结果。各种尝试的结果都非常乱,在此不贴图了。
我想要的结果是,再贴一遍,如下。

中间是计数值;行,是优良中差;列是……每列分别统计。
如果每列分别统计,但是不要求放在一张表中,透视表是可以胜任的。如下。

我们可以看出,这三张表合在一起,就是我希望的结果。
严格地说,这三张表(每张表取1列,横向拼接在一起;
并且行标签都使用 优、良、中、差这4个条件;
并且计数值都使用 优、良、中、差这4种情况对应的计数)合在一起,就是我希望的结果。
“合在一起”符合这么复杂的定义才行。但是透视表的 多列 并不是这个意思。
透视表的 多列,是指 每一列作为一个条件,这些条件以 与操作 复合。例如

以上数据得表下表。

使用透视表的前提条件是 只有1列是数据(计数项,或者求得项等),基余的列都是条件。而我在这里遇到的两张表,学生评教师的 和 订餐-派送 的,其中的 每章 或者 每餐,都不是条件,而是多列数据。
如果从SQL的角度考虑,那么相当于 group by 语法。Group by后面如果有多个列/字段,列/字段之间用逗号分隔,其含义是 既符合又符合,例如 既是东北地区,又是吉林,可爱度10;东北地区所有省份可爱度之和 30。
另一个可供参照的技术是 平行数组。两个数组是独立的,一个数组是语文成绩,另一个数组是数学成绩。两个数组都使用学号作为下标,因此发生关联。两个数组都是计数或求和项,下标(对应excel中的左侧那些列)是条件。
3. 解决方案0 归约
有个近乎耍赖的方案。这个故事告诉我们:1.很多巧妙的方案都近乎耍赖,然而有效;2.巧妙不得长久和通用。
既然我们要求的是计数,那么如果正确赋值,我们可以只计数,而不分类、统计、汇总。因此也不需要透视表。

这个问题,我们把 优、良、中、差分别赋值为10、8、6、4。在这里,我们假装这就是教务部门要求的赋值权重,或者教师不会得到比4分还低的评价。事实上的要求共5档,然而4档还是5档并不影响我们的技术讨论,因此忽略这一区别。Ctrl-h,给出对应赋值,全部替换。

得到下表。

求平均值。

得到下面的表格。

可以看到,第一章教学效果最后,第二章稍差,第三章快放弃了。
虽然以上数据已经符合教务部门的期待了,但是在以上归约过程中,我们舍弃了优、良、中、差每个档次的数量,因此丢失了一些细节。
在 订餐-派送 问题中,按类别计数这一节细不能丢弃,如何赋值呢?

令几种类别 正交,使用多项式。用每“位”数字对一种类别计数。
特色1 |
1 |
特色2 |
10 |
特色3 |
100 |
基本 |
1000 |
全素 |
10000 |
得到下面的表格,计数。

早餐这一列3205的意思是 3份基本,2份特色3,0份特色2,5份特色1。按宿舍和楼层求和,可以得到 地点-类别 派送数量。

春华,2份基本,3份特色1。
如果计数超过10种,那么可以使用为每种类别预留多位数字。
4. 解决方案1 按列分别统计后合并
还可以把多列数据拆开,对每列数据单独统计。
4.1 透视表
对每一章分别用透视表统计。

手动排序,得到下表。

把行和列改为第二章。

得到第二章的统计结果。

用同样方法得到第三章的统计结果。把三章数据横向拼接,得到下面的表格。

4.2 Access - group by
把Excel粘贴或导入到Access。

新建查询,SQL视图如下。

执行得到结果,尚未根据优良中差排序。

然后对F2排序,横向合并,略。
之所以使用Access,是因为拆分单列和合并结果比Excel更方便。
4.3 Excel - countif函数
这种方法学自 OLIVER_QIN cnblogs 奔跑的金鱼。
https://www.cnblogs.com/OliverQin/p/9436814.html
在下图中绿色的单元格中使用函数 countif。第一个参数是用于统计的区域,这里对应第一章。第二个参数是判断的依据,左侧的优良中差是手动键入的。“良”是红色,因为当前统计的是良的数量,引用“良”。

注意需要用形如$B$2的绝对引用,这样在向其他单元格复制时引用的位置不会变动。类似的,第二章和第三章如下图。

5. 解决方案2 转变为一维数据-透视表
把列标题作为变量名,改为一维数据。具体操作步骤如下。
第一步,把表格复制三份,纵向排列。如下表所示。

第二步,删除除了第一个以外的标题行。

第三步,增加一列,章节。按下图填写 第一章、第二章、第三章。

第四步,删除并非本章的成绩。

第四步,把第一章、第二章、第三章3列合并为一列。

以上,完成了把列标题作为变量名,改为一维数据。这样就符合透视表的要求,只有一列,即最右一列 作为数据的计数项,其余的左边两列都是条件。
对上面“一维”表格使用透视表。

手动对行和列排序,得到下面的表格。

6. 解决方案3 透视表-多重计算合并计算数据区域
以下方案最为优雅来自,OLIVER_QIN cnblogs 奔跑的金鱼
https://www.cnblogs.com/OliverQin/p/9436814.html。
快捷键Alt + D P,调出 数据透视表和数据透视图向导,选择点选按钮 多重合并计算数据区域。

共3步,所以第2步拆成了2a和2b!此处有手动狗头表情。

选定区域,然后点击 添加 按钮。

注意,此时得到的透视表不是我们想要的,如下表。

当前的行、列、值是这样的。

修改为下图所示,关键点是行由“行”改为“值”,即由按行(姓名)改为按值(优、良、中、差)分类。

多列数据的分类统计
这个问题在三两年前就2022年4月,2022年11月分别困扰过我。当时解决了,然后忘到脑后。后来又遇到同一次问题,虽然后来也解决了,然而解决之前又一次走上同一条弯路,这感觉令人不悦。
最近2023年7月,又一次遇到同一类型的问题。探索的顺序和以前又完全相同。我不禁想起我的本科导师李树杰老师教导过我的,“杨儿啊,你得做笔记啊。”我当时说什么来着,“那些失败的过程有啥好记的。”
得正经记录一下了,不然还会第四次错。贴出来是最好的记录,因此有了这个帖子。
先从问题的背景和故障现象说起。
问题
如下表所示,是学生对教师授课效果的评价。例如,阿大同学认为,教师第一章讲得优,第二章讲得良,第三章讲得中。依此类推。

要求是,统计 每一章 分别 有多少优、多少良、多少中、多少差。即,想得到下面的表格。

按上表所示,我们可以看出,纵向看例如第一章;有7人优,2人良,1人中,共10人;横向看例如优的人数,第一章7,第二章4,第三章2,共13人。
另一个类似问题的场景,订餐-派送。如下表所示,我们希望按宿舍得到一张表,按宿舍-楼层再得到一张表。每张表都按 早餐每种菜品多少份、午餐每种菜品多少份、晚餐每种菜品多少份 计数。

需求/字段尚不稳定,可能多次随时变更。每次变更后都要求 快!
为什么用透视表不行
统计数据最快的不是R或者python,更不是matlab。而是excel。这里的快不是指执行速度,而是指开发。响应快速,培训周期短,用户接受迅速,部署环境简单(经常已经安装过)。
Excel里看起来最像解决方案的是功能强大的 透视表。
然而不行。
关于透视表的使用,值得另用一篇博客才能说清楚。在此暂且略过,假设你已经会了。我尝试了每个字段在行或者在行,尝试了各种计数值的方法,都得不到我想要的结果。各种尝试的结果都非常乱,在此不贴图了。
我想要的结果是,再贴一遍,如下。

中间是计数值;行,是优良中差;列是……每列分别统计。
如果每列分别统计,但是不要求放在一张表中,透视表是可以胜任的。如下。

我们可以看出,这三张表合在一起,就是我希望的结果。
严格地说,这三张表(每张表取1列,横向拼接在一起;
并且行标签都使用 优、良、中、差这4个条件;
并且计数值都使用 优、良、中、差这4种情况对应的计数)合在一起,就是我希望的结果。
“合在一起”符合这么复杂的定义才行。但是透视表的 多列 并不是这个意思。
透视表的 多列,是指 每一列作为一个条件,这些条件以 与操作 复合。例如

以上数据得表下表。

使用透视表的前提条件是 只有1列是数据(计数项,或者求得项等),基余的列都是条件。而我在这里遇到的两张表,学生评教师的 和 订餐-派送 的,其中的 每章 或者 每餐,都不是条件,而是多列数据。
如果从SQL的角度考虑,那么相当于 group by 语法。Group by后面如果有多个列/字段,列/字段之间用逗号分隔,其含义是 既符合又符合,例如 既是东北地区,又是吉林,可爱度10;东北地区所有省份可爱度之和 30。
另一个可供参照的技术是 平行数组。两个数组是独立的,一个数组是语文成绩,另一个数组是数学成绩。两个数组都使用学号作为下标,因此发生关联。两个数组都是计数或求和项,下标(对应excel中的左侧那些列)是条件。
解决方案0 归约
有个近乎耍赖的方案。这个故事告诉我们:1.很多巧妙的方案都近乎耍赖,然而有效;2.巧妙不得长久和通用。
既然我们要求的是计数,那么如果正确赋值,我们可以只计数,而不分类、统计、汇总。因此也不需要透视表。

这个问题,我们把 优、良、中、差分别赋值为10、8、6、4。在这里,我们假装这就是教务部门要求的赋值权重,或者教师不会得到比4分还低的评价。事实上的要求共5档,然而4档还是5档并不影响我们的技术讨论,因此忽略这一区别。Ctrl-h,给出对应赋值,全部替换。

得到下表。

求平均值。

得到下面的表格。

可以看到,第一章教学效果最后,第二章稍差,第三章快放弃了。
虽然以上数据已经符合教务部门的期待了,但是在以上归约过程中,我们舍弃了优、良、中、差每个档次的数量,因此丢失了一些细节。
在 订餐-派送 问题中,按类别计数这一节细不能丢弃,如何赋值呢?

令几种类别 正交,使用多项式。用每“位”数字对一种类别计数。
特色1 |
1 |
特色2 |
10 |
特色3 |
100 |
基本 |
1000 |
全素 |
10000 |
得到下面的表格,计数。

早餐这一列3205的意思是 3份基本,2份特色3,0份特色2,5份特色1。按宿舍和楼层求和,可以得到 地点-类别 派送数量。

春华,2份基本,3份特色1。
如果计数超过10种,那么可以使用为每种类别预留多位数字。
解决方案1按列分别统计后合并
还可以把多列数据拆开,对每列数据单独统计。
4.1 透视表
对每一章分别用透视表统计。

手动排序,得到下表。

把行和列改为第二章。

得到第二章的统计结果。

用同样方法得到第三章的统计结果。把三章数据横向拼接,得到下面的表格。

4.2 Access - group by
把Excel粘贴或导入到Access。

新建查询,SQL视图如下。

执行得到结果,尚未根据优良中差排序。

然后对F2排序,横向合并,略。
之所以使用Access,是因为拆分单列和合并结果比Excel更方便。
4.3 Excel - countif函数
这种方法学自 OLIVER_QIN cnblogs 奔跑的金鱼。
https://www.cnblogs.com/OliverQin/p/9436814.html
在下图中绿色的单元格中使用函数 countif。第一个参数是用于统计的区域,这里对应第一章。第二个参数是判断的依据,左侧的优良中差是手动键入的。“良”是红色,因为当前统计的是良的数量,引用“良”。

注意需要用形如$B$2的绝对引用,这样在向其他单元格复制时引用的位置不会变动。类似的,第二章和第三章如下图。

解决方案2 转变为一维数据-透视表
把列标题作为变量名,改为一维数据。具体操作步骤如下。
第一步,把表格复制三份,纵向排列。如下表所示。

第二步,删除除了第一个以外的标题行。

第三步,增加一列,章节。按下图填写 第一章、第二章、第三章。

第四步,删除并非本章的成绩。

第四步,把第一章、第二章、第三章3列合并为一列。

以上,完成了把列标题作为变量名,改为一维数据。这样就符合透视表的要求,只有一列,即最右一列 作为数据的计数项,其余的左边两列都是条件。
对上面“一维”表格使用透视表。

手动对行和列排序,得到下面的表格。

解决方案3 透视表-多重计算合并计算数据区域
以下方案最为优雅来自,OLIVER_QIN cnblogs 奔跑的金鱼
https://www.cnblogs.com/OliverQin/p/9436814.html。
快捷键Alt + D P,调出 数据透视表和数据透视图向导,选择点选按钮 多重合并计算数据区域。

共3步,所以第2步拆成了2a和2b!此处有手动狗头表情。

选定区域,然后点击 添加 按钮。

注意,此时得到的透视表不是我们想要的,如下表。

当前的行、列、值是这样的。

修改为下图所示,关键点是行由“行”改为“值”,即由按行(姓名)改为按值(优、良、中、差)分类。

对行和列手动排序,得到下面的表格。

总结
(1)核心原理 透视表认为只有一列数据是计数项,其余的列都是条件。
所以可以 拆分为单列统计再合并。
所以可以 把通过把标题改为变量名,把多列数据转为一维数据。
(2)特殊技术 数据透视表和数据透视图向导 可以 多重合并计算数据区域。
(3)你得做笔记啊。失败的实验也同样重要。发表是最好的记录。
对行和列手动排序,得到下面的表格。

7. 总结
(1)核心原理 透视表认为只有一列数据是计数项,其余的列都是条件。
所以可以 拆分为单列统计再合并。
所以可以 把通过把标题改为变量名,把多列数据转为一维数据。
(2)特殊技术 数据透视表和数据透视图向导 可以 多重合并计算数据区域。
(3)你得做笔记啊。失败的实验也同样重要。发表是最好的记录。
