select 数据帧中非 NA 数据范围的简单有效方法
Simple and efficient way to select non-NA data range in data frames
假设您有以下数据框:
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
如何以非常有效的方式 select 非 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
,我认为在 base R 中可能也存在一些像这个任务一样的东西 "of-the-shelf"。
更新
基于我得到的answers/comments的一些比较:
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
虽然可能还有一些内置函数可以执行此操作,但您可以通过子集化来完成。
当 is.na
传递整个 data.frame
时,它会生成一个布尔掩码,因此如果您对 !is.na(dat)
的行和列求和(即添加 TRUE
not NA
) 的值,对于具有 only 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,则此方法会留下 row/column:
> 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
(如果您更愿意将 rows/columns 替换为 any NA
,请调整感叹号,或使用 complete.cases
。)
此外,它应该非常快,因为 rowSums
和 colSums
是非常高度优化的,所以它应该仍然可以快速处理大型数据结构。
假设您有以下数据框:
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
如何以非常有效的方式 select 非 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
,我认为在 base R 中可能也存在一些像这个任务一样的东西 "of-the-shelf"。
更新
基于我得到的answers/comments的一些比较:
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
虽然可能还有一些内置函数可以执行此操作,但您可以通过子集化来完成。
当 is.na
传递整个 data.frame
时,它会生成一个布尔掩码,因此如果您对 !is.na(dat)
的行和列求和(即添加 TRUE
not NA
) 的值,对于具有 only 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,则此方法会留下 row/column:
> 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
(如果您更愿意将 rows/columns 替换为 any NA
,请调整感叹号,或使用 complete.cases
。)
此外,它应该非常快,因为 rowSums
和 colSums
是非常高度优化的,所以它应该仍然可以快速处理大型数据结构。