Data.frame içinde tüm veya bazı NA'lara (eksik değerler) sahip satırları kaldırın


852

Bu veri çerçevesindeki satırları kaldırmak istiyorum:

a) tüm sütunlarda s içerir NA. Aşağıda örnek veri çerçevem ​​var.

             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

Temel olarak, aşağıdaki gibi bir veri çerçevesi almak istiyorum.

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

b) sadece bazı sütunlarda s içerirNA , bu yüzden bu sonucu da alabilirim:

             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

Yanıtlar:


1063

Ayrıca complete.casesşunları da kontrol edin :

> 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.omitsadece hepsini kaldırmak için daha güzel NA. complete.casesveri çerçevesinin yalnızca belirli sütunlarını ekleyerek kısmi seçime izin verir:

> 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

Çözümünüz işe yaramıyor. Kullanmakta ısrar ediyorsanız is.na, şöyle bir şey yapmanız gerekir:

> 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

ancak complete.caseskullanımı çok daha net ve hızlıdır.


8
Sondaki virgülün önemi nedir final[complete.cases(final),]?
hertzsprung

6
@hertzsprung Sütunları değil, satırları seçmeniz gerekir. Bunu başka nasıl yapardın?
Joris Meys

4
Basit bir olumsuzlaması var complete.casesmı? Satırları atmak yerine NA'larla tutmak istersem? final[ ! complete.cases(final),]işbirliği yapmıyor ...
tumultous_rooster

2
finalçerçevesi değişkeni nedir?
Morse

1
@Prateek gerçekten de öyle.
Joris Meys

256

Deneyin na.omit(your.data.frame). İkinci soruya gelince, başka bir soru olarak (açıklık için) göndermeyi deneyin.


na.omit satırları düşürür ancak satır numaralarını korur. Düzgün numaralandırılması için bunu nasıl düzeltirsiniz?
Ayı

3
@ Satır sayılarını umursamıyorsanız, sadece yapın rownames(x) <- NULL.
Roman Luštrik

herhangi bir sütunda yer alan na.omit()satırları düşürdüğünü lütfen unutmayınNA
Victor Maxwell

116

tidyryeni bir işlevi vardır 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
Borular ile arasında gerçek bir bağlantı yoktur drop_na. Örneğin df %>% drop_na(), df %>% na.omit()ve drop_na(df)temel olarak eşdeğerdir.
Ista

4
@Ista katılmıyorum. na.omitatlanan vakaların indeksleri gibi ek bilgiler ekler ve daha da önemlisi sütun seçmenize izin vermez - burası drop_naparlar.
lukeA

3
Tabii ki, bunun hiçbirinin borularla bir ilgisi olmadığı. Sen kullanabilirsiniz na.omitkullanabilirsiniz tıpkı veya boru olmadan drop_naveya boru olmadan.
Ista

1
Doğru, borularla hiçbir ilgisi yok. drop_na () sadece diğerleri gibi bir işlevdir ve bu nedenle doğrudan veya bir boru kullanılarak çağrılabilir. Ne yazık ki, drop_na (), belirtilen diğer yöntemlerin aksine, hayvanat bahçesi veya xts nesne türlerinde kullanılamaz. Bu bazıları için bir sorun olabilir.
Dave

Doğru, bu yüzden cevabı düzenledim, böylece borulardan bahsetmeyecek.
Arthur Yip

91

Satırların herhangi bir NA içerip içermediğini kontrol etmek için aşağıdaki yolu tercih ederim:

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

Bu, arka arkaya NA olup olmadığını gösteren değerleri olan mantıksal vektör döndürür. Kaç satır bırakmanız gerektiğini görmek için kullanabilirsiniz:

sum(row.has.na)

ve sonunda onları bırak

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

NA'ların belirli bir kısmına sahip satırları filtrelemek için biraz daha zorlaşır (örneğin, 'uygulamak için' final [, 5: 6] 'besleyebilirsiniz). Genel olarak, Joris Meys'in çözümü daha zarif görünüyor.


2
Bu son derece yavaş. Örneğin yukarıda belirtilen complete.cases () çözümünden çok daha yavaştır. En azından benim durumumda xts verisi.
Dave

3
rowSum(!is.na(final))daha uygun görünüyorapply()
sindri_baldur

45

Satırların geçersiz sayıldığı konusunda daha fazla kontrol sahibi olmak istiyorsanız başka bir seçenek

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

