Ik wil de regels in dit dataframe verwijderen die:
a) bevatten NA
en 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 NA
en 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.
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.