You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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]

这个方案的优势:

  1. 完全向量化操作,data.table内部优化了分组逻辑,处理10万条数据速度远快于循环。
  2. 自动处理只有1次考试的id,这类记录会被直接过滤,不需要额外的异常捕获。
  3. 一步到位完成统计和概率计算,不需要你后续用dplyr做多次分组合并。

内容的提问来源于stack exchange,提问作者stats_noob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 23:45:34