我想删除这个数据帧中的行:

a)在所有列中包含NAs。下面是我的示例数据帧。

             gene hsap mmul mmus rnor cfam
1 ENSG00000208234    0   NA   NA   NA   NA
2 ENSG00000199674    0   2    2    2    2
3 ENSG00000221622    0   NA   NA   NA   NA
4 ENSG00000207604    0   NA   NA   1    2
5 ENSG00000207431    0   NA   NA   NA   NA
6 ENSG00000221312    0   1    2    3    2

基本上,我想获得如下所示的数据帧。

             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0   2    2    2    2
6 ENSG00000221312    0   1    2    3    2

b)只在某些列中包含NAs,所以我也可以得到这个结果:

             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0   2    2    2    2
4 ENSG00000207604    0   NA   NA   1    2
6 ENSG00000221312    0   1    2    3    2

当前回答

如果性能是优先考虑的,请使用数据。表和na.省略(),可选参数cols=。

na.omit.data.table是我的基准测试中最快的(见下文),无论是对所有列还是对选定列(OP问题第2部分)。

如果你不想使用数据。表,使用complete.cases()。

在一个普通的data.frame上,完成。case比na.省略()或dplyr::drop_na()快。请注意,na. ignore .data.frame不支持cols=。

基准测试结果

下面是base(蓝色)、dplyr(粉红色)和数据的比较。表(黄色)方法,删除所有或选择缺失的观测值,在100万个观测值的名义数据集上,20个数值变量具有5%的独立缺失可能性,以及第2部分的4个变量的子集。

结果可能会根据特定数据集的长度、宽度和稀疏性而有所不同。

注意y轴上的对数刻度。

基准测试脚本

#-------  Adjust these assumptions for your own use case  ------------
row_size   <- 1e6L 
col_size   <- 20    # not including ID column
p_missing  <- 0.05   # likelihood of missing observation (except ID col)
col_subset <- 18:21  # second part of question: filter on select columns

#-------  System info for benchmark  ----------------------------------
R.version # R version 3.4.3 (2017-11-30), platform = x86_64-w64-mingw32
library(data.table); packageVersion('data.table') # 1.10.4.3
library(dplyr);      packageVersion('dplyr')      # 0.7.4
library(tidyr);      packageVersion('tidyr')      # 0.8.0
library(microbenchmark)

#-------  Example dataset using above assumptions  --------------------
fakeData <- function(m, n, p){
  set.seed(123)
  m <-  matrix(runif(m*n), nrow=m, ncol=n)
  m[m<p] <- NA
  return(m)
}
df <- cbind( data.frame(id = paste0('ID',seq(row_size)), 
                        stringsAsFactors = FALSE),
             data.frame(fakeData(row_size, col_size, p_missing) )
             )
dt <- data.table(df)

par(las=3, mfcol=c(1,2), mar=c(22,4,1,1)+0.1)
boxplot(
  microbenchmark(
    df[complete.cases(df), ],
    na.omit(df),
    df %>% drop_na,
    dt[complete.cases(dt), ],
    na.omit(dt)
  ), xlab='', 
  main = 'Performance: Drop any NA observation',
  col=c(rep('lightblue',2),'salmon',rep('beige',2))
)
boxplot(
  microbenchmark(
    df[complete.cases(df[,col_subset]), ],
    #na.omit(df), # col subset not supported in na.omit.data.frame
    df %>% drop_na(col_subset),
    dt[complete.cases(dt[,col_subset,with=FALSE]), ],
    na.omit(dt, cols=col_subset) # see ?na.omit.data.table
  ), xlab='', 
  main = 'Performance: Drop NA obs. in select cols',
  col=c('lightblue','salmon',rep('beige',2))
)

其他回答

如果性能是优先考虑的,请使用数据。表和na.省略(),可选参数cols=。

na.omit.data.table是我的基准测试中最快的(见下文),无论是对所有列还是对选定列(OP问题第2部分)。

如果你不想使用数据。表,使用complete.cases()。

在一个普通的data.frame上,完成。case比na.省略()或dplyr::drop_na()快。请注意,na. ignore .data.frame不支持cols=。

基准测试结果

下面是base(蓝色)、dplyr(粉红色)和数据的比较。表(黄色)方法,删除所有或选择缺失的观测值,在100万个观测值的名义数据集上,20个数值变量具有5%的独立缺失可能性,以及第2部分的4个变量的子集。

结果可能会根据特定数据集的长度、宽度和稀疏性而有所不同。

