vba新姿势,如何让vba的数据处理"超越"Python

2021-09-01 12:16:19 浏览数 (1)

前言

上一节我们讨论了 Python 在数据处理上的优势,前后台大概收到的有用评论如下:

  1. "了解下 power query,可以很简单解决"
  2. "你文中说到vba也可以做到在固定代码中插入逻辑,具体如何做到?"
  3. "你的vba水平不行,才需要写这么复杂,比如分组可以先排序,再遍历判断边界做处理"
  4. "vba可以调用odbc等数据库驱动,使用 sql 呀"

那些不看内容不经思考的评论就不说了

真希望他们评论的同时能自己动手实践一下,后续我会逐一使用 power bi(不仅仅 pq,也用 dax),sql 实现。只要能提高工作效率,我非常乐意学习,也从不只看重某一种工具。

但是今天我要首先替 vba 说一句公道话,难道Excel数据处理任务的vba代码,真的不可能做到像 pandas 一样简洁直白吗?某些场景下,绝对可以!

实战案例是数据拆分,如果你觉得其他实现方式能比今天的代码更加简洁,欢迎给出你的答案,而非仅仅一句"xxx工具比你简单"

泰坦尼克号沉船事件中的乘客信息表:

实现几个简单的拆分需求:

  1. 按"性别",把数据拆分到不同的工作表,工作表名字使用"性别(值)"
  2. 按 "性别"、"船舱等级",把数据拆分到不同的工作表,工作表名字使用"性别(值),船舱等级(值)"
  3. 按 "性别" ,把数据拆分到不同的工作簿(文件),文件名字使用"性别值.xlsx",每个对应文件中,按 "船舱等级",拆分到不同的工作表,工作表名字使用"船舱等级(值)"

需要达到以下目标:

  1. vba 代码多余表达要接近于 python 代码
  2. 就算换另一份数据,只需要修改关键表达即可使用(比如按某字段分组,只需要修改字段名字即可),无须大范围修改代码。
  3. 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",获取本文源码

0 人点赞