在数据帧中选择非NA数据范围的简单而有效的方法

时间:2016-02-06 01:01:47

标签: r indexing dataframe na

假设您有以下数据框:

dat <- data.frame(a = c(1:3, NA), b = c(letters[1:3], NA), c = NA)

> dat
   a    b  c
1  1    a NA
2  2    b NA
3  3    c NA
4 NA <NA> NA

如何以非常有效的方式选择非NA区域?

这是我目前使用的:

ensureNonNaRange <- function(dat) {
  idx_col <- ! sapply(dat, function(ii) all(is.na(ii)))
  idx_row <- ! sapply(1:nrow(dat), function(ii) all(is.na(unlist(dat[ii, ]))))
  dat[idx_row, idx_col]
}

> ensureNonNaRange(dat)
  a b
1 1 a
2 2 b
3 3 c

就在今天,我才注意到之前我还不知道的非常有用的功能type.convert,我认为可能还存在一些东西,而且还有一些东西。喜欢基地R的这个任务。

更新

根据我得到的答案/评论进行一些比较:

ensureNonNaRange2 <- function(dat) {
  dat[rowSums(!is.na(dat)) != 0, colSums(!is.na(dat)) != 0]
}

microbenchmark::microbenchmark(
  a = ensureNonNaRange(dat),
  b = ensureNonNaRange2(dat)
)

Unit: microseconds
 expr     min       lq     mean   median       uq     max neval
    a 296.178 310.1070 346.2259 329.0210 349.9875 680.035   100
    b 112.313 120.0845 134.1716 125.6555 133.7200 338.112   100

1 个答案:

答案 0 :(得分:2)

虽然可能还有一些内置函数可以执行此操作,但您可以使用子集进行此操作。

is.na传递整个data.frame时,它会生成一个布尔掩码,因此如果对!is.na(dat)的行和列求和(即添加TRUE值什么是 not NA),对于只有 NA s的行和列,您得到的总和为零。

因此,如果我们按行和列总和为!= 0时的子集,我们会留下非NA值的行和列:

> dat[rowSums(!is.na(dat)) != 0, colSums(!is.na(dat)) != 0]
  a b
1 1 a
2 2 b
3 3 c

如果行或列中的某些但不是全部值都是NA,则此方法会留下该行/列:

> dat[2,2] <- NA
> dat[rowSums(!is.na(dat)) != 0, colSums(!is.na(dat)) != 0]
  a    b
1 1    a
2 2 <NA>
3 3    c

(如果您希望使用任何 NA来删除行/列,请调整感叹号,或使用complete.cases。)

此外,它应该非常超快,因为rowSumscolSums已经过高度优化,因此它仍然可以在庞大的数据结构上快速运行。