web-dev-qa-db-fra.com

R: remplacer plusieurs valeurs dans plusieurs colonnes d'images par NA

J'essaie de réaliser quelque chose de similaire à cette question mais avec plusieurs valeurs qui doivent être remplacées par NA et dans un grand jeu de données.

df <- data.frame(name = rep(letters[1:3], each = 3), foo=rep(1:9),var1 = rep(1:9), var2 = rep(3:5, each = 3))

qui génère cette dataframe:

df
  name foo var1 var2
1    a   1    1    3
2    a   2    2    3
3    a   3    3    3
4    b   4    4    4
5    b   5    5    4
6    b   6    6    4
7    c   7    7    5
8    c   8    8    5
9    c   9    9    5

Je voudrais remplacer toutes les occurrences de, par exemple, 3 et 4 par NA, mais uniquement dans les colonnes commençant par "var". 

Je sais que je peux utiliser une combinaison d'opérateurs [] pour obtenir le résultat souhaité:

df[,grep("^var[:alnum:]?",colnames(df))][ 
        df[,grep("^var[:alnum:]?",colnames(df))] == 3 |
        df[,grep("^var[:alnum:]?",colnames(df))] == 4
   ] <- NA

df
  name foo var1 var2
1    a   1    1    NA
2    a   2    2    NA
3    a   3    NA   NA
4    b   4    NA   NA
5    b   5    5    NA
6    b   6    6    NA
7    c   7    7    5
8    c   8    8    5
9    c   9    9    5

Maintenant, mes questions sont les suivantes:

  1. Existe-t-il un moyen de le faire de manière efficace, étant donné que mon jeu de données Actuel compte environ 100 000 lignes et que 400 variables sur 500 commencent Par "var". Cela semble (subjectivement) lent sur mon ordinateur lorsque j'utilise La technique des doubles crochets.
  2. Comment pourrais-je aborder le problème si Au lieu de 2 valeurs (3 et 4) à remplacer par NA, j'avais une longue liste De 100 valeurs différentes, par exemple? Est-il possible de spécifier plusieurs valeurs en faisant une série de conditions maladroites séparées par l'opérateur |?
13
Peutch

Vous pouvez également le faire en utilisant replace:

sel <- grepl("var",names(df))
df[sel] <- lapply(df[sel], function(x) replace(x,x %in% 3:4, NA) )
df

#  name foo var1 var2
#1    a   1    1   NA
#2    a   2    2   NA
#3    a   3   NA   NA
#4    b   4   NA   NA
#5    b   5    5   NA
#6    b   6    6   NA
#7    c   7    7    5
#8    c   8    8    5
#9    c   9    9    5

Une analyse rapide utilisant un échantillon de millions de lignes de données suggère que cela est plus rapide que les autres réponses.

12
thelatemail

Vous pouvez aussi faire:

col_idx <- grep("^var", names(df))
values <- c(3, 4)
m1 <- as.matrix(df[,col_idx])
m1[m1 %in% values] <- NA
df[col_idx]  <- m1
df
#   name foo var1 var2
#1    a   1    1   NA
#2    a   2    2   NA
#3    a   3   NA   NA
#4    b   4   NA   NA
#5    b   5    5   NA
#6    b   6    6   NA
#7    c   7    7    5
#8    c   8    8    5
#9    c   9    9    5
7
akrun

Je n'ai pas chronométré cette option, mais J'ai écrit une fonction appelée makemeNA qui fait partie de mon paquet "SOfun" réservé à GitHub .

Avec cette fonction, l’approche ressemblerait à ceci:

library(SOfun)

Cols <- grep("^var", names(df))
df[Cols] <- makemeNA(df[Cols], NAStrings = as.character(c(3, 4)))
df
#   name foo var1 var2
# 1    a   1    1   NA
# 2    a   2    2   NA
# 3    a   3   NA   NA
# 4    b   4   NA   NA
# 5    b   5    5   NA
# 6    b   6    6   NA
# 7    c   7    7    5
# 8    c   8    8    5
# 9    c   9    9    5

La fonction utilise l'argument na.strings dans type.convert pour effectuer la conversion en NA


Installez le paquet avec:

library(devtools)
install_github("SOfun", "mrdwab")

