将NA移至底部

3
我正在寻找一种简单的方法,将至少有一个NA值的所有行移动到数据框/数据表底部。例如:
> df <- data.table(aaa=c(1,2,3,4,NA,6,7),
                   bbb=c(1,9,5,NA,3,NA,9),
                   ccc=c(NA,3,NA,4,8,NA,2)
                   )
> df
   aaa bbb ccc
1:   1   1  NA
2:   2   9   3
3:   3   5  NA
4:   4  NA   4
5:  NA   3   8
6:   6  NA  NA
7:   7   9   2

将会变成这样:

> df2 <- moveNAtoBottom(df)
> df2
   aaa bbb ccc
1:   2   9   3
2:   7   9   2
3:   1   1  NA
4:   3   5  NA
5:   4  NA   4
6:  NA   3   8
7:   6  NA  NA

即所有没有NA值的列都在顶部。

注:

  • 目前,我不在意是否对行应用任何排序,或者是否保持或反转顺序……只要表格现在分为两个部分,顶部没有NAs,底部有NAs
  • 这可以是数据框或数据表
  • 依赖项是可以接受的

更好的方法是,如果我能够指定搜索NA的列。例如,仍然使用上一个示例:

> df3 <- moveNAtoBottom(df, applyTo = c("bbb", "ccc"))
> df3
   aaa bbb ccc
1:   2   9   3
2:  NA   3   8
3:   7   9   2
4:   1   1  NA
5:   3   5  NA
6:   4  NA   4
7:   6  NA  NA

谢谢

4个回答

6
你可以这样做:
library(dplyr)
df %>% 
  arrange(rowSums(is.na(.)))

这将会给出:

#  aaa bbb ccc
#1   2   9   3
#2   7   9   2
#3   1   1  NA
#4   3   5  NA
#5   4  NA   4
#6  NA   3   8
#7   6  NA  NA

或者您想指定列名:

df %>% 
  arrange(rowSums(is.na(select_(., 'bbb:ccc'))))

这就是结果:

#  aaa bbb ccc
#1   2   9   3
#2  NA   3   8
#3   7   9   2
#4   1   1  NA
#5   3   5  NA
#6   4  NA   4
#7   6  NA  NA

1
运行良好,谢谢。我可以问一个关于 dplyr 的问题吗?为什么它需要使用管道符才能正常工作?如果它们与 magrittr 的管道符以相同的方式工作,那么人们可能会认为 arrange(rowSums(is.na(select_(df, 'bbb:ccc')))) 也能正常工作,不是吗? - François M.
1
你需要指定在哪个“数据”上执行“arrange()”:尝试使用arrange(df, rowSums(is.na(select_(df, 'bbb:ccc')))) - Steven Beaupré

5

由于OP使用了data.table,因此可以使用data.table中的一个选项通过lapply遍历列,在is.na检查是否为NA,使用Reduce计算行总和,并使用该值对“df”行进行order

df[order(Reduce(`+`, lapply(df, is.na)))]
#    aaa bbb ccc
#1:   2   9   3
#2:   7   9   2
#3:   1   1  NA
#4:   3   5  NA
#5:   4  NA   4
#6:  NA   3   8
#7:   6  NA  NA

这里有一个类似的方法:

df[, anyna := FALSE]
for (k in names(df)) df[!(anyna), anyna := is.na(get(k))]
df[order(anyna)][, anyna := NULL][]

3
好的。这里有一个未导出的函数is_na,在这里它将非常有用。也许我们应该导出它。 - Arun
它也可以在稳定版本中使用,只是没有被导出。 - Arun

2

一个简单的基础R解决方案可能是:

> df <- data.frame(aaa=c(1,2,3,4,NA,6,7),
+                  bbb=c(1,9,5,NA,3,NA,9),
+                  ccc=c(NA,3,NA,4,8,NA,2))
> ok <- complete.cases(df)
> rbind(df[ok,], df[!ok,])
  aaa bbb ccc
2   2   9   3
7   7   9   2
1   1   1  NA
3   3   5  NA
4   4  NA   4
5  NA   3   8
6   6  NA  NA

如果只想选择某些列:

> ok <- complete.cases(df[, c("bbb","ccc")])
> rbind(df[ok,], df[!ok,])
  aaa bbb ccc
2   2   9   3
5  NA   3   8
7   7   9   2
1   1   1  NA
3   3   5  NA
4   4  NA   4
6   6  NA  NA

1

尝试:

na=apply(df,1,function(x)sum(is.na(x)))

df[order(na),]

或者一个函数
moveNAtoBottom<-function(df, applyTo=NULL){
  df<-as.data.frame(df)
  if (is.null(applyTo)) applyTo=colnames(df)
  na=rowSums(is.na(df[,applyTo,drop=F]))
  data.table(df[order(na),])
}

moveNAtoBottom(df)
moveNAtoBottom(df,c("ccc", "aaa"))

   aaa bbb ccc
1:   2   9   3
2:   4  NA   4
3:   7   9   2
4:   1   1  NA
5:   3   5  NA
6:  NA   3   8
7:   6  NA  NA

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接