R语言中基于条件的数值向量随机分配问题
问题:R语言按条件随机分配数值时,同一条件下所有行取值相同
我正在用虚构值生成模拟数据集,需要在R语言中根据条件为特定行随机分配数值。但当前编写的脚本仅为每个条件返回一个样本值,无法实现让符合条件的所有行按设定概率随机分配所有样本值的需求。
尝试的两种代码实现
代码尝试1
library(dplyr) set.seed(123) patient_data$strength <- NA patient_data1 <- patient_data %>% mutate( strength = case_when( medication_name == "morphine" & frequency == "1-2" ~ as.character(sample(c(10, 30, 100, 200), 1, prob = c(0.25, 0.25, 0.25, 0.25))), medication_name == "codeine" & frequency == "1-2" ~ as.character(sample(c(60, 90, 120), 1, prob = c(1/3, 1/3, 1/3))), medication_name == "oxycodone" & frequency == "1-2" ~ as.character(sample(c(20, 40, 80, 120), 1, prob = c(0.25, 0.25, 0.25, 0.25))), medication_name == "oxycodone" & frequency == "4-6" ~ as.character(sample(c(5, 10, 20), 1, prob = c(1/3, 1/3, 1/3))), medication_name == "tramadol" & frequency == "4-6" ~ as.character((50)), medication_name == "tramadol" & frequency == "1-2" ~ as.character(sample(c(75, 150, 300, 400), 1, prob = c(0.25, 0.25, 0.25, 0.25))), medication_name == "fentanyl" & frequency == "1-4" ~ as.character((200)), medication_name == "fentanyl" & frequency %in% c("1", "2") ~ as.character(sample(c(25, 50, 75, 100), 1, prob = c(0.25, 0.25, 0.25, 0.25))), medication_name == "buprenorphine" & frequency %in% c("1", "2") ~ as.character(sample(c(5, 10, 20), 1, prob = c(1/3, 1/3, 1/3))), medication_name %in% c("morphine", "codeine") & frequency == "4-6" ~ as.character(sample(c(15, 30), 1, prob = c(0.5, 0.5))), TRUE ~ NA_character_ ) ) print(patient_data1)
代码尝试2
set.seed(123) patient_data$strength <- NA patient_data2 <- patient_data %>% mutate( strength = ifelse(medication_name == "morphine" & frequency == "1-2", sample(c(10, 30, 100, 200), size = 1), ifelse(medication_name == "morphine" & frequency == "4-6", sample(c(15, 30), size = 1), ifelse(medication_name == "codeine" & frequency == "1-2", sample(c(60, 90, 120), size = 1), ifelse(medication_name == "codeine" & frequency == "4-6", sample(c(15, 30), size = 1), ifelse(medication_name == "oxycodone" & frequency == "1-2", sample(c(20, 40, 80, 120), size = 1), ifelse(medication_name == "oxycodone" & frequency == "4-6", sample(c(5, 10, 20), size = 1), ifelse(medication_name == "tramadol" & frequency == "1-2", sample(c(75, 150, 300, 400), size = 1), ifelse(medication_name == "tramadol" & frequency == "4-6", 50, ifelse(medication_name == "fentanyl" & frequency == "1-4", 200, ifelse(medication_name == "fentanyl" & (frequency == "1" | frequency == "2"), sample(c(25, 50, 75, 100), size = 1), ifelse(medication_name == "buprenorphine" & (frequency == "1" | frequency == "2"), sample(c(5, 10, 20), size = 1), NA )))) ))))) ))) print(patient_data2)
初始数据框
patient_data <- read.table(text="medication_name frequency strength codeine 4-6 NA codeine 4-6 NA morphine 1-2 NA morphine 1-2 NA codeine 4-6 NA fentanyl 1-4 NA morphine 1-2 NA morphine 1-2 NA morphine 1-2 NA oxycodone 4-6 NA", header=T)
当前输出结果
medication_name frequency strength codeine 4-6 30 codeine 4-6 30 morphine 1-2 10 morphine 1-2 10 codeine 4-6 30 fentanyl 1-4 200 morphine 1-2 10 morphine 1-2 10 morphine 1-2 10 oxycodone 4-6 20
期望的输出分布
分布1(匹配设定的样本值分布)
patient_data1$strength n percent 100 78 0.46706587 15 37 0.22155689 20 3 0.01796407 200 12 0.07185629 5 24 0.14371257 50 13 0.07784431
分布2(匹配设定的样本值分布)
patient_data2$strength n percent 10 81 0.48502994 20 24 0.14371257 25 5 0.02994012 30 37 0.22155689 50 8 0.04790419 200 12 0.07185629
问题原因与解决方案
问题根源
两种尝试的代码中,每个条件分支的sample()函数都只指定了size=1,这会生成单个随机值,然后被广播到所有符合该条件的行,导致同一条件下所有行的strength取值完全相同。
解决方法
核心思路是为每个符合条件的行生成独立的随机样本,也就是让sample()生成和当前条件下行数一致的样本数量。可以通过dplyr的分组(group_by)结合n()获取当前组的行数来实现:
library(dplyr) set.seed(123) patient_data_final <- patient_data %>% # 按药物名称和频率分组,确保同一条件的行在同一组 group_by(medication_name, frequency) %>% mutate( strength = case_when( medication_name == "morphine" & frequency == "1-2" ~ as.character(sample(c(10, 30, 100, 200), size = n(), replace = TRUE, prob = c(0.25, 0.25, 0.25, 0.25))), medication_name == "codeine" & frequency == "1-2" ~ as.character(sample(c(60, 90, 120), size = n(), replace = TRUE, prob = c(1/3, 1/3, 1/3))), medication_name == "oxycodone" & frequency == "1-2" ~ as.character(sample(c(20, 40, 80, 120), size = n(), replace = TRUE, prob = c(0.25, 0.25, 0.25, 0.25))), medication_name == "oxycodone" & frequency == "4-6" ~ as.character(sample(c(5, 10, 20), size = n(), replace = TRUE, prob = c(1/3, 1/3, 1/3))), medication_name == "tramadol" & frequency == "4-6" ~ as.character(50), medication_name == "tramadol" & frequency == "1-2" ~ as.character(sample(c(75, 150, 300, 400), size = n(), replace = TRUE, prob = c(0.25, 0.25, 0.25, 0.25))), medication_name == "fentanyl" & frequency == "1-4" ~ as.character(200), medication_name == "fentanyl" & frequency %in% c("1", "2") ~ as.character(sample(c(25, 50, 75, 100), size = n(), replace = TRUE, prob = c(0.25, 0.25, 0.25, 0.25))), medication_name == "buprenorphine" & frequency %in% c("1", "2") ~ as.character(sample(c(5, 10, 20), size = n(), replace = TRUE, prob = c(1/3, 1/3, 1/3))), medication_name %in% c("morphine", "codeine") & frequency == "4-6" ~ as.character(sample(c(15, 30), size = n(), replace = TRUE, prob = c(0.5, 0.5))), TRUE ~ NA_character_ ) ) %>% # 取消分组,恢复正常数据框结构 ungroup() print(patient_data_final)
代码说明
group_by(medication_name, frequency):将数据按药物名称和频率分组,确保同一条件的行被归为一组。sample(..., size = n()):n()会返回当前组的行数,让sample()生成对应数量的随机样本,每个行对应一个独立值。replace = TRUE:允许重复抽取样本,符合模拟数据的常见需求(如果不需要重复可以去掉,但通常模拟时需要)。
运行这段代码后,同一条件下的行将按设定的概率随机分配不同的数值,符合期望的分布要求。
内容的提问来源于stack exchange,提问作者db2020
相关产品推荐
相关产品推荐

