如何为数据框列表中符合条件的每行抽取Bernoulli分布结果?
问题描述
我需要在数据框列表中,对每个数据框里distance列值大于1000的每行,独立从伯努利分布(Bernoulli)中抽取0或1。但我怀疑当前代码是针对整个数据框生成分布结果,而非逐行处理,请问如何确认这一点?同时,我不清楚如何修改rbinom(length(distance),1,0.8),使其为符合条件的每行进行单次抽取。
当前代码:
mylistnew<-lapply(mylist, transform, outcome = ifelse(distance > 1000, rbinom(length(distance),1,0.8), NA))
数据子集:
list(structure(c(775.056695476403, 1414.15314106691, 2509.95923787194, 1666.71143236238, 585.640129954299, 1169.17884175758, 152.505503148836, 619.226302243787, 1263.66546590149, 1682.8712425131, -2.86809018002943, -2.87220511792857, -2.91236875367306, -2.91236875367306, -2.91137226768259, -2.91236875367306, -2.86275243787543, -2.8606012634912, -2.86264610888995, -2.86004943151114, 58.2523804031471, 58.2594633464797, 58.1998311185373, 58.1998311185373, 58.1999333186371, 58.1998311185373, 58.243480631029, 58.2359999509482, 58.2407966146843, 58.2335609045358, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), .Dim = c(10L, 4L), .Dimnames = list(NULL, c("distance", "lon", "lat", "ID"))), structure(c(775.056695476403, 1414.15314106691, 2509.95923787194, 1666.71143236238, 585.640129954299, 1169.17884175758, 152.505503148836, 619.226302243787, 1263.66546590149, 1682.8712425131, -2.86809018002943, -2.87220511792857, -2.91236875367306, -2.91236875367306, -2.91137226768259, -2.91236875367306, -2.86275243787543, -2.8606012634912, -2.86264610888995, -2.86004943151114, 58.2523804031471, 58.2594633464797, 58.1998311185373, 58.1998311185373, 58.1999333186371, 58.1998311185373, 58.243480631029, 58.2359999509482, 58.2407966146843, 58.2335609045358, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), .Dim = c(10L, 4L), .Dimnames = list(NULL, c("distance", "lon", "lat", "ID"))))
解决方案
一、确认当前代码的问题
你的怀疑是对的,当前代码没有实现逐行独立抽取。核心原因是ifelse的运行逻辑:当第二个参数是一个向量时,ifelse会循环使用该向量的元素匹配条件为TRUE的行,若条件为TRUE的行数和向量长度不一致,就会重复取值,导致并非每行独立生成随机数。
验证步骤:
- 固定随机种子确保结果可复现:
set.seed(123) - 运行原代码后查看
outcome列 - 观察所有
distance>1000的行,会发现部分行的outcome值重复,这就证明代码没有逐行独立生成随机数。
二、修改代码的正确方式
以下两种方法都能实现仅对符合条件的每行独立抽取:
方法1:先初始化NA向量,再对目标行赋值
这种方法逻辑清晰,避免ifelse的潜在问题:
set.seed(123) # 可选,用于复现结果 mylistnew <- lapply(mylist, function(df) { df$outcome <- NA_real_ # 先将outcome列初始化为NA idx <- df$distance > 1000 # 筛选符合条件的行索引 # 仅为符合条件的行生成伯努利随机数 df$outcome[idx] <- rbinom(sum(idx), 1, 0.8) df })
方法2:改进ifelse写法
若坚持使用ifelse,需让rbinom生成的随机数数量与数据框行数一致,确保每个行对应一个独立随机数:
set.seed(123) mylistnew <- lapply(mylist, transform, outcome = ifelse(distance > 1000, rbinom(nrow(.), 1, 0.8), NA))
这里nrow(.)获取当前数据框的行数,生成对应长度的随机向量,ifelse会将每个随机数对应到每一行,实现逐行独立抽取。
三、验证修改后的结果
运行修改后的代码后,可通过以下命令查看结果:
# 查看第一个数据框的distance和outcome列 print(mylistnew[[1]][, c("distance", "outcome")])
distance<=1000的行outcome为NAdistance>1000的行outcome为0或1,且每个值都是独立生成的(重复运行代码结果会变化,固定种子则结果一致)
内容的提问来源于stack exchange,提问作者Salmo salar
相关产品推荐
相关产品推荐

