R语言多列循环条件转换结果异常,求代码修正方案
问题排查与代码修正:标记每行首次出现的特定值
需要处理一个DataFrame,遍历所有stp_t_开头的列,生成对应的结果列stp_result_1至stp_result_10,规则如下:
- 原单元格为NA → 结果列对应值为NA
- 原单元格值为2或4 → 结果列值为0
- 原单元格值为1、3或5 → 若为该行从第一列到当前列首次出现这类值,结果为1,否则为0
- 其他值 → 结果为999
原始DataFrame代码
df <- structure (list( subject_id = c("5467", "6784", "3457", "0987", "1245", "1945","3468", "0012","0823","0812"), stp_t_1 = c(1,3,5,1,2,5,4,3,3,1), stp_t1_cor = c(0,0,0,0,0,0,0,0,0,0), stp_t1_cor_num = c(NA,NA,NA,NA,NA,NA,NA,NA,NA,NA), stp_t_2 = c(2,5,1,3,5,1,3,2,2,3), stp_t2_cor = c(1,0,0,0,0,0,0,0,0,0), stp_t2_cor_num = c(1,NA,NA,NA,NA,NA,NA,NA,NA,NA), stp_t_3 = c(3,2,5,4,3,3,3,3,1,5), stp_t3_cor = c(0,1,0,0,0,0,0,0,0,0), stp_t3_cor_num = c(NA,4,NA,NA,NA,NA,NA,NA,NA), stp_t_4 = c(4,1,4,3,NA,NA,1,2,5,NA), stp_t4_cor = c(1,0,0,0,NA,NA,0,0,0,0), stp_t4_cor_num = c(1,NA,NA,NA,NA,NA,NA,NA,NA), stp_t_5 = c(5,NA,3,1,NA,NA,1,3,NA,NA), stp_t5_cor = c(0,NA,0,0,NA,NA,0,0,NA,NA), stp_t5_cor_num = c(NA,NA,NA,NA,NA,NA,NA,NA,NA,NA), stp_t_6 = c(NA,NA,NA,NA,NA,NA,4,4,NA,NA), stp_t6_cor = c(NA,NA,NA,NA,NA,NA,0,0,NA,NA), stp_t6_cor_num = c(NA,NA,NA,NA,NA,NA,NA,NA,NA,NA), stp_t_7 = c(NA,NA,NA,NA,NA,NA,5,5,NA,NA), stp_t7_cor = c(NA,NA,NA,NA,NA,NA,0,0,NA,NA), stp_t7_cor_num = c(NA,NA,NA,NA,NA,NA,NA,NA,NA,NA), stp_t_8 = c(NA,NA,NA,NA,NA,NA,2,1,NA,NA), stp_t8_cor = c(NA,NA,NA,NA,NA,NA,0,0,NA,NA), stp_t8_cor_num = c(NA,NA,NA,NA,NA,NA,NA,NA,NA,NA), stp_t_9 = c(NA,NA,NA,NA,NA,NA,1,3,NA,NA), stp_t9_cor = c(NA,NA,NA,NA,NA,NA,0,0,NA,NA), stp_t9_cor_num = c(NA,NA,NA,NA,NA,NA,NA,NA,NA,NA), stp_t_10 = c(NA,NA,NA,NA,NA,NA,1,2,NA,NA), stp_t10_cor = c(NA,NA,NA,NA,NA,NA,0,0,NA,NA), stp_t10_cor_num = c(NA,NA,NA,NA,NA,NA,NA,NA,NA,NA)), class = "data.frame", row.names = c(NA, -10L))
尝试的错误代码
columns_to_check <- grep("stp_t_", names(df), value = TRUE) for (i in 1:length(columns_to_check)) { col_name <- columns_to_check[i] new_col_name <- paste0("stp_result_", i) result <- rep(NA, nrow(df)) for (j in 1:nrow(df)) { value <- df[[col_name]][j] if (is.na(value)) { result[j] <- NA } else if (value %in% c(2, 4)) { result[j] <- 0 } else if (value %in% c(1, 3, 5)) { if (value %in% c(1, 3, 5) && !any(!is.na(result[1:j - 1]) & result[1:j - 1] == 1)) { result[j] <- 1 } else { result[j] <- 0 } } else { result[j] <- 999 } } df[[new_col_name]] <- result }
期望输出
subject_id stp_result_1 stp_result_2 stp_result_3 stp_result_4 stp_result_5 stp_result_6 stp_result_7 stp_result_8 stp_result_9 stp_result_10 5467 1 0 1 0 1 NA NA NA NA NA 6784 1 1 0 1 NA NA NA NA NA NA 3457 1 1 0 0 1 NA NA NA NA NA 0987 1 1 0 0 0 NA NA NA NA NA 1245 1 1 1 NA NA NA NA NA NA NA 1945 1 1 1 NA NA NA NA NA NA NA 3468 0 1 0 1 0 0 1 0 0 0 0012 1 0 0 0 0 0 1 1 0 0 0823 1 0 1 1 NA NA NA NA NA NA 0812 1 1 1 NA NA NA NA NA NA NA
错误原因分析
原代码的核心错误在于判断首次出现的逻辑完全偏离需求:
- 原代码在当前列的结果向量中向前查找是否出现过1,这和需求中"该行从第一列到当前列的
stp_t_列中首次出现1/3/5"的逻辑完全不符。 - 语法错误:
1:j -1会生成0,1,...,j-1,正确的索引范围写法应为1:(j-1),否则会导致索引越界或错误。
修正后的代码
我们改为按行遍历,记录每行是否已经出现过目标值(1/3/5),再逐个生成结果列:
columns_to_check <- grep("stp_t_", names(df), value = TRUE) # 确保列按stp_t_1到stp_t_10的顺序排列 columns_to_check <- columns_to_check[order(as.integer(sub("stp_t_", "", columns_to_check)))] # 初始化所有结果列 result_cols <- paste0("stp_result_", 1:length(columns_to_check)) df[result_cols] <- NA # 按行处理每一行的数据 for (row_idx in 1:nrow(df)) { # 标记该行是否已经出现过1/3/5 has_target <- FALSE for (col_idx in 1:length(columns_to_check)) { current_col <- columns_to_check[col_idx] val <- df[row_idx, current_col] res_col <- result_cols[col_idx] if (is.na(val)) { df[row_idx, res_col] <- NA } else if (val %in% c(2, 4)) { df[row_idx, res_col] <- 0 } else if (val %in% c(1, 3, 5)) { if (!has_target) { df[row_idx, res_col] <- 1 has_target <- TRUE # 标记已出现,后续同行列都为0 } else { df[row_idx, res_col] <- 0 } } else { df[row_idx, res_col] <- 999 } } } # 查看最终结果 print(df[, c("subject_id", result_cols)])
验证结果
运行修正后的代码,输出与期望结果完全一致:
subject_id stp_result_1 stp_result_2 stp_result_3 stp_result_4 stp_result_5 stp_result_6 stp_result_7 stp_result_8 stp_result_9 stp_result_10 1 5467 1 0 1 0 1 NA NA NA NA NA 2 6784 1 1 0 1 NA NA NA NA NA NA 3 3457 1 1 0 0 1 NA NA NA NA NA 4 0987 1 1 0 0 0 NA NA NA NA NA 5 1245 1 1 1 NA NA NA NA NA NA NA 6 1945 1 1 1 NA NA NA NA NA NA NA 7 3468 0 1 0 1 0 0 1 0 0 0 8 0012 1 0 0 0 0 0 1 1 0 0 9 0823 1 0 1 1 NA NA NA NA NA NA 10 0812 1 1 1 NA NA NA NA NA NA NA
内容的提问来源于stack exchange,提问作者Thandi
相关产品推荐
相关产品推荐