Yukarıdakileri kullanarak, bu:

             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

Oluyor:

             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

... her iki rnorAND için de NA içeren tek satır olduğu için yalnızca 5. satır kaldırılır cfam. Boole mantığı daha sonra belirli gereksinimlere uyacak şekilde değiştirilebilir.


5
ancak, her birini yazmadan çok sayıda sütunu kontrol etmek istiyorsanız bunu nasıl kullanabilirsiniz? [, 4: 100] aralık sonunu kullanabilirsiniz?
Herman Toothrot

40

Her satır için kaç NA geçerli olacağını kontrol etmek istiyorsanız, bu işlevi deneyin. Birçok anket veri seti için çok fazla boş soru yanıtı, sonuçları bozabilir. Böylece belirli bir eşikten sonra silinirler. Bu işlev, satır silinmeden önce kaç NA olabileceğini seçmenizi sağlar:

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

Varsayılan olarak, tüm NA'ları ortadan kaldıracaktır:

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

Veya izin verilen maksimum NA sayısını belirtin:

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

Performans bir öncelikse data.tableve na.omit()isteğe bağlı parametre ile kullanın cols=.

na.omit.data.table tüm sütunlar için mi yoksa belirli sütunlar için mi (OP soru bölüm 2), karşılaştırmamdaki en hızlı (aşağıya bakın).

Kullanmak istemiyorsanız data.table,complete.cases() .

Bir vanilyada data.frame, veya complete.casesdaha hızlıdır . Bildirim desteklemiyorna.omit()dplyr::drop_na()na.omit.data.framecols= .

Karşılaştırma sonucu

İşte baz (mavi), dplyr(pembe) vedata.table bağımsız 5% eksik olma olasılığı olan 20 sayısal değişkenin 1 milyon gözleminin kavramsal veri kümesinde, tümünü veya eksik eksikleri atmak için (sarı) yöntemlerin ve Bölüm 2 için 4 değişkenli altküme.

Sonuçlarınız, veri kümenizin uzunluğuna, genişliğine ve genişliğine bağlı olarak değişebilir.

Y eksenindeki günlük ölçeğini not edin.

resim açıklamasını buraya girin

Karşılaştırma komut dosyası

#-------  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 paketini kullanarak NA'yı aşağıdaki gibi filtreleyebiliriz:

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

1
Bu, yaklaşık 10.000 kat daha yavaş performans gösterirdrop_na()
Zimano

17

Bu, en az ONE NA olmayan değeri olan satırları döndürür.

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

Bu, en az İKİ olmayan NA değerine sahip satırları döndürür.

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

16

İlk sorunuz için, tüm NA'lardan kurtulmak için rahat olduğum bir kodum var. Daha basit hale getirmek için @Gregor için teşekkürler.

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

İkinci soru için, kod önceki çözümden sadece bir alternatiftir.

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

-5'in verilerinizdeki sütun sayısı olduğuna dikkat edin. Bu, rowSums değeri 5'e kadar eklediğinden ve çıkarıldıktan sonra sıfır olduklarından tüm NA'larla satırları ortadan kaldıracaktır. Bu kez, aslojik olarak gereklidir.


final [as.logical ((rowSums (is.na (final)) - ncol (final))),] evrensel bir cevap için
Ferroao

14

Bunun için altküme işlevini de kullanabiliriz.

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

Bu yalnızca hem mmul hem de rnor'da NA olmayan satırları verecektir.


9

Ben bir sentezleyiciyim :). Burada cevapları tek bir fonksiyonda birleştirdim:

#' 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

datVeri çerçeveniz olarak kabul edilirse , beklenen çıktıya

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

Her iki general ve oldukça okunabilir kod verir bir yaklaşım kullanmaktır filterfonksiyon ve dplyr paketinde varyasyonlarını ( 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(.))

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)

Yukarıdaki işlev, herhangi bir sütunda 'NA' olan veri çerçevesindeki tüm satırları siler ve elde edilen verileri döndürür. Gibi birden fazla değeri kontrol etmek NAve fonksiyon parametresini ?değiştirmek istiyorsanızdart=c('NA')dart=c('NA', '?')


3

Benim tahminim, bu şekilde daha zarif bir şekilde çözülebilir:

  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
bu ile satırlar korunur NA. OP'nin ne istediğini düşünüyorum:df %>% filter_all(all_vars(!is.na(.)))
asifzuba
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.