data.frame内のすべてまたは一部のNA(欠損値)を持つ行を削除します


852

このデータフレームの次のような行を削除します。

a)すべての列にわたってsを含みNAます。以下は私のデータフレームの例です。

             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)一部の列のみにsを含めるNAため、この結果も得られます。

             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

回答:


1063

またチェックcomplete.cases

> final[complete.cases(final), ]
             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0    2    2    2    2
6 ENSG00000221312    0    1    2    3    2

na.omitすべてを削除するだけの方が適していNAます。complete.casesデータフレームの特定の列のみを含めることにより、部分的な選択が可能になります。

> final[complete.cases(final[ , 5:6]),]
             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

あなたのソリューションは機能しません。の使用を主張する場合はis.na、次のようなことを行う必要があります。

> final[rowSums(is.na(final[ , 5:6])) == 0, ]
             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

しかし、使用complete.casesははるかに明確で高速です。


8
末尾のカンマの意味は何final[complete.cases(final),]ですか?
hertzsprung 2012年

6
@hertzsprung列ではなく行を選択する必要があります。他にどのようにしますか?
Joris Meys、2012年

4
の単純な否定はありcomplete.casesますか?行を破棄せずにNAで保持したい場合はどうすればよいですか?final[ ! complete.cases(final),]協力しない...
tumultous_rooster

2
finalデータフレーム変数ですか?
モールス

1
@Prateek確かにそうです。
Joris Meys 2018年

256

お試しくださいna.omit(your.data.frame)。2番目の質問については、(明確にするために)別の質問として投稿してみてください。


na.omitは行を削除しますが、行番号は保持します。適切に番号が付けられるように、これをどのように修正しますか?
クマ

3
行番号を気にしない場合は@Bearを実行してくださいrownames(x) <- NULL
RomanLuštrik18年

na.omit()列に含まNAれる行を削除することに注意してください
Victor Maxwell

116

tidyr新しい機能がありますdrop_na

library(tidyr)
df %>% drop_na()
#              gene hsap mmul mmus rnor cfam
# 2 ENSG00000199674    0    2    2    2    2
# 6 ENSG00000221312    0    1    2    3    2
df %>% drop_na(rnor, cfam)
#              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

3
パイプとの間には実際の接続はありませんdrop_na。たとえばdf %>% drop_na()df %>% na.omit()drop_na(df)はすべて基本的に同等です。
Ista

4
@Ista同意しない。na.omit省略されたケースのインデックスなどの追加情報を追加します。さらに重要なこととして、列を選択することはできませんdrop_na。これが素晴らしいところです。
lukeA

3
確かに、私の指摘は、それとはパイプとは何の関係もないということです。あなたは使用することができna.omit、使用することができます同じように、またはパイプなしdrop_naでまたはパイプなし。
Ista

1
確かに、パイプとはまったく関係ありません。drop_na()は他の関数と同じで、直接またはパイプを使用して呼び出すことができます。残念ながら、drop_na()は、他の前述のメソッドとは異なり、zooまたはxtsオブジェクトタイプでは使用できません。これは問題になるかもしれません。
Dave

そうです、パイプに触れないように答えを編集しました。
Arthur Yip

91

行にNAが含まれているかどうかを確認するには、次の方法を使用します。

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

これは、行にNAがあるかどうかを示す値を持つ論理ベクトルを返します。これを使用して、ドロップする必要がある行数を確認できます。

sum(row.has.na)

そして最終的にそれらを落とす

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

NAの特定の部分を含む行をフィルタリングするには、少しトリッキーになります(たとえば、「final [、5:6]」を「apply」にフィードできます)。一般に、Joris Meysのソリューションはより洗練されているようです。


2
これは非常に遅いです。たとえば、前述のcomplete.cases()ソリューションよりもはるかに遅い。少なくとも、私の場合は、xtsデータについてです。
デイブ

3
rowSum(!is.na(final))次よりも適しているようですapply()
sindri_baldur

45

行が無効であると見なされる方法をより詳細に制御したい場合の別のオプションは、

final <- final[!(is.na(final$rnor)) | !(is.na(rawdata$cfam)),]

上記を使用して、これ:

             gene hsap mmul mmus rnor cfam
1 ENSG00000208234    0   NA   NA   NA   2
2 ENSG00000199674    0   2    2    2    2
3 ENSG00000221622    0   NA   NA   2   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
1 ENSG00000208234    0   NA   NA   NA   2
2 ENSG00000199674    0   2    2    2    2
3 ENSG00000221622    0   NA   NA   2   NA
4 ENSG00000207604    0   NA   NA   1    2
6 ENSG00000221312    0   1    2    3    2

...行5のみが削除されます。これは、rnorANDの両方のNAを含む唯一の行であるためcfamです。その後、ブール論理を変更して、特定の要件に合わせることができます。


5
しかし、それぞれを入力せずに多くの列をチェックしたい場合、これをどのように使用できますか?final [、4:100]の範囲を使用できますか?
Herman Toothrot 2016年

40

各行に有効なNAの数を制御したい場合は、この関数を試してください。多くの調査データセットでは、あまりにも多くの空白の質問の回答が結果を台無しにする可能性があります。したがって、それらは特定のしきい値の後で削除されます。この関数を使用すると、削除する前に行が保持できるNAの数を選択できます。

delete.na <- function(DF, n=0) {
  DF[rowSums(is.na(DF)) <= n,]
}

デフォルトでは、すべてのNAが削除されます。

