2016-08-11 21 views
5

mam rzadki tabeli danych, który wygląda tak:Computing na wielu nazw kolumn w data.table

data = data.table(
    var1 = c("a","",""), 
    var2 = c("","","c"), 
    var3 = c("a","b",""), 
    var4 = c("","b","") 
) 
     var1 var2 var3 var4 
    1: a   a  
    2:    b b 
    3:   c  

Chciałbym dodać kolumnę, która zawiera ciąg zer i jedynek, które zmienne są wskazujący przedstawić w każdym rzędzie, tak:

var1 var2 var3 var4 concat 
1: a   a  1|0|1|0 
2:    b b 0|0|1|1 
3:   c   0|1|0|0 

mogę dostać się do tego za pomocą następującego polecenia:

data[, concat := paste(
      as.integer(var1 != ""), 
      as.integer(var2 != ""), 
      as.integer(var3 != ""), 
      as.integer(var4 != ""), 
      sep = "|")] 

Jednakże, jeśli mam setki zmiennych, wolę użyć jakiegoś obliczenia, aby uzyskać pożądane wyrażenie. Być może coś opartego na paste0("var",1:4) lub przynajmniej wektor nazw kolumn. Jakieś sugestie?

Odpowiedz

4

same podstawowe podejścia jak powyżej:

data[ , concat := apply(.SD, 1, function(x) paste(+(x == ""), collapse = "|"))][] 
# var1 var2 var3 var4 concat 
# 1: a   a  0|1|0|1 
# 2:    b b 1|1|0|0 
# 3:   c   1|0|1|1 
+0

Jestem naprawdę zaskoczony, że zapętlenie nad każdym rzędem jest szybsze niż "wklejanie" ich razem jako kolumn. Ale wydaje się, że tak jest, niezależnie od tego, co moim zdaniem powinno się stać! :-P – thelatemail

2
data$concat <- apply(apply(data, 2, function(x) ifelse(x == "", 0, 1)), 1, function(x) paste(x, collapse="|")) 

Podział:

1) Dla każdej kolumny data sprawdź, czy elementem jest pusta, jeśli tak zwraca 0, w przeciwnym wypadku 1

apply(data, 2, function(x) ifelse(x == "", 0, 1)) 

Nazwijmy powrót z (1) zmienna concat. Dla każdego wiersza concat wklej wszystko razem z potokiem (|), oddzielając je. Ustaw nową kolumnę data w taki sposób.

apply(concat, 1, function(x) paste(x, collapse="|")) 
+0

Dobrze, ale sugestie: nie ma powodu, aby po prostu robić wszystko, co jest w rzędzie; jeśli wymagane jest użycie kolumn, wezwano do użycia metody lapply (.SD, "==", "") '; ['ifelse' jest powolny] (http://stackoverflow.com/questions/16275149/does-ifelse-really-calculate-oth-of-its-vectors- everyrytime-is-it-slow), więc unikaj tego ; i to ma znacznik '[data.table]', powinieneś być [przypisując przez odniesienie do ': ='] (https://rawgit.com/wiki/Rdatatable/data.table/vignettes/datatable-reference- semantics.html) – MichaelChirico

+0

Zapytałem cię o '.SD', nigdy wcześniej tego nie widziałem, podoba mi się twoja odpowiedź lepiej, ale właśnie napisałem to, co wypracowałem na własną rękę, które działało. – TomNash

1

Innym rozwiązaniem jest grupa danych po wierszu i przenieść każdy wiersz ze sobą:

data[,concat := paste0(as.integer(.SD != ""), collapse = "|"), by = 1:nrow(data)] 
data 
# var1 var2 var3 var4 concat 
#1: a   a  1|0|1|0 
#2:    b b 0|0|1|1 
#3:   c   0|1|0|0 
4

zmiana nie wymagając żadnego ugrupowania z każdego rzędu lub apply -ing w każdym rzędzie.

data[, concat := do.call(paste, c(lapply(.SD, function(x) (x!="")+0), sep="|")) ] 

# var1 var2 var3 var4 concat 
#1: a   a  1|0|1|0 
#2:    b b 0|0|1|1 
#3:   c   0|1|0|0 
+1

Zamiast wszystkich testów równości, po których następuje przymus (w każdej odpowiedzi), można by się domyślić, że "nchar". – Frank

+1

@Frank - prawda, ale zakłada, że ​​prawidłowe wartości w rzeczywistych danych to tylko jeden znak. Jeśli nie, przypuszczam, że mógłbyś zrobić 'pmin' lub coś, co by to przykryło, ale to tylko zwiększa złożoność. – thelatemail

2

Dzięki za wiele różnorodnych rozwiązań. Jestem pod wrażeniem!

Zrobiłem benchmarking na moim dużym zestawie danych, aby porównać czas działania kilku różnych podejść. Oto co znaleziono:

data[ , concat := apply(.SD, 1, function(x) paste(+(x == ""), collapse = "|"))] 

czas: 6 min 41 s

data[, concat := do.call(paste, c(lapply(.SD, function(x) (x!="")+0), sep="|")) ] 

czasu: 10 min 26 s

data[,concat := paste0(as.integer(.SD != ""), collapse = "|"), by = 1:nrow(data)] 

Czas:> 40 min (ręcznie zabite)

Powiązane problemy