2014-09-10 8 views
12

Próbuję osiągnąć coś podobnego do this question, ale z wieloma wartościami, które muszą zostać zastąpione przez NA, oraz w dużym zbiorze danych.R: Zamień wiele wartości w wielu kolumnach ramek danych na NA

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

który generuje ten 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 

chciałbym zastąpić wszystkie wystąpienia, powiedzmy, 3 i 4 przez NA, ale tylko w kolumnach, które zaczynają się od "var".

wiem, że mogę użyć kombinacji [] operatorów, aby osiągnąć wynik chcę:

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 

Teraz moje pytania są następujące:

  1. Czy istnieje sposób, aby zrobić to w efektywny sposób, biorąc pod uwagę, że mój aktualny zbiór danych ma około 100 000 linii, a 400 z 500 zmiennych rozpoczyna z "var". Wydaje się (subiektywnie) spowalniać na moim komputerze, gdy używam techniki podwójnego nawiasu.
  2. Jak podejść do problemu, jeśli zamiast 2 wartości (3 i 4) zostaną zastąpione przez NA, miałem długą listę, powiedzmy, 100 różnych wartości? Czy istnieje sposób na określenie wielu wartości przy konieczności wykonywania niezdarnej serii warunków oddzielonych przez operatora?
+0

Można użyć ''% w%. – Roland

Odpowiedz

12

Można też to zrobić przy użyciu 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 

Niektóre szybkie benchmarking stosując próbkę mln wiersz danych sugeruje to szybciej niż inne odpowiedzi.

+0

+1 Czy mógłbyś opublikować testy porównawcze? Dzięki. – akrun

+0

Na moich danych przeszedłem od 17 sekund do 1,8 sekundy, dziesięciokrotny spadek w porównaniu do metody @akrun! Dzięki! – Peutch

+0

@akrun, dodałem benchmarki do mojej odpowiedzi. – A5C1D2H2I1M1N2O1R2T1

3

Oto podejście:

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

Można również zrobić:

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 
+0

Dziękuję. Z moimi danymi rozwiązanie to okazuje się 6 lub 7 razy szybsze niż metoda "sapply". – Peutch

+0

@Peutch - Wydaje mi się, że wycisnąłem z tego odrobinę więcej przy pomocy 'replace' - czy mógłbyś przetestować swoje rzeczywiste dane? – thelatemail

4

Nie timed tę opcję, ale Pisałem funkcję o nazwie makemeNA który jest częścią z my GitHub-only "SOfun" package.

Dzięki tej funkcji, podejście byłoby coś takiego:

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 

Funkcja wykorzystuje na.strings argumentu w type.convert zrobić konwersję do NA.


Zainstaluj pakiet z:

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

(lub ulubionego sposobu instalowania pakietów z GitHub).


Oto kilka testów porównawczych. Postanowiłem zrobić rzeczy interesujące i zastąpić wartości numeryczne i nieliczbowe NA, aby zobaczyć, jak rzeczy się porównują.

Oto przykładowe dane:

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

Oto funkcje do testu:

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 
} 

łamię się fun2 i fun3. Nie szaleję na punkcie fun2, ponieważ konwertuje wszystko na ten sam typ. Spodziewam się też, że wolniej będzie fun3.

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

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

Więc teraz przychodzi do mnie i 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 ty Thela!

+0

+1 Przekształciłem go w macierz, ponieważ w przykładzie pokazano kolumny "numeryczne" do porównania – akrun

0

Oto rozwiązanie 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 
Powiązane problemy