delete.na(final)
             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0    2    2    2    2
6 ENSG00000221312    0    1    2    3    2

または、許可されるNAの最大数を指定します。

delete.na(final, 2)
             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

39

パフォーマンスを優先する場合は、使用data.tableしてna.omit()、オプションのparamを持ちますcols=

na.omit.data.table すべての列または選択した列(OP質問パート2)に関係なく、私のベンチマーク(以下を参照)で最速です。

使用したくない場合はdata.tablecomplete.cases()

バニラdata.framecomplete.casesは、na.omit()またはより速いですdplyr::drop_na()。をna.omit.data.frameサポートしていないことに注意してくださいcols=

ベンチマーク結果

これは、ベース(青)、dplyr(ピンク)、およびdata.table欠落している可能性が5%独立している20の数値変数の100万観測値の想定データセットで、欠落している観測値をすべてまたは選択して削除(黄色)メソッドのパート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))
)

18

dplyrパッケージを使用すると、次のようにNAをフィルターできます。

dplyr::filter(df,  !is.na(columnname))

1
より遅い10.000回程度これを実行するdrop_na()
Zimano

17

これにより、NA以外の値が少なくとも1つある行が返されます。

final[rowSums(is.na(final))<length(final),]

これにより、少なくとも2つの非NA値を持つ行が返されます。

final[rowSums(is.na(final))<(length(final)-1),]

16

最初の質問については、すべてのNAを削除するのに快適なコードがあります。@Gregorに感謝します。

final[!(rowSums(is.na(final))),]

2番目の質問の場合、コードは前のソリューションからの変更点にすぎません。

final[as.logical((rowSums(is.na(final))-5)),]

-5はデータの列数です。これにより、rowSumsの合計が5になり、減算後にゼロになるため、すべてのNAを持つ行が削除されます。今回は、as.logicalが必要です。


final [as.logical((rowSums(is.na(final))-ncol(final)))、]普遍的な回答
Ferroao

14

これにはサブセット機能を使用することもできます。

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

これにより、mmulとrnorの両方にNAがない行のみが表示されます


9

私はシンセサイザーです。ここで、答えを1つの関数にまとめました。

#' keep rows that have a certain number (range) of NAs anywhere/somewhere and delete others
#' @param df a data frame
#' @param col restrict to the columns where you would like to search for NA; eg, 3, c(3), 2:5, "place", c("place","age")
#' \cr default is NULL, search for all columns
#' @param n integer or vector, 0, c(3,5), number/range of NAs allowed.
#' \cr If a number, the exact number of NAs kept
#' \cr Range includes both ends 3<=n<=5
#' \cr Range could be -Inf, Inf
#' @return returns a new df with rows that have NA(s) removed
#' @export
ez.na.keep = function(df, col=NULL, n=0){
    if (!is.null(col)) {
        # R converts a single row/col to a vector if the parameter col has only one col
        # see https://radfordneal.wordpress.com/2008/08/20/design-flaws-in-r-2-%E2%80%94-dropped-dimensions/#comments
        df.temp = df[,col,drop=FALSE]
    } else {
        df.temp = df
    }

    if (length(n)==1){
        if (n==0) {
            # simply call complete.cases which might be faster
            result = df[complete.cases(df.temp),]
        } else {
            # credit: http://stackoverflow.com/a/30461945/2292993
            log <- apply(df.temp, 2, is.na)
            logindex <- apply(log, 1, function(x) sum(x) == n)
            result = df[logindex, ]
        }
    }

    if (length(n)==2){
        min = n[1]; max = n[2]
        log <- apply(df.temp, 2, is.na)
        logindex <- apply(log, 1, function(x) {sum(x) >= min && sum(x) <= max})
        result = df[logindex, ]
    }

    return(result)
}

8

datあなたのデータフレームと仮定すると、期待される出力は

1。rowSums

> dat[!rowSums((is.na(dat))),]
             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0   2    2    2    2
6 ENSG00000221312    0   1    2    3    2

2。lapply

> dat[!Reduce('|',lapply(dat,is.na)),]
             gene hsap mmul mmus rnor cfam
2 ENSG00000199674    0   2    2    2    2
6 ENSG00000221312    0   1    2    3    2

7

一般および利回りかなり可読コードの両方だ一つのアプローチは、使用することですfilter(dplyrパッケージの機能とその亜種をfilter_allfilter_atfilter_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(.))

4
delete.dirt <- function(DF, dart=c('NA')) {
  dirty_rows <- apply(DF, 1, function(r) !any(r %in% dart))
  DF <- DF[dirty_rows, ]
}

mydata <- delete.dirt(mydata)

上記の関数は、任意の列に「NA」があるデータフレームからすべての行を削除し、結果のデータを返します。のような複数の値をチェックし、関数paramを次のようNA?変更dart=c('NA')したい場合dart=c('NA', '?')


3

私の推測では、これはこの方法でよりエレガントに解決できると思います:

  m <- matrix(1:25, ncol = 5)
  m[c(1, 6, 13, 25)] <- NA
  df <- data.frame(m)
  library(dplyr) 
  df %>%
  filter_all(any_vars(is.na(.)))
  #>   X1 X2 X3 X4 X5
  #> 1 NA NA 11 16 21
  #> 2  3  8 NA 18 23
  #> 3  5 10 15 20 NA

6
これはで行を保持しますNA。:私は、どのようなOPが望んでいることだと思うdf %>% filter_all(all_vars(!is.na(.)))
asifzuba
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.