注意y轴上的对数刻度。

基准测试脚本

#-------  Adjust these assumptions for your own use case  ------------
row_size   <- 1e6L 
col_size   <- 20    # not including ID column
p_missing  <- 0.05   # likelihood of missing observation (except ID col)
col_subset <- 18:21  # second part of question: filter on select columns

#-------  System info for benchmark  ----------------------------------
R.version # R version 3.4.3 (2017-11-30), platform = x86_64-w64-mingw32
library(data.table); packageVersion('data.table') # 1.10.4.3
library(dplyr);      packageVersion('dplyr')      # 0.7.4
library(tidyr);      packageVersion('tidyr')      # 0.8.0
library(microbenchmark)

#-------  Example dataset using above assumptions  --------------------
fakeData <- function(m, n, p){
  set.seed(123)
  m <-  matrix(runif(m*n), nrow=m, ncol=n)
  m[m<p] <- NA
  return(m)
}
df <- cbind( data.frame(id = paste0('ID',seq(row_size)), 
                        stringsAsFactors = FALSE),
             data.frame(fakeData(row_size, col_size, p_missing) )
             )
dt <- data.table(df)

par(las=3, mfcol=c(1,2), mar=c(22,4,1,1)+0.1)
boxplot(
  microbenchmark(
    df[complete.cases(df), ],
    na.omit(df),
    df %>% drop_na,
    dt[complete.cases(dt), ],
    na.omit(dt)
  ), xlab='', 
  main = 'Performance: Drop any NA observation',
  col=c(rep('lightblue',2),'salmon',rep('beige',2))
)
boxplot(
  microbenchmark(
    df[complete.cases(df[,col_subset]), ],
    #na.omit(df), # col subset not supported in na.omit.data.frame
    df %>% drop_na(col_subset),
    dt[complete.cases(dt[,col_subset,with=FALSE]), ],
    na.omit(dt, cols=col_subset) # see ?na.omit.data.table
  ), xlab='', 
  main = 'Performance: Drop NA obs. in select cols',
  col=c('lightblue','salmon',rep('beige',2))
)

使用{dplyr}包中的filter()函数和across()帮助函数是一种既通用又能产生可读性较好的代码的方法。

library(dplyr)

vars_to_check <- c("rnor", "cfam")

# Filter a specific list of columns to keep only non-missing entries

df %>% 
  filter(across(one_of(vars_to_check),
                ~ !is.na(.x)))

# Filter all the columns to exclude NA
df %>% 
  filter(across(everything(),
                ~ !is.na(.)))

# Filter only numeric columns
df %>%
  filter(across(where(is.numeric),
                ~ !is.na(.)))

类似地,在dplyr包中也有变体函数(filter_all, filter_at, filter_if),它们完成同样的事情:

library(dplyr)

vars_to_check <- c("rnor", "cfam")

# Filter a specific list of columns to keep only non-missing entries
df %>% 
  filter_at(.vars = vars(one_of(vars_to_check)),
            ~ !is.na(.))

# Filter all the columns to exclude NA
df %>% 
  filter_all(~ !is.na(.))

# Filter only numeric columns
df %>%
  filter_if(is.numeric,
            ~ !is.na(.))

我们也可以用子集函数。

finalData<-subset(data,!(is.na(data["mmul"]) | is.na(data["rnor"])))

这将只给出那些在mmul和rnor中都没有NA的行

Dplyr 1.0.4引入了两个配套的过滤函数:if_any()和if_all()。在这种情况下,if_all()伴随函数将特别有用:

a)删除所有列中包含NAs的行

df %>% 
  filter(if_all(everything(), ~ !is.na(.x)))

这一行将只保留那些列中没有NAs的行。

b)删除仅在某些列中包含NAs的行

cols_to_check = c("rnor", "cfam")

df %>% 
  filter(if_all(cols_to_check, ~ !is.na(.x)))

这一行将检查任何指定的列(cols_to_check)是否有NAs,并只保留没有NAs的那些行。

我更喜欢用下面的方法来检查行中是否包含NAs:

row.has.na <- apply(final, 1, function(x){any(is.na(x))})

这将返回逻辑向量,其中的值表示一行中是否有NA。你可以使用它来查看你需要删除多少行:

sum(row.has.na)

并最终放弃它们

final.filtered <- final[!row.has.na,]

对于过滤具有特定部分NAs的行,它变得有点棘手(例如,您可以将'final[,5:6]'输入到'apply')。 一般来说,Joris Meys的解决方案似乎更优雅。