(ou votre méthode préférée d'installation de paquets à partir de GitHub).


Voici quelques analyses comparatives. J'ai décidé de rendre les choses intéressantes et de remplacer les valeurs numériques et non numériques par NA pour voir comment les choses se comparent.

Voici les exemples de données:

n <- 1000000
set.seed(1)
df <- data.frame(
  name1 = sample(letters[1:3], n, TRUE), 
  name2 = sample(letters[1:3], n, TRUE),
  name3 = sample(letters[1:3], n, TRUE),
  var1 = sample(9, n, TRUE), 
  var2 = sample(5, n, TRUE),
  var3 = sample(9, n, TRUE))

Voici les fonctions à tester:

fun1 <- function() {
  Cols <- names(df)
  df[Cols] <- makemeNA(df[Cols], NAStrings = as.character(c(3, 4, "a")))
  df
}

fun2 <- function() {
  values <- c(3, 4, "a")
  col_idx <- names(df)
  m1 <- as.matrix(df)
  m1[m1 %in% values] <- NA
  df[col_idx]  <- m1
  df
}

fun3 <- function() {
  values <- c(3, 4, "a")
  col_idx <- names(df)
  val_idx <- sapply(df[col_idx], "%in%", table = values)
  is.na(df[col_idx]) <- val_idx
  df
}

fun4 <- function() {
  sel <- names(df)
  df[sel] <- lapply(df[sel], function(x) 
    replace(x, x %in% c(3, 4, "a"), NA))
  df
}

J'éclate fun2 et fun3. Je ne suis pas fou de fun2 car il convertit tout dans le même type. Je m'attends aussi à ce que fun3 soit plus lent.

system.time(fun2())
#    user  system elapsed 
#    4.45    0.33    4.81 

system.time(fun3())
#    user  system elapsed 
#   34.31    0.38   34.74 

Alors maintenant, ça revient à moi et Thela ...

library(microbenchmark)
microbenchmark(fun1(), fun4(), times = 50)
# Unit: seconds
#    expr      min       lq   median       uq      max neval
#  fun1() 2.934278 2.982292 3.070784 3.091579 3.617902    50
#  fun4() 2.839901 2.964274 2.981248 3.128327 3.930542    50

Dang vous Thela!

4

Voici une approche:

# the values that should be replaced by NA
values <- c(3, 4)

# index of columns
col_idx <- grep("^var", names(df))
# [1] 3 4

# index of values (within these columns)
val_idx <- sapply(df[col_idx], "%in%", table = values)
#        var1  var2
#  [1,] FALSE  TRUE
#  [2,] FALSE  TRUE
#  [3,]  TRUE  TRUE
#  [4,]  TRUE  TRUE
#  [5,] FALSE  TRUE
#  [6,] FALSE  TRUE
#  [7,] FALSE FALSE
#  [8,] FALSE FALSE
#  [9,] FALSE FALSE

# replace with NA
is.na(df[col_idx]) <- val_idx

df
#   name foo var1 var2
# 1    a   1    1   NA
# 2    a   2    2   NA
# 3    a   3   NA   NA
# 4    b   4   NA   NA
# 5    b   5    5   NA
# 6    b   6    6   NA
# 7    c   7    7    5
# 8    c   8    8    5
# 9    c   9    9    5
3
Sven Hohenstein

Voici une solution Dplyr:

# Define replace function
repl.f <- function(x) ifelse(x%in%c(3,4), NA,x)

library(dplyr)
cbind(select(df, -starts_with("var")),
  mutate_each(select(df, starts_with("var")), funs(repl.f)))

  name foo var1 var2
1    a   1    1   NA
2    a   2    2   NA
3    a   3   NA   NA
4    b   4   NA   NA
5    b   5    5   NA
6    b   6    6   NA
7    c   7    7    5
8    c   8    8    5
9    c   9    9    5
0
Tomiris

Je pense que dplyr convient très bien à cette tâche.
En utilisant replace() comme suggéré par @thelatemail, vous pourriez faire quelque chose comme ceci:

library("dplyr")
df <- df %>% 
  mutate_at(vars(starts_with("var")),
            funs(replace(., . %in% c(3, 4), NA)))

df
#   name foo var1 var2
# 1    a   1    1   NA
# 2    a   2    2   NA
# 3    a   3   NA   NA
# 4    b   4   NA   NA
# 5    b   5    5   NA
# 6    b   6    6   NA
# 7    c   7    7    5
# 8    c   8    8    5
# 9    c   9    9    5
0
statmerkur