R语言如何自动同步turn列NA值与原数据行首个NA位置
R语言实现按行匹配原数据NA位置自动填充回合列NA
问题描述
我有如下名为m的数据集:
structure(list(id = 1:4, A1 = c(20, 20, 20, 20), B1 = c(20, 20, 20, 20), A2 = c(10.0873038130365, 4.24227746311085, 4.15920316251515, 14.466663533707), B2 = c(8.02412449161373, 1.94874394931141, 12.9319354292045, 18.1870020286129), A3 = c(-2.52545701169281, 3.91930463167899, -3.22801555234644, 12.175898045939), B3 = c(6.72839637238315, 0.216884504971863, 9.43932210811731, 10.8221145438518), A4 = c(NA, -2.99467608949688, NA, 6.81498054505025), B4 = c(NA, -10.1318519125029, NA, 1.91945144708921), A5 = c(NA, NA, NA, -2.53105562138148), B5 = c(NA, NA, NA, -4.39906344008031)), row.names = c(1L, 4L, 8L, 11L), class = "data.frame", reshapeWide = list(v.names = NULL, timevar = "time", idvar = "id", times = 1:5, varying = structure(c("A1", "B1", "A2", "B2", "A3", "B3", "A4", "B4", "A5", "B5"), .Dim = c(2L, 5L))))
数据集预览:
id A1 B1 A2 B2 A3 B3 A4 B4 A5 B5 1 1 20 20 10.087304 8.024124 -2.525457 6.7283964 NA NA NA NA 4 2 20 20 4.242277 1.948744 3.919305 0.2168845 -2.994676 -10.131852 NA NA 8 3 20 20 4.159203 12.931935 -3.228016 9.4393221 NA NA NA NA 11 4 20 20 14.466664 18.187002 12.175898 10.8221145 6.814981 1.919451 -2.531056 -4.399063
我希望针对每一行(截至该行第一个NA出现的位置为止),创建随机的turn(回合)列(类似游戏中的回合设定),目前手动实现的代码如下:
A_options <- c("red", "blue", "green", "yellow") A_turn_1 <- sample(A_options, 4, replace=TRUE, prob=c(0.25, 0.25, 0.25, 0.25)) A_turn_2 <- sample(A_options, 4, replace=TRUE, prob=c(0.25, 0.25, 0.25, 0.25)) A_turn_3 <- sample(A_options, 4, replace=TRUE, prob=c(0.25, 0.25, 0.25, 0.25)) A_turn_4 <- sample(A_options, 4, replace=TRUE, prob=c(0.25, 0.25, 0.25, 0.25)) A_turn_5 <- sample(A_options, 4, replace=TRUE, prob=c(0.25, 0.25, 0.25, 0.25)) B_options <- c("grey", "black", "white", "pink") B_turn_1 <- sample(B_options, 4, replace=TRUE, prob=c(0.25, 0.25, 0.25, 0.25)) B_turn_2 <- sample(B_options, 4, replace=TRUE, prob=c(0.25, 0.25, 0.25, 0.25)) B_turn_3 <- sample(B_options, 4, replace=TRUE, prob=c(0.25, 0.25, 0.25, 0.25)) B_turn_4 <- sample(B_options, 4, replace=TRUE, prob=c(0.25, 0.25, 0.25, 0.25)) B_turn_5 <- sample(B_options, 4, replace=TRUE, prob=c(0.25, 0.25, 0.25, 0.25)) new = cbind(m,A_turn_1,A_turn_2, A_turn_3, A_turn_4, A_turn_5, B_turn_1, B_turn_2, B_turn_3, B_turn_4, B_turn_5)
生成的数据集预览:
id A1 B1 A2 B2 A3 B3 A4 B4 A5 B5 A_turn_1 A_turn_2 A_turn_3 A_turn_4 A_turn_5 B_turn_1 B_turn_2 B_turn_3 B_turn_4 B_turn_5 1 1 20 20 10.087304 8.024124 -2.525457 6.7283964 NA NA NA NA yellow green blue green yellow grey black black pink grey 4 2 20 20 4.242277 1.948744 3.919305 0.2168845 -2.994676 -10.131852 NA NA red red yellow green red pink black white black black 8 3 20 20 4.159203 12.931935 -3.228016 9.4393221 NA NA NA NA blue red yellow blue blue pink grey black white pink 11 4 20 20 14.466664 18.187002 12.175898 10.8221145 6.814981 1.919451 -2.531056 -4.399063 green yellow red red blue pink white grey white grey
需要实现的规则:对每一行,turn列的NA终止位置需要和原数据行的第一个NA位置保持同步,具体要求:
- 第1行:
A_turn_4、A_turn_5、B_turn_4、B_turn_5替换为NA - 第2行:
A_turn_5、B_turn_5替换为NA - 第3行:
A_turn_4、A_turn_5、B_turn_4、B_turn_5替换为NA - 第4行:无需替换任何元素为NA
实现方法
核心逻辑是先计算每行第一个NA对应的有效回合数,再批量把超出有效回合的turn列设为NA,不需要手动逐列处理。
第一步:计算每行有效回合数
# 提取所有A、B开头的原始数值列(排除id列) value_cols <- grep("^[AB]\\d", names(m), value = TRUE) # 按行查找第一个NA出现的列位置,无NA的行返回列数+1 first_na_pos <- apply(m[value_cols], 1, function(x) { na_idx <- which(is.na(x)) if (length(na_idx) == 0) length(value_cols) + 1 else min(na_idx) }) # 每回合包含A、B两列,计算每行有效回合数 valid_rounds <- ceiling((first_na_pos - 1) / 2)
计算得到的valid_rounds结果为c(3,4,3,5),和需求完全匹配。
方法1:修改已经生成好的new数据集
如果已经按手动方式生成了全量turn列,可以直接循环逐行替换NA:
# 提取所有A、B类turn列 a_turn_cols <- grep("^A_turn_\\d", names(new), value = TRUE) b_turn_cols <- grep("^B_turn_\\d", names(new), value = TRUE) # 逐行替换超出有效回合的位置为NA for (i in seq_len(nrow(new))) { r <- valid_rounds[i] if (r < length(a_turn_cols)) { new[i, a_turn_cols[(r+1):length(a_turn_cols)]] <- NA } if (r < length(b_turn_cols)) { new[i, b_turn_cols[(r+1):length(b_turn_cols)]] <- NA } }
方法2:直接生成带正确NA的数据集(更简洁)
不需要先生成所有随机值再替换,可以在生成turn列的时候直接把无效位置设为NA,减少冗余计算:
A_options <- c("red", "blue", "green", "yellow") B_options <- c("grey", "black", "white", "pink") max_round <- 5 # 循环生成每回合的turn列 for (turn in seq_len(max_round)) { # 先生成本回合全量随机值 m[[paste0("A_turn_", turn)]] <- sample(A_options, nrow(m), replace = TRUE) m[[paste0("B_turn_", turn)]] <- sample(B_options, nrow(m), replace = TRUE) # 有效回合数小于当前回合的行,直接设为NA invalid_rows <- valid_rounds < turn m[invalid_rows, paste0("A_turn_", turn)] <- NA m[invalid_rows, paste0("B_turn_", turn)] <- NA }
运行后得到的结果完全符合规则,不需要手动调整每一列。
内容的提问来源于stack exchange,提问作者stats_noob
相关产品推荐
相关产品推荐

