R语言:我是否正确向量化了计算考试结果条件概率的代码?
关于R代码向量化改写的疑问及优化方案
一、生成数据集的代码
id = sample.int(10000, 100000, replace = TRUE) res = c(1,0) results = sample(res, 100000, replace = TRUE) date_exam_taken = sample(seq(as.Date('1999/01/01'), as.Date('2020/01/01'), by="day"), 100000, replace = TRUE) my_data = data.frame(id, results, date_exam_taken) my_data <- my_data[order(my_data$id, my_data$date_exam_taken),] my_data$general_id = 1:nrow(my_data) my_data$exam_number = ave(my_data$general_id, my_data$id, FUN = seq_along) my_data$general_id = NULL
二、初始循环实现代码
library(data.table) setDT(my_data) my_list = vector("list", length(unique(my_data$id))) # 创建预定义维度的空向量 my_vector = vector("list", 100) for (i in 1:length(unique(my_data$id))) { tryCatch({ start_i = my_data[my_data$id == i,] pairs_i = data.frame(first = head(start_i$results, -1), second = tail(start_i$results, -1)) frame_i = as.data.table(table(pairs_i)) frame_i[, id := i] print(frame_i) my_vector[[i]] = frame_i }, error = function(e){}) } final = rbindlist(my_vector)
三、尝试的向量化改写代码
# 我觉得不需要提前创建列表或向量来存结果? my_vector = sapply(unique(my_data$id), function(i) { {tryCatch({ setDT(my_data) start_i = my_data[my_data$id == i,] pairs_i = data.frame(first = head(start_i$results, -1), second = tail(start_i$results, -1)) frame_i = as.data.frame(table(pairs_i)) frame_i$i = i print(frame_i) return(frame_i) }, error = function(e){print(paste("发生错误:", e))}) } }) # 报错了,所以换了另一段代码 final = rbindlist(my_vector, fill = TRUE) # 不确定这样是不是完全可行? final = do.call(rbind.data.frame, my_vector)
四、疑问解答:你的改写不算真正的向量化
你用sapply的写法本质还是遍历每个id,只是换了一种循环形式,没有实现真正的向量化,而且还存在几个问题:
- 每次循环里重复执行
setDT(my_data),完全多余,只需要在开头执行一次。 - 当某个id只有1次考试时,
head(start_i$results, -1)会生成空数据,table(pairs_i)会出错,虽然有tryCatch但处理不够高效。 - 最后合并时,因为部分列表元素结构可能不一致,
do.call(rbind.data.frame, my_vector)很容易报错。
真正的向量化优化方案(用data.table实现)
直接利用data.table的分组移位功能,不需要循环或遍历,效率提升明显:
library(data.table) setDT(my_data) # 按id分组,生成每个记录的前一次考试结果 my_data[, prev_result := shift(results), by = id] # 过滤掉没有前一次结果的记录(每个id的第一条考试记录) pairs_data = my_data[!is.na(prev_result), .(first = prev_result, second = results)] # 统计每种(first, second)配对的数量 final = pairs_data[, .N, by = .(first, second)] # 直接计算条件概率:给定first时second的概率 final[, prob := N / sum(N), by = first]
这个方案的优势:
- 完全向量化操作,data.table内部优化了分组逻辑,处理10万条数据速度远快于循环。
- 自动处理只有1次考试的id,这类记录会被直接过滤,不需要额外的异常捕获。
- 一步到位完成统计和概率计算,不需要你后续用dplyr做多次分组合并。
内容的提问来源于stack exchange,提问作者stats_noob
相关产品推荐
相关产品推荐

