R语言实现非NA列随机抽样行均值及NA异常处理
问题:按行随机抽取非NA值计算均值(处理不同NA数量的行)
我有一个数据框,需求是为每一行随机抽取3个非NA列(各行抽取的列可以不同),并计算这3个抽样值的均值。数据框中存在大量全NA、仅含1个或2个非NA值的行,且无法删除这些行。
我尝试了以下代码:
df_new <- df %>% rowwise %>% mutate(inflo_mean = mean(sample(na.omit(c_across(everything())), 3)))
运行后出现报错:
Error in `mutate()`: ℹ In argument: `inflo_mean = mean(sample(na.omit(c_across(everything())), 3))`. ℹ In row 1. Caused by error in `sample.int()`: ! invalid first argument
之后我拆分步骤,针对不同NA情况处理,编写了如下代码:
df_new2 <- df %>% rowwise() %>% mutate(num_NAs = sum(!is.na(across(starts_with("Col_")))), v_inflo = list(na.omit(c_across((starts_with("Col_"))))), inflo_mean = case_when(num_NAs > 2 ~ mean(sample(v_inflo, 3)), num_NAs == 2 ~ mean(v_inflo), num_NAs == 1 ~ as.numeric(v_inflo), num_NAs == 0 ~ NA_real_, TRUE ~ NA_real_))
但仍出现相同错误。我已确认所有列均为整数类型,请问问题出在哪里?是否有其他解决方案?
示例数据:
> dput(df) structure(list(Col_1 = c(NA, 77L, 82L, 172L), Col_2 = c(NA, 79L, NA, 135L), Col_3 = c(NA, 81L, NA, 131L), Col_4 = c(NA_integer_, NA_integer_, NA_integer_, NA_integer_), Col_5 = c(NA, NA, NA, 33L), Col_6 = c(NA, NA, NA, 104L), Col_7 = c(NA, NA, NA, 106L ), Col_8 = c(NA, NA, NA, 93L), Col_9 = c(NA, NA, NA, 50L), Col_10 = c(NA, NA, NA, 48L), Col_11 = c(NA, NA, NA, 96L), Col_12 = c(NA_integer_, NA_integer_, NA_integer_, NA_integer_), Col_13 = c(NA_integer_, NA_integer_, NA_integer_, NA_integer_), Col_14 = c(NA_integer_, NA_integer_, NA_integer_, NA_integer_), Col_15 = c(NA_integer_, NA_integer_, NA_integer_, NA_integer_)), class = c("tbl_df", "tbl", "data.frame"), row.names = c(NA, -4L))
问题根源
报错出现在全NA行:当na.omit(c_across(...))返回空向量时,sample()函数接收空向量作为第一个参数,会触发invalid first argument错误。即使你在case_when里判断了num_NAs == 0 ~ NA_real_,但case_when会评估所有分支(非短路执行),全NA行仍会执行sample(v_inflo, 3),此时v_inflo是空列表,直接触发错误。
解决方案
方案1:使用if/else短路逻辑(推荐)
if/else是短路执行的,只有满足条件的代码块会被运行,避免空向量传入sample():
df_final <- df %>% rowwise() %>% mutate( non_na_vals = list(na.omit(c_across(starts_with("Col_")))), inflo_mean = { len <- length(non_na_vals) if (len == 0) { NA_real_ } else if (len == 1) { as.numeric(non_na_vals) } else if (len == 2) { mean(non_na_vals) } else { mean(sample(non_na_vals, 3)) } }, non_na_vals = NULL # 可选:删除临时列 ) %>% ungroup() # 取消行分组,恢复常规数据框操作效率
方案2:给sample添加安全长度判断
在sample中用min(3, length(v_inflo))确保抽样数量不超过非NA值的长度,同时将全NA行的判断放在case_when最前面(减少无效评估):
df_final <- df %>% rowwise() %>% mutate( num_NAs = sum(!is.na(across(starts_with("Col_")))), v_inflo = list(na.omit(c_across(starts_with("Col_")))), inflo_mean = case_when( num_NAs == 0 ~ NA_real_, num_NAs == 1 ~ as.numeric(v_inflo), num_NAs == 2 ~ mean(v_inflo), num_NAs > 2 ~ mean(sample(v_inflo, min(3, length(v_inflo)))), TRUE ~ NA_real_ ) ) %>% ungroup()
内容的提问来源于Stack Exchange,提问作者Em Laskey
相关产品推荐
相关产品推荐

