Verwijder rijen met alle of enkele NA’s (ontbrekende waarden) in data.frame

Ik wil de regels in dit dataframe verwijderen die:

a) bevatten NAen in alle kolommen. Hieronder staat mijn voorbeeldgegevensframe.

             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

Kortom, ik zou graag een dataframe willen hebben zoals het volgende.

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

b) bevatten NAen in slechts enkele kolommen, dus ik kan ook dit resultaat krijgen:

             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

Antwoord 1, autoriteit 100%

Controleer ook 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 is leuker om gewoon alle NA‘s te verwijderen. complete.cases staat gedeeltelijke selectie toe door alleen bepaalde kolommen van het dataframe op te nemen:

> 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

Uw oplossing werkt niet. Als u erop staat is.na te gebruiken, moet u iets doen als:

> 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

maar het gebruik van complete.cases is een stuk overzichtelijker en sneller.


Antwoord 2, autoriteit 25%

Probeer na.omit(your.data.frame). Wat betreft de tweede vraag, probeer deze als een andere vraag te posten (voor de duidelijkheid).


Antwoord 3, autoriteit 14%

tidyr heeft een nieuwe functie 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

Antwoord 4, autoriteit 8%

Ik geef de voorkeur aan de volgende manier om te controleren of rijen NA’s bevatten:

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

Dit retourneert een logische vector met waarden die aangeven of er een NA in een rij is. Je kunt het gebruiken om te zien hoeveel rijen je moet laten vallen:

sum(row.has.na)

en laat ze uiteindelijk vallen

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

Voor het filteren van rijen met bepaalde delen van NA’s wordt het een beetje lastiger (je kunt bijvoorbeeld ‘final[5:6]’ invoeren in ‘apply’).
Over het algemeen lijkt de oplossing van Joris Meys eleganter.


Antwoord 5, autoriteit 5%

Als je controle wilt over hoeveel NA’s geldig zijn voor elke rij, probeer dan deze functie. Voor veel enquêtedatasets kunnen te veel blanco vraagantwoorden de resultaten verpesten. Ze worden dus verwijderd na een bepaalde drempel. Met deze functie kunt u kiezen hoeveel NA’s de rij mag hebben voordat deze wordt verwijderd:

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

Standaard worden alle NA’s geëlimineerd:

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

Of specificeer het maximum aantal toegestane NA’s:

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

Antwoord 6, autoriteit 4%

Een andere optie als u meer controle wilt over hoe rijen als ongeldig worden beschouwd, is

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

Met behulp van het bovenstaande, dit:

             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

Wordt:

             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

…waar alleen rij 5 wordt verwijderd omdat het de enige rij is die NA’s bevat voor zowel rnor als cfam. De booleaanse logica kan dan worden gewijzigd om aan specifieke vereisten te voldoen.


Antwoord 7, autoriteit 4%

Als prestatie een prioriteit is, gebruik dan data.table en na.omit() met optionele param cols=.

na.omit.data.table is de snelste op mijn benchmark (zie hieronder), of het nu voor alle kolommen is of voor geselecteerde kolommen (OP-vraag deel 2).

Als je data.table niet wilt gebruiken, gebruik dan complete.cases().

Op een vanille data.frame, complete.cases is sneller dan na.omit() of dplyr::drop_na(). Merk op dat na.omit.data.frame cols= niet ondersteunt.

Benchmarkresultaat

Hier is een vergelijking van base (blauw), dplyr (roze) en data.table (geel) methoden voor het verwijderen van alle of geselecteerde ontbrekende waarnemingen, op fictieve dataset van 1 miljoen waarnemingen van 20 numerieke variabelen met onafhankelijke 5% kans om te ontbreken, en een subset van 4 variabelen voor deel 2.

Uw resultaten kunnen variëren op basis van lengte, breedte en schaarsheid van uw specifieke dataset.

Let op de logschaal op de y-as.

voer hier de afbeeldingsbeschrijving in

Benchmarkscript

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

Antwoord 8, autoriteit 2%

Met het dplyr-pakket kunnen we NA als volgt filteren:

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

Antwoord 9, autoriteit 2%

Hiermee worden de rijen geretourneerd die ten minste EEN niet-NA-waarde hebben.

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

Hiermee worden de rijen geretourneerd die ten minste TWEE niet-NA-waarde hebben.

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

Antwoord 10, autoriteit 2%

Voor je eerste vraag: ik heb een code waarmee ik me comfortabel voel om alle NA’s te verwijderen. Bedankt voor @Gregor om het eenvoudiger te maken.

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

Voor de tweede vraag is de code slechts een afwisseling van de vorige oplossing.

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

Merk op dat de -5 het aantal kolommen in uw gegevens is. Hierdoor worden rijen met alle NA’s geëlimineerd, aangezien de rijSommen optellen tot 5 en na aftrekken worden ze nullen. Deze keer is as.logisch nodig.


Antwoord 11

We kunnen hiervoor ook de subset-functie gebruiken.

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

Dit geeft alleen die rijen die geen NA hebben in zowel mmul als rnor


Antwoord 12

Een benadering die zowel algemeen is als redelijk leesbare code oplevert, is het gebruik van de filter()-functie en de across()-helperfuncties uit het {dplyr}-pakket.

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(.)))

Evenzo zijn er ook de variantfuncties in het dplyr-pakket (filter_all, filter_at, filter_if) die hetzelfde bereiken:

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(.))

Antwoord 13

Ik ben een synthesizer :). Hier heb ik de antwoorden gecombineerd in één functie:

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

Antwoord 14

Ervan uitgaande dat dat uw dataframe is, kan de verwachte output worden bereikt met

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

Antwoord 15

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)

Bovenstaande functie verwijdert alle rijen uit het gegevensframe met ‘NA’ in een kolom en retourneert de resulterende gegevens. Als u wilt controleren op meerdere waarden zoals NA en ? verander dart=c('NA') in functieparameter in dart=c('NA', '?')


Antwoord 16

Mijn gok is dat dit op deze manier eleganter kan worden opgelost:

  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

Antwoord 17

dplyr 1.0.4 introduceerde twee begeleidende functies voor filter: dit zijn if_any() en if_all(). De begeleidende functie if_all() is in dit geval bijzonder handig:

a) Om rijen te verwijderen die NA’s in alle kolommen bevatten

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

Deze regel bewaart alleen die rijen waar geen van de kolommen NA’s heeft.

b) Om rijen te verwijderen die NA’s bevatten in slechts enkele kolommen

cols_to_check = c("rnor", "cfam")
df %>% 
  filter(if_all(cols_to_check, ~ !is.na(.x)))

Deze regel controleert of een van de opgegeven kolommen (cols_to_check) NA’s heeft, en bewaart alleen die rijen waar dit niet het geval is.

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Other episodes