如何用data.table实现按用户累计分类事件计数的虚拟变量转换?
问题描述
我有一个名为current的data.table,包含3个用户的多条记录,每条记录包含person.id、分类类型的event列以及date列,且已按时间顺序排序。需要将其转换为名为desired的表:为每行新增各事件类型的列,记录该用户截至当前行时遇到对应事件的累计次数。此外原数据集还有其他需要保留到结果中的列。之前用循环实现,但面对百万级数据时效率极差,求高效数据处理方法。
附代码示例(修正原代码语法错误):
library(data.table) current <- data.table( person.id = c(1,2,3,1,2,3), event = factor(c("categoryA", "categoryA", "categoryD", "categoryA", "categoryC", "categoryB")), date = as.Date(c("2020-01-01", "2020-03-23", "2020-09-09", "2020-12-30", "2021-06-03", "2022-03-22")) ) # 期望的转换操作 desired <- current[, ...] print(current) print(desired)
原输出示例:
person.id event date 1: 1 categoryA 2020-01-01 2: 2 categoryA 2020-03-23 3: 3 categoryD 2020-09-09 4: 1 categoryA 2020-12-30 5: 2 categoryC 2021-06-03 6: 3 categoryB 2022-03-22 person.id event date categoryA categoryB categoryC categoryD 1: 1 categoryA 2020-01-01 1 0 0 0 2: 2 categoryA 2020-03-23 1 0 0 0 3: 3 categoryD 2020-09-09 0 0 0 1 4: 1 categoryA 2020-12-30 2 0 0 0 5: 2 categoryC 2021-06-03 1 0 1 0 6: 3 categoryB 2022-03-22 0 1 0 1
高效解决方案
利用data.table的分组累积计算特性,直接在原表上批量生成各事件的累计计数列,完全避免循环,适合百万级数据:
library(data.table) # 修正后的原始数据 current <- data.table( person.id = c(1,2,3,1,2,3), event = factor(c("categoryA", "categoryA", "categoryD", "categoryA", "categoryC", "categoryB")), date = as.Date(c("2020-01-01", "2020-03-23", "2020-09-09", "2020-12-30", "2021-06-03", "2022-03-22")) ) # 获取所有事件类型 event_types <- levels(current$event) # 按person.id分组,为每个事件类型生成累计计数列 current[, (event_types) := lapply(event_types, function(type) { cumsum(event == type) }), by = person.id] # 此时current就是期望的desired表 print(current)
原理说明
- 分组隔离:
by = person.id确保每个用户的累计计数独立计算,不会跨用户混淆。 - 批量列生成:
(event_types)用变量名批量创建新列,无需手动逐个编写事件列名。 - 高效向量运算:
cumsum(event == type)对分组内数据执行向量式累积求和,data.table底层优化过的运算逻辑比循环快几个数量级。 - 自动保留原列:所有原始列(包括其他需要保留的列)都会被完整保留,无需额外处理。
运行结果与示例中的desired表完全一致。
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

