Je voudrais supprimer les lignes dans ce cadre de données qui:
a) contient NA
s dans toutes les colonnes. Voici mon exemple de trame de données.
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
En gros, j'aimerais obtenir un bloc de données tel que celui-ci.
gene hsap mmul mmus rnor cfam
2 ENSG00000199674 0 2 2 2 2
6 ENSG00000221312 0 1 2 3 2
b) contient NA
s dans seulement quelques colonnes, je peux donc aussi obtenir le résultat suivant:
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
Vérifiez également 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
_ est plus pratique pour supprimer uniquement tous les NA
. _complete.cases
_ permet la sélection partielle en n'incluant que certaines colonnes du cadre de données:
_> 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
_
Votre solution ne peut pas fonctionner. Si vous insistez pour utiliser _is.na
_, vous devez faire quelque chose comme:
_> 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
_
mais utiliser _complete.cases
_ est beaucoup plus clair et plus rapide.
Essayez na.omit(your.data.frame)
. Quant à la deuxième question, essayez de l'afficher comme une autre question (pour plus de clarté).
tidyr
a une nouvelle fonction 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
_
Je préfère suivre la méthode suivante pour vérifier si les lignes contiennent des NA:
row.has.na <- apply(final, 1, function(x){any(is.na(x))})
Cela renvoie un vecteur logique avec des valeurs indiquant s'il y a ou non un NA dans une ligne. Vous pouvez l'utiliser pour voir combien de lignes vous devez supprimer:
sum(row.has.na)
et finalement les laisser tomber
final.filtered <- final[!row.has.na,]
Pour filtrer les lignes avec certaines parties de NA, cela devient un peu plus compliqué (par exemple, vous pouvez alimenter "final [ 5: 6]" pour "appliquer"). En règle générale, la solution de Joris Meys semble être plus élégante.
Une autre option si vous souhaitez mieux contrôler l’invalidité des lignes est
final <- final[!(is.na(final$rnor)) | !(is.na(rawdata$cfam)),]
En utilisant ce qui précède, ceci:
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
Devient:
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
... où seule la ligne 5 est supprimée puisqu'il s'agit de la seule ligne contenant des NA pour les deux rnor
ET cfam
. La logique booléenne peut ensuite être modifiée pour répondre à des exigences spécifiques.
Si vous souhaitez contrôler le nombre d'AN valides pour chaque ligne, essayez cette fonction. Pour de nombreux ensembles de données d’enquête, trop de réponses en blanc peuvent ruiner les résultats. Ils sont donc supprimés après un certain seuil. Cette fonction vous permettra de choisir le nombre d'AN que la ligne peut avoir avant d'être supprimée:
delete.na <- function(DF, n=0) {
DF[rowSums(is.na(DF)) <= n,]
}
Par défaut, toutes les AN seront éliminées:
delete.na(final)
gene hsap mmul mmus rnor cfam
2 ENSG00000199674 0 2 2 2 2
6 ENSG00000221312 0 1 2 3 2
Ou spécifiez le nombre maximum d'AN autorisé:
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
data.table
_ et na.omit()
avec le paramètre optionnel _cols=
_.na.omit.data.table
est le plus rapide de mon repère (voir ci-dessous), que ce soit pour toutes les colonnes ou pour certaines colonnes (question OP 2).
data.table
_, utilisez complete.cases()
.Sur une vanille _data.frame
_, complete.cases
est plus rapide que na.omit()
ou dplyr::drop_na()
. Notez que _na.omit.data.frame
_ ne prend pas en charge _cols=
_.
Voici une comparaison des méthodes de base (bleu), dplyr
(rose) et _data.table
_ (jaune) pour supprimer toutes ou sélectionner des observations manquantes dans un jeu de données notionnel de 1 million d'observations de 20 variables numériques avec indépendante 5% de probabilité d’être manquant et un sous-ensemble de 4 variables pour la partie 2.
Vos résultats peuvent varier en fonction de la longueur, de la largeur et de la parcimonie de votre ensemble de données.
Notez l'échelle du journal sur l'axe des 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))
)
_
En utilisant le package dplyr, nous pouvons filtrer NA comme suit:
dplyr::filter(df, !is.na(columnname))
Cela renverra les lignes qui ont au moins UNE valeur autre que NA.
final[rowSums(is.na(final))<length(final),]
Cela renverra les lignes qui ont au moins DEUX valeurs non NA.
final[rowSums(is.na(final))<(length(final)-1),]
Pour votre première question, j'ai un code avec lequel je suis à l'aise pour me débarrasser de toutes les NA. Merci pour @Gregor de simplifier les choses.
final[!(rowSums(is.na(final))),]
Pour la deuxième question, le code est simplement une alternative à la solution précédente.
final[as.logical((rowSums(is.na(final))-5)),]
Notez que -5 correspond au nombre de colonnes de vos données. Cela éliminera les lignes avec toutes les NA, puisque la somme de somme est égale à 5 et qu'elles deviennent des zéros après la soustraction. Cette fois-ci, comme si logique était nécessaire.
Nous pouvons également utiliser la fonction de sous-ensemble pour cela.
finalData<-subset(data,!(is.na(data["mmul"]) | is.na(data["rnor"])))
Cela donnera uniquement les lignes qui n'ont pas NA dans les deux valeurs mmul et rnor
Je suis un synthétiseur :). Ici, j'ai combiné les réponses en une seule fonction:
#' 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)
}
En supposant que dat
en tant que votre cadre de données, le résultat attendu peut être obtenu à l'aide de
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)
La fonction ci-dessus supprime toutes les lignes du cadre de données contenant "NA" dans une colonne et renvoie les données résultantes. Si vous voulez vérifier plusieurs valeurs comme NA
et ?
changez Dart=c('NA')
dans la fonction param en Dart=c('NA', '?')
Je suppose que cela pourrait être résolu de manière plus élégante de cette manière
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
Une approche à la fois générale et qui produit un code assez lisible consiste à utiliser la fonction filter
et ses variantes dans le package dplyr (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(.))
na.omit fera dans ce cas. Une fois que cela est fait, vous pouvez visualiser votre jeu de données en utilisant un graphique de valeurs manquantes.