用户行为分析系列实践 3 - 有序二分法过滤
目标任务
用户事件表T结构和部分数据示例如下:
Time | UserID | EventTypeID | EventType |
2022/6/1 10:20 | 1072755 | 3 | Search |
2022/6/1 12:12 | 1078030 | 2 | Browse |
2022/6/1 12:36 | 1005093 | 5 | Submit |
2022/6/1 13:21 | 1048655 | 1 | Login |
2022/6/1 14:46 | 1037824 | 6 | Logout |
2022/6/1 15:19 | 1049626 | 4 | AddtoCart |
2022/6/1 16:00 | 1009296 | 5 | Submit |
2022/6/1 16:39 | 1070713 | 2 | Browse |
2022/6/1 17:40 | 1090884 | 3 | Search |
T表字段说明:
字段名 | 数据类型 | 字段含义 |
Time | 日期时间 | 事件发生的时间戳,精确到毫秒 |
UserID | 整数 | 用户ID |
EventTypeID | 整数 | 事件类型ID |
EventType | 字符串 | 事件类型名称 |
计算任务:
统计指定时间段内每种事件类型下的发生次数和发生过该事件的去重用户数。
需要先从很大的数据量里筛选出指定时间段内的数据。
实践技能
将数据按时间字段排序后保存,读取指定时间段数据时采用二分法过滤,不必遍历所有数据,可以极大地加快过滤速度。
示例代码
1、 使用按时间有序的二进制文件转储数据库的数据
存量数据:将数据读出时按时间排序,然后写入二进制文件
A | |
1 | =connect("demo").cursor@x("select * from T order by Time") |
2 | =file("T.btx").export@b(A1) |
A1 读取T表的数据时按时间排序
增量数据:用时间戳来识别增量数据,每天0点以后,把前一天的增量数据追加到集文件里:
A | |
1 | =connect("demo").cursor@x("select * from T where Time>=? && Time<? order by Time",date(now()-1), date(now())) |
2 | =file("T.btx").export@ba(A1) |
A1 通过过滤条件筛选出前一天的数据,产生游标,需要先按时间戳排序
A2 将A1中的数据通过游标读出追加到T.btx集文件中。由于本例采用时间戳来识别增量数据,增量部分天然是后序时间发生的事件,所以不需要考虑和原文件排序归并的问题,直接追加即可。
2、 使用二分法快速过滤后再分组汇总
设统计时间段为2022年3月15日到2022年6月16日:
A | |
1 | >start=date("2022-03-15","yyyy-MM-dd"),end=date("2022-06-16","yyyy-MM-dd") |
2 | =file("T.btx").iselect@br(start:end,Time).groups(EventTypeID; EventType, count(1):Num, icount(UserID):iNum) |
A2 iselect表示从对表达式Time有序的文件读出Time在[start,end]区间的记录构成游标,此时区间是闭区间。@r选项表示Time不是唯一的,因为存在多用户并发操作的情况。@b表示从二进制文件读取。
iselect时不能同时使用并行选项,在单任务场景时,如果选出结果集较大,不一定会比采用并行后的全遍历更快。但在多任务场景时,通常也不能使用并行(多CPU要用于处理并发),iselect就会有较大优势。
运行结果:
EventTypeID | EventType | Num | iNum |
1 | Login | 4033000 | 393400 |
2 | Browse | 3578901 | 348791 |
3 | Search | 2947931 | 257539 |
4 | AddtoCart | 1845674 | 175476 |
5 | Submit | 867345 | 83375 |
6 | Logout | 4033000 | 393400 |