2012-07-03 21 views
65

Mam funkcję, która zwraca dwie wartości na liście. Obie wartości należy dodać do tabeli data.table w dwóch nowych kolumnach. Ocena funkcji jest kosztowna, więc chciałbym uniknąć konieczności obliczania funkcji dwukrotnie. Oto przykład:Dodaj wiele kolumn do R data.tabeli w jednym wywołaniu funkcji?

library(data.table) 
example(data.table) 
DT 
    x y v 
1: a 1 42 
2: a 3 42 
3: a 6 42 
4: b 1 4 
5: b 3 5 
6: b 6 6 
7: c 1 7 
8: c 3 8 
9: c 6 9 

Oto przykład mojej funkcji. Pamiętam, że powiedział to kosztowne obliczeniowej, na górze, że nie ma sposobu, aby wydedukować jedną wartość powrocie z innych podanych wartości (jak w poniższym przykładzie):

myfun <- function (y, v) 
{ 
ret1 = y + v 
ret2 = y - v 
return(list(r1 = ret1, r2 = ret2)) 
} 

Oto mój sposób na dodanie dwóch kolumn w jednej instrukcji . Trzeba jednak dwukrotnie zadzwonić do myfun:

DT[,new1:=myfun(y,v)$r1][,new2:=myfun(y,v)$r2] 

    x y v new1 new2 
1: a 1 42 43 -41 
2: a 3 42 45 -39 
3: a 6 42 48 -36 
4: b 1 4 5 -3 
5: b 3 5 8 -2 
6: b 6 6 12 0 
7: c 1 7 8 -6 
8: c 3 8 11 -5 
9: c 6 9 15 -3 

Jakieś sugestie, jak to zrobić? Mogę zapisać r2 w oddzielnym środowisku za każdym razem, gdy zadzwonię do myfun, po prostu potrzebuję sposobu na dodanie dwóch kolumn przez odniesienie na raz.

+0

Dlaczego Twoja funkcja nie przyjmuje ramki danych i nie zwraca bezpośrednio ramki danych? 'Myfun <- funkcję (r, t) { ret1 = Y + V ret2 = y - v zwrotny (lista (R1 = ret1 r2 = ret2)) } –

+3

@Etienne Bo kopiuje wejścia do tworzenia nowe wyjście. Florian używa 'data.table' dla swojej wydajności pamięci z dużymi zestawami danych; nie kopiuje w ogóle 'x',' y' lub 'v', nawet jeden raz. Pomyśl o zbiorach danych 20 GB w pamięci RAM. –

Odpowiedz

86

Można przechowywać dane wyjściowe rozmowy Funkcja:

z <- myfun(DT$y,DT$v) 
head(DT[,new1:=z$r1][,new2:=z$r2]) 
#  x y v new1 new2 
# [1,] a 1 42 43 -41 
# [2,] a 3 42 45 -39 
# [3,] a 6 42 48 -36 
# [4,] b 1 4 5 -3 
# [5,] b 3 5 8 -2 
# [6,] b 6 6 12 0 

ale wydaje się to również do pracy:

DT[, c("new1","new2") := myfun(y,v), with = FALSE] 

Nowość w data.table v1.8.3 na R-Forge, with = FALSE nie jest już potrzebny, dla wygody:

DT[, c("new1","new2") := myfun(y,v)] 

Do najświeższych wiadomości na żywo jest here.

+2

wow, ten drugi jest niesamowity, dzięki! po prostu uruchomiłem go za pomocą 'debug (myfun)', aby zobaczyć jak często jest wywoływany: raz. wspaniały. –

+1

+10 ode mnie. Właśnie podniosłem [FR # 2120] (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=2120&group_id=240&atid=978) na "Upuszczenie konieczności' z = FALSE" dla LHS z ': =' " –

+6

Należy pamiętać, że recykling list również jest wykonywany; np. 'c (" a "," b "," c "," d "): = lista (1,2)' wstawia 1 do 'a' i' c', a 2 do 'b' i' d '. Jeśli któraś z kolumn nie istnieje, zostaną one dodane przez odniesienie. Nie wiesz, jak użyteczny jest w praktyce recykling ': ='. To bardziej dla 'c (" a "," b "," c "): = NULL', który usuwa te 3 kolumny. Wewnętrznie jest to recykling wartości NULL do (semantycznej) listy o długości 3. –

-5

Dlaczego Twoja funkcja nie przyjmuje ramki danych i nie zwraca bezpośrednio ramki danych?

myfun <- function (DT) 
{ 
DT$ret1 = with(DT, y + v) 
DT$ret2 = with(DT, y - v) 
return(DT) 
} 
+25

Ponieważ to kopiuje całe 'DT', dwa razy. Florian używa 'data.table' dla swojej wydajności pamięci z dużymi zestawami danych; nie kopiuje w ogóle 'x',' y' lub 'v', nawet jeden raz. –

2

Aby zbudować na poprzedniej odpowiedzi, można użyć lapply w funkcję wyświetlania więcej niż jednej kolumnie. Możliwe jest wówczas użycie funkcji z większą liczbą kolumn tabeli data.table.

myfun <- function(a,b){ 
    res1 <- a+b 
    res2 <- a-b 
    list(res1,res2) 
} 

DT <- data.table(z=1:10,x=seq(3,30,3),t=seq(4,40,4)) 
DT 

## DT 
##  z x t 
## 1: 1 3 4 
## 2: 2 6 8 
## 3: 3 9 12 
## 4: 4 12 16 
## 5: 5 15 20 
## 6: 6 18 24 
## 7: 7 21 28 
## 8: 8 24 32 
## 9: 9 27 36 
## 10: 10 30 40 

col <- colnames(DT) 
DT[, paste0(c('r1','r2'),rep(col,each=2)):=unlist(lapply(.SD,myfun,z), 
                recursive=FALSE),.SDcols=col] 
## > DT 
##  z x t r1z r2z r1x r2x r1t r2t 
## 1: 1 3 4 2 0 4 2 5 3 
## 2: 2 6 8 4 0 8 4 10 6 
## 3: 3 9 12 6 0 12 6 15 9 
## 4: 4 12 16 8 0 16 8 20 12 
## 5: 5 15 20 10 0 20 10 25 15 
## 6: 6 18 24 12 0 24 12 30 18 
## 7: 7 21 28 14 0 28 14 35 21 
## 8: 8 24 32 16 0 32 16 40 24 
## 9: 9 27 36 18 0 36 18 45 27 
## 10: 10 30 40 20 0 40 20 50 30 
1

Odpowiedź nie może być używana, gdy funkcja nie jest wektoryzowana.

Na przykład w następującej sytuacji nie działa w zamierzony:

myfun <- function (y, v, g) 
{ 
    ret1 = y + v + length(g) 
    ret2 = y - v + length(g) 
    return(list(r1 = ret1, r2 = ret2)) 
} 
DT 
# v y     g 
# 1: 1 1     1 
# 2: 1 3    4,2 
# 3: 1 6    9,8,6 

DT[,c("new1","new2"):=myfun(y,v,g)] 
DT 
# v y  g new1 new2 
# 1: 1 1  1 5 3 
# 2: 1 3 4,2 7 5 
# 3: 1 6 9,8,6 10 8 

zawsze będzie dodać rozmiar kolumny g nie wielkość każdego wektora w g

roztworu w taki Przypadek to:

DT[, c("new1","new2") := data.table(t(mapply(myfun,y,v,g)))] 
DT 
# v y  g new1 new2 
# 1: 1 1  1 3 1 
# 2: 1 3 4,2 6 4 
# 3: 1 6 9,8,6 10 8 
Powiązane problemy