前言
上一节我们讨论了 Python 在数据处理上的优势,前后台大概收到的有用评论如下:
- "了解下 power query,可以很简单解决"
- "你文中说到vba也可以做到在固定代码中插入逻辑,具体如何做到?"
- "你的vba水平不行,才需要写这么复杂,比如分组可以先排序,再遍历判断边界做处理"
- "vba可以调用odbc等数据库驱动,使用 sql 呀"
那些不看内容不经思考的评论就不说了
真希望他们评论的同时能自己动手实践一下,后续我会逐一使用 power bi(不仅仅 pq,也用 dax),sql 实现。只要能提高工作效率,我非常乐意学习,也从不只看重某一种工具。
但是今天我要首先替 vba 说一句公道话,难道Excel数据处理任务的vba代码,真的不可能做到像 pandas 一样简洁直白吗?某些场景下,绝对可以!
实战案例是数据拆分,如果你觉得其他实现方式能比今天的代码更加简洁,欢迎给出你的答案,而非仅仅一句"xxx工具比你简单"!
泰坦尼克号沉船事件中的乘客信息表:
实现几个简单的拆分需求:
- 按"性别",把数据拆分到不同的工作表,工作表名字使用"性别(值)"
- 按 "性别"、"船舱等级",把数据拆分到不同的工作表,工作表名字使用"性别(值),船舱等级(值)"
- 按 "性别" ,把数据拆分到不同的工作簿(文件),文件名字使用"性别值.xlsx",每个对应文件中,按 "船舱等级",拆分到不同的工作表,工作表名字使用"船舱等级(值)"
需要达到以下目标:
- vba 代码多余表达要接近于 python 代码
- 就算换另一份数据,只需要修改关键表达即可使用(比如按某字段分组,只需要修改字段名字即可),无须大范围修改代码。
- vba 代码执行效率与原有的"数组 字典"一致
说白了,就是要提升 vba 的开发效率(写代码的时间) 与 保持执行效率(代码执行时间)
在固定逻辑中,插入自定义逻辑
上一篇文章的后半部分已经说明了为什么 vba 中写不出来像 pandas 的数据处理库,这里不再复述。
为什么python比vba更适合自动化处理Excel数据?
但是,vba 真的就不能做到"封装固定逻辑的同时,给予使用者放入自定义逻辑" 吗?
如下数据:
- 按 列1,列2 分组,每组数据输出也好,统计也行
vba中实现这个有许多方式,我就用最常用的一种方式,数组 字典:
- 这里使用 "|" 连接多个 作为 key 其实是不合理的做法,要避免
- 红框是本需求的关键逻辑
- 注意了,他是在固定逻辑的循环里面
当然也可以先排序,遍历判断边界。怎么实现分组其实没关系。
问题是排序只能对单元格区域,很多时候需求不是直接排序,或不希望改变原数据,这就导致你需要先输出单元格,排序后再放入数组,多了一些与分组没关联的操作
关键是,与需求相关的核心逻辑,是上图红框部分,就那么一小段的代码。
如果能够把非红框部分的代码包含在一个方法中,那么以后不管啥数据啥需求,分组相关的操作只需要调用这个方法就可以!
如果你是喜欢敲代码,以敲出复杂逻辑代码自豪,那么可以跳过这些内容
看看我们要怎么做到。
首先,把关键逻辑提取出来成为方法:
- 方法名字以及里面的2个参数名字叫啥都可以
固定逻辑中,我们需要动态调用:
- 关键就是这个 application.run 方法
- 其中的 "test.each_group" 是 "模块名字.方法名字"
- 现在外部逻辑可以让使用者自定义方法,作为字符串插入
此时,固定逻辑的方法,可以单独收起来到一个通用模块(或类模块),里面的代码以后都不需要改动。以后你在新项目中要使用,只需要导入这个模块就可以。
这就是 vba 中实现插入逻辑的实现方式,非常简单。
vba 中还可以通过实现接口做到插入逻辑,这是最正统的方式,但是那是 vb 为了应用开发工程而设计的,不适合我们的非专业开发者
核心原理就是这样子,我把这些固定方法定义在 vba_pd 模块下:
现在看看实际应用是怎么样?
需求1:按"性别",把数据拆分到不同的工作表,工作表名字使用"性别(值)"
先看 pandas :
vba:
- Call vba_pd.groupby_apply(df, "4", "main.each_性别") ,就是分组 处理
- 参数1自然是数据数组
- 参数2是分组列,4表示第4列
- 参数3是每个组的处理逻辑,执行时,每一组"性别"的数据就会传入自定义方法中执行
- 红框方法中,xdf 参数实际也是一个二维数组
分组 key 实际也可以做成字段,不过为了方便讲解,这里没有制作成完整的类模块形式
虽然看起来 vba 代码多一些,但多出来的只是一些通用细节:
- 指定工作表与单元格区域生成数组,用了2句,完全可以用一句
- 分组关键列vba用的是列号,这只是我偷懒,实际可以改造成支持列名指定
- 而 pandas 代码自带输出表头,vba实际也能做到
可以说,代码上的多余表达两者都非常少,这需求可以说打个平手
那么,可不可以做成多关键列分组?
需求2:按 "性别"、"船舱等级",把数据拆分到不同的工作表,工作表名字使用"性别(值),船舱等级(值)"
先看 pandas :
再看vba:
- 与之前需求变动非常少,因为本身需求表达变动也不多。代码就不应该有很大的区别
- groupby_apply 的参数2,使用英文逗号分隔指定列号即可多关键列分组
对比结果也与前一个需求一样,打个平手。
数据的传递
需求3:按 "性别" ,把数据拆分到不同的工作簿(文件),文件名字使用"性别值.xlsx",每个对应文件中,按 "船舱等级",拆分到不同的工作表,工作表名字使用"船舱等级(值)"
pandas 实现:
vba 实现:
- 注意绿色框中的调用,方法 groupby_apply 参数3之后,我们可以传递无数个参数,他们会组成一个字典,在组处理方法中参数3 kws,可以获取数据
- 看看每个方法中的处理,对照一下需求描述,多余的代码非常少!
如果你还没有理解这种代码的优点,你不妨自己动手用自己的方式实现一次。
不仅仅只是分组
"你没有超越 pandas 呢!"
额,是的,毕竟我只是用了几十分钟做的小模块,自然不能与人家整个社区做了几十年的积累相比。
有些人可能以为这玩意只能做分组,实际上这与数组、字典这些玩意一点关系都没有,核心还是那句,"固定逻辑中,允许插入自定义逻辑"。
所以,理论上vba可以做到如下实现代码:
如果你仍然偏爱于原有的写法,那么你可能是一位热爱敲代码的人。可惜(幸好)我不是
公众号回复"xlwings",获取本文源码