VBA分段统计数字的次数
时间:2022-07-22
本文章向大家介绍VBA分段统计数字的次数,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
1、需求:
根据员工的年龄,分年龄段统计人数。
2、举例:
接着上一次的例子,得到了出生日期后,然后你又得到任务,需要分年龄段统计人数。
序号 |
年龄段 |
人数 |
---|---|---|
1 |
20以下 |
|
2 |
20-35 |
|
3 |
35-45 |
|
4 |
45-55 |
|
5 |
55以上 |
算年龄用Year函数获取年份,用当前的年份减就可以了。
其实熟悉函数的话,这个用LOOKUP是非常合适的:
=LOOKUP(E2,{0,"20以下";20,"20-35";35,"35-45";45,"45-55";55,"55以上"})
然后再用数据透视表或者SUMIF等方法都可以。
3、代码实现
我们来看看用VBA如何完成这项工作,其实我们也是要实现一个类似LOOKUP的函数,LOOKUP的实现原理应该就是使用了二分法来查找,所谓二分法,从名字上大概就能猜到,它每次查找都能把数据量减半,大概原理如下:
二分法一次就能去掉一半的数据量,查找是非常高效的。100个数字,最多7次就可以找到所需要的数据,是以2为底数,计算数据个数的对数,1亿的数据量的话,最多是27次能找到需要的数据。当然它有一个重要的前提,数据源必须是排序的。
好了,知道了原理,我们用VBA代码来实现它:
'Arr 数据源,升序
'FindValue 要查找的数据
'找到Arr中刚好小于或等于它、并且下一个大于它的数据,返回下标
Function BinarySearch(arr() As Long, FindValue As Long) As Long
Dim low As Long, high As Long
Dim iMid As Long
Dim iEnd As Long
iEnd = UBound(arr)
high = iEnd
low = LBound(arr)
Do While low <= high
iMid = (high + low) 2
If arr(iMid) = FindValue Then
Exit Do
ElseIf arr(iMid) < FindValue Then
'小于的时候还要保证iMid+1是大于它的
If iMid = iEnd Then
Exit Do
Else
If arr(iMid + 1) > FindValue Then
Exit Do
End If
End If
'没有退出,说明还要往后面继续查找
low = iMid + 1
Else
high = iMid - 1
End If
Loop
If low > high Then
BinarySearch = -1
Else
BinarySearch = iMid
End If
End Function
有了这个函数,我们看看如何使用它来分段统计人数,最简单的想法自然是根据返回的下标,在数据源基础上新生成一列年龄段的描述,再根据这个新列用字典对象来统计。
但是,既然函数能够返回年龄段的下标,其实我们直接用数组就可以来统计出现的次数了:
Enum RetCode
ErrRT = -1
SuccRT = 1
End Enum
Enum Pos
RowStart = 2
姓名 = 3
年龄 = 5
KeyCol = 姓名
Cols = 年龄
End Enum
Type DataStruct
Src() As Variant
Rows As Long
Cols As Long
Result() As Variant
End Type
Sub vba_main()
Dim d As DataStruct
If RetCode.ErrRT = ReadSrc(d) Then Exit Sub
If RetCode.ErrRT = GetResult(d) Then Exit Sub
End Sub
Private Function GetResult(d As DataStruct) As RetCode
ReDim d.Result(1 To 5, 1 To 2) As Variant
d.Result(1, 1) = "20以下"
d.Result(2, 1) = "20-35"
d.Result(3, 1) = "35-45"
d.Result(4, 1) = "45-55"
d.Result(5, 1) = "55以上"
Dim arr(1 To 5) As Long
arr(1) = 0
arr(2) = 20
arr(3) = 35
arr(4) = 45
arr(5) = 55
Dim i As Long
Dim prow As Long
For i = Pos.RowStart To d.Rows
prow = BinarySearch(arr, VBA.CLng(d.Src(i, Pos.年龄)))
d.Result(prow, 2) = d.Result(prow, 2) + 1
Next
Range("A1").Offset(1, Pos.Cols + 1).Resize(5, 2).Value = d.Result
End Function
Private Function ReadSrc(d As DataStruct) As RetCode
ReadSrc = ReadData(d.Src, d.Rows, Pos.Cols, Pos.KeyCol, Pos.RowStart)
End Function
Private Function ReadData(ByRef RetArr() As Variant, ByRef RetRow As Long, Cols As Long, KeyCol As Long, RowStart As Long) As RetCode
ActiveSheet.AutoFilterMode = False
RetRow = Cells(Cells.Rows.Count, KeyCol).End(xlUp).Row
If RetRow < RowStart Then
MsgBox "没有数据"
ReadData = RetCode.ErrRT
Exit Function
End If
RetArr = Cells(1, 1).Resize(RetRow, Cols).Value
ReadData = RetCode.SuccRT
End Function
结果:
技巧:
这个问题其实还能有一个很好的技巧,我们观察需要统计的数据,很明显,数据是比较小的,不会超过100,而且又是数字,我们先记录1-100的数字对应的年龄段的下标,再判断年龄属于哪个区间段的时候,直接读取数组就可以了,省去了二分法查找,代码只需要改动GetResult:
Private Function GetResult(d As DataStruct) As RetCode
ReDim d.Result(1 To 5, 1 To 2) As Variant
d.Result(1, 1) = "20以下"
d.Result(2, 1) = "20-35"
d.Result(3, 1) = "35-45"
d.Result(4, 1) = "45-55"
d.Result(5, 1) = "55以上"
Dim arr(1 To 6) As Long
arr(1) = 0
arr(2) = 20
arr(3) = 35
arr(4) = 45
arr(5) = 55
arr(6) = 101
'技巧:利用1个数组来记录数字的下标
Dim Interval(100) As Long
Dim i As Long, j As Long
For i = 1 To 5
For j = arr(i) To arr(i + 1) - 1
Interval(j) = i
Next
Next
Dim prow As Long
For i = Pos.RowStart To d.Rows
'直接通过数组获取年龄段的下标
prow = Interval(VBA.CLng(d.Src(i, Pos.年龄)))
d.Result(prow, 2) = d.Result(prow, 2) + 1
Next
Range("A1").Offset(1, Pos.Cols + 1).Resize(5, 2).Value = d.Result
End Function
在数据量很大的情况,你会非常明显的感觉到这个技巧带来的速度提升。
- 10.11 Linux网络相关
- 利用Crypto++实现RSA加密算法
- 重学javascript 红皮高程(6)
- WriteUp分享 | LCTF的一道preg_match绕过+出题人的锅
- 利用crypto++库实现AES加密算法
- Android之倒计时CountdownTimer用法
- WriteUp分享 | LCTF的一道padding oracle攻击+sprintf格式化字符串导致的SQL注入
- 10.27 target介绍
- Android 之游戏开发流程
- shell脚本后台运行
- Android的.so文件你需要知道那些知识
- xshel配色方案
- cocos2dx-v3.5 2048 (一): 项目架构
- cocos2dx-v3.5 2048 (二): GameTool的设计与实现
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 从 SVN 迁移到 Git
- 在 Windows 系统上配置 Apache Git 服务器
- WMCTF2020 部分Writeup&招新帖
- 【DB宝15】生产环境中,如何利用DG的备库来异机还原一个新库?
- Java命令执行学习笔记
- SAP UI5应用DatePicker控件的设计明细
- 如何根据自己的实际需求开发属于自己的sublime text插件
- Sony Z13 系列笔记本安装 NVIDIA 官方最新版显卡驱动程序
- 通过网页进行 iOS 应用内部分发
- 【DB笔试面试853】在Oracle中,什么是手动建库?手动建库有哪些步骤?
- 使用 Intel HAXM 为 Android 模拟器加速,媲美真机
- NHibernate 使用 SqlQuery
- 谈谈 INotifyPropertyChanged 的实现
- C 语言小知识
- 使用代码配置 NHibernate