Am'd cum pentru a elimina liniile în acest cadru de date care:
o) contin `NA pe toate coloanele. Mai jos este exemplul meu cadru de date.
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
Practic, am'd place pentru a obține un cadru de date, cum ar fi următoarele.
gene hsap mmul mmus rnor cfam
2 ENSG00000199674 0 2 2 2 2
6 ENSG00000221312 0 1 2 3 2
b) contin `NA în doar câteva coloane, așa că am putea obține, de asemenea, acest rezultat:
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
De asemenea, a verifica complet.cazuri
:
> 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.omite
este mai frumos pentru eliminarea tuturor " NA " 's. complet.cazuri
permite selecție parțială, prin includerea doar a anumitor coloane de dataframe:
> 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
Solutia ta poate't de lucru. Daca insisti sa folosesti este.na
, atunci trebuie să faci ceva de genul:
> 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
dar folosind complete.cazuri` este destul de mult mai clară, și mai repede.
tidyr
are o nouă funcție 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
Prefer felul următor pentru a verifica dacă rânduri conțin orice NAs:
row.has.na <- apply(final, 1, function(x){any(is.na(x))})
Aceasta returnează valoarea logică vector cu valori care indică dacă există orice NA într-un rând. Puteți să-l utilizați pentru a vedea cât de multe rânduri'll trebuie să renunțe:
sum(row.has.na)
în cele din urmă și fixați-le
final.filtered <- final[!row.has.na,]
Pentru filtrare rânduri cu o anumită parte de NAs devine un pic mai complicată (de exemplu, puteți alimenta 'final[,5:6]' la 'aplica'). În general, Joris Meys' soluție pare a fi mai elegant.
O altă opțiune dacă doriți un control mai mare asupra cum rânduri sunt considerate a fi incorecte este
final <- final[!(is.na(final$rnor)) | !(is.na(rawdata$cfam)),]
Folosind cele de mai sus, acest lucru:
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
Devine:
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
...în cazul în care numai rândul 5 este eliminat, deoarece acesta este doar rândul care conține NAs pentru ambele rnor " ȘI " cfam`. În logica booleană poate fi apoi schimbate pentru a se potrivi cerințelor specifice.
Daca vrei control asupra cât de multe Snc sunt valabile pentru fiecare rând, încercați această funcție. Pentru mai multe serii de date din sondaje, prea multe blank răspunsuri întrebare poate ruina rezultate. Deci, acestea sunt șterse după un anumit prag. Această funcție vă va permite să alegeți cât de multe NAs la rând poate avea înainte de a-l's șters:
delete.na <- function(DF, n=0) {
DF[rowSums(is.na(DF)) <= n,]
}
În mod implicit, acesta va elimina toate Snc:
delete.na(final)
gene hsap mmul mmus rnor cfam
2 ENSG00000199674 0 2 2 2 2
6 ENSG00000221312 0 1 2 3 2
Sau specifica numărul maxim de NAs permis:
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
cu opțional param
cols=`.na.omit.data.table
este cel mai rapid de pe meu de referință (a se vedea mai jos), fie pentru toate coloanele sau pentru a selecta coloane (OP întrebare partea a 2-a).
, utilizat
complet.cazuri()`.Pe o vanilie de date.cadru, [
complete.cases](https://www.rdocumentation.org/packages/stats/versions/3.4.3/topics/complete.cases) este mai rapid decât [
na.omite()](https://www.rdocumentation.org/packages/stats/versions/3.4.3/topics/na.fail) sau [
dplyr::drop_na()](https://www.rdocumentation.org/packages/tidyr/versions/0.8.0/topics/drop_na). Observați că
na.omite.date.cadrunu suport
cols=`.
Aici este o comparație de bază (albastru), dplyr
(roz), și datele.masa (galben), metode de scadere fie toate sau selectați lipsă de observații, pe noțională set de date de 1 milion de observații de 20 numerice variabile independente cu 5% probabilitatea de a fi dispărut, și un subset de 4 variabile pentru partea a 2-a.
Rezultatele pot varia în funcție de lungimea, lățimea, și sparsity de anumit set de date.
Notă scară logaritmică pe axa 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))
)
Pentru prima întrebare, am un cod care sunt confortabil cu a scăpa de toate NAs. Multumesc pentru @Gregor pentru a face mai simplu.
final[!(rowSums(is.na(final))),]
Pentru cea de-a doua întrebare, codul este doar o alternanță de soluția anterioară.
final[as.logical((rowSums(is.na(final))-5)),]
Observa -5 este numărul de coloane de date. Acest lucru va elimina rânduri cu tot cu NAs, deoarece rowSums adaugă până la 5 și au devenit zerouri după scădere. De data asta, ca.logic este necesar.
Eu sunt un sintetizator:). Aici am combinat răspunsurile într-o singură funcție:
#' 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)
}
Presupunând că dat
ca dataframe, rezultatul așteptat poate fi realizat folosind
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
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)
Funcția de mai sus șterge toate rândurile din cadru de date care are 'NA' în orice coloană și returnează rezultate de date. Dacă doriți să verificați pentru mai multe valori, cum ar fi NA "și"? "schimbare" dart=c('NA') în funcție param
dart=c('NA', '?')`
O abordare care's atât generale, cât și a randamentelor destul de lizibil codul este de a utiliza "filtru" funcția și variantele sale în dplyr pachet (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(.))
Părerea mea este că acest lucru ar putea fi mai elegant rezolvate în acest fel
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