在R中仅移除数据集首尾指定列全为NA的行
问题描述
给定如下数据集:
col1 col2 var1 var2 var3 1 1 NA NA NA 2 2 NA NA NA 3 3 NA 3 NA 4 4 4 4 4 5 5 5 5 5 6 6 6 NA 6 7 7 NA 7 7 8 8 NA NA NA 9 9 9 NA NA 10 10 NA NA NA
需要以自动化方式(优先使用tidyverse)仅移除首尾位置中指定列(var1-var3)全为NA的行,最终结果如下:
col1 col2 var1 var2 var3 3 3 NA 3 NA 4 4 4 4 4 5 5 5 5 5 6 6 6 NA 6 7 7 NA 7 7 8 8 NA NA NA 9 9 9 NA NA
即仅删除第1、2、10行(第8行虽指定列全为NA,但因不在首尾需保留)。
以下是我自己的tidyverse解法:
library(tidyverse) have <- tibble(col1 = 1:10, col2 = 1:10, var1 = 1:10, var2 = 1:10, var3 = 1:10) have[c(1, 2, 10), 3:5] <- NA have[3, c(3, 5)] <- NA have[6, 4] <- NA have[7, 3] <- NA have[8, 3:5] <- NA have[9, 4:5] <- NA no_select_vars <- 3 # 指定列的数量 want <- have |> mutate(no_missing = rowSums(across(-c(col1, col2), ~ is.na(.x)))) |> slice(first(which(no_missing < no_select_vars)):n()) |> slice(1:last(which(no_missing < no_select_vars))) |> select(-no_missing)
请问是否有现成的函数可实现该需求,或更优雅的解法?
优雅解法推荐
目前没有专门的原生函数直接实现这个“仅移除首尾全NA指定列的行”的需求,但可以用tidyverse工具写出更简洁、可读性更强的代码:
解法1:用filter结合累积逻辑判断
核心思路是先标记出指定列不全为NA的行,再通过累积判断保留第一个有效行到最后一个有效行之间的所有内容:
library(tidyverse) # 构造数据集(和原始代码一致) have <- tibble(col1 = 1:10, col2 = 1:10, var1 = 1:10, var2 = 1:10, var3 = 1:10) have[c(1, 2, 10), 3:5] <- NA have[3, c(3, 5)] <- NA have[6, 4] <- NA have[7, 3] <- NA have[8, 3:5] <- NA have[9, 4:5] <- NA # 优雅解法 want <- have |> mutate(has_non_na = !if_all(var1:var3, is.na)) |> filter(cumany(has_non_na) & cumany(rev(has_non_na))) |> select(-has_non_na)
逻辑解释:
!if_all(var1:var3, is.na):判断每行的var1-var3是否不全为NA,生成布尔标记列has_non_nacumany(has_non_na):从前往后累积判断,只要前面出现过有效行,后续所有行都会保留(自动剔除开头的全NA行)cumany(rev(has_non_na)):将has_non_na反转后做累积判断,再反向生效,相当于从后往前累积剔除结尾的全NA行- 两者取交集,刚好保留第一个有效行到最后一个有效行之间的所有行
解法2:简化版位置索引法
如果偏好使用位置索引逻辑,也可以优化你的原始代码,避免手动指定列数:
want <- have |> mutate(has_non_na = !if_all(var1:var3, is.na)) |> slice(first(which(has_non_na)):last(which(has_non_na))) |> select(-has_non_na)
这个思路和你的原始解法一致,但用if_all替代了rowSums,无需手动计算指定列的数量,后续增减var列时不需要修改参数,灵活性更强。
内容的提问来源于stack exchange,提问作者tumbling_tower
相关产品推荐
相关产品推荐

