Zastosuj funkcję na podzbiorze kolumn (.SDcols) podczas stosowania innej funkcji na innej kolumnie (w grupach)

Jest to bardzo podobne do pytania stosującego wspólną funkcję do wielu kolumn data.table uning .SDcols odpowiedzi dokładnie tutaj .

Różnica polega na tym, że chciałbym jednocześnie zastosować inną funkcję na innej kolumnie, która nie jest częścią podzbioru .SD. Poniżej zamieszczam prosty przykład, aby pokazać moją próbę rozwiązania problemu:

dt = data.table(grp = sample(letters[1:3],100, replace = TRUE),
                v1 = rnorm(100), 
                v2 = rnorm(100), 
                v3 = rnorm(100))
sd.cols = c("v2", "v3")
dt.out = dt[, list(v1 = sum(v1),  lapply(.SD,mean)), by = grp, .SDcols = sd.cols]

Daje następujący błąd:

Error in `[.data.table`(dt, , list(v1 = sum(v1), lapply(.SD, mean)), by = grp,  
: object 'v1' not found

Teraz to ma sens, ponieważ v1 kolumna nie jest zawarte w podzbiorze kolumn, które muszą być ocenione jako pierwsze. Więc zbadałem dalej, włączając go do mojego podzbioru kolumn:

sd.cols = c("v1","v2", "v3")
dt.out = dt[, list(sum(v1), lapply(.SD,mean)), by = grp, .SDcols = sd.cols]

Teraz to nie powoduje błędu, ale dostarcza odpowiedź zawierającą 9 wierszy (dla 3 grup), z sumą powtórzoną trzykrotnie w kolumnie V1, a środki dla wszystkich 3 kolumn (zgodnie z oczekiwaniami, ale nie chcą) umieszczone w V2 Jak pokazano poniżej:

> dt.out 
   grp        V1                  V2
1:   c -1.070608 -0.0486639841313638
2:   c -1.070608  -0.178154270921521
3:   c -1.070608  -0.137625003604012
4:   b -2.782252 -0.0794929150464099
5:   b -2.782252  -0.149529237116445
6:   b -2.782252   0.199925178109264
7:   a  6.091355   0.141659419355985
8:   a  6.091355 -0.0272192037753071
9:   a  6.091355 0.00815760216214876

Rozwiązanie obejścia problemu w 2 krokach

Oczywiście można rozwiązać problem w wielu krokach, obliczając mean według grupy dla podzbioru kolumn i łącząc go z sum według grupy dla pojedynczej kolumny w następujący sposób:

dt.out1 = dt[, sum(v1), by = grp]
dt.out2 = dt[, lapply(.SD,mean), by = grp, .SDcols = sd.cols]
dt.out = merge(dt.out1, dt.out2, by = "grp")

> dt.out
   grp        V1         v2           v3
1:   a  6.091355 -0.0272192  0.008157602
2:   b -2.782252 -0.1495292  0.199925178
3:   c -1.070608 -0.1781543 -0.137625004

Jestem pewien, że to dość prosta rzecz, której mi brakuje, z góry dzięki za wszelkie wskazówki.

Author: Community, 2013-12-09

2 answers

Update: Issue #495 jest teraz rozwiązany za pomocą tego ostatniego commita , możemy teraz zrobić to dobrze:

require(data.table) # v1.9.7+
set.seed(1L)
dt = data.table(grp = sample(letters[1:3],100, replace = TRUE),
                v1 = rnorm(100), 
                v2 = rnorm(100), 
                v3 = rnorm(100))
sd.cols = c("v2", "v3")
dt.out = dt[, list(v1 = sum(v1),  lapply(.SD,mean)), by = grp, .SDcols = sd.cols]

Należy jednak pamiętać, że w tym przypadku v2 zostanie zwrócona jako lista. To dlatego, że robisz list(val, list()) skutecznie. To, co zamierzasz zrobić, to być może:

dt[, c(list(v1=sum(v1)), lapply(.SD, mean)), by=grp, .SDcols = sd.cols]
#    grp        v1          v2         v3
# 1:   a -6.440273  0.16993940  0.2173324
# 2:   b  4.304350 -0.02553813  0.3381612
# 3:   c  0.377974 -0.03828672 -0.2489067

Zobacz historię dla starszej odpowiedzi.

 25
Author: Arun,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-07 23:57:52

Spróbuj tego:

dt[,list(sum(v1), mean(v2), mean(v3)), by=grp]

W data.table, użycie list() w drugim argumencie pozwala opisać zestaw kolumn, które prowadzą do ostatecznego data.table.

Jeśli to coś warte, .SD może być dość powolne [^1], więc możesz chcieć tego uniknąć, chyba że naprawdę potrzebujesz wszystkich danych dostarczonych w podzbiorze data.table, Jak możesz dla bardziej wyrafinowanej funkcji.

Inną opcją, jeśli masz wiele kolumn dla .SDcols, byłoby wykonanie scalenia w jednej linii za pomocą data.table merge składnia.

Na przykład:

dt[, sum(v1), by=grp][dt[,lapply(.SD,mean), by=grp, .SDcols=sd.cols]]

Aby użyć merge z data.table, Musisz najpierw użyć setkey() na swoim data.table, aby wiedział, jak dopasować rzeczy.

Tak naprawdę, najpierw trzeba:

setkey(dt, grp)

Następnie możesz użyć powyższej linii, aby uzyskać równoważny wynik.

[^1]: uważam to za szczególnie prawdziwe, ponieważ liczba grup zbliża się do liczby całkowitych wierszy. Może się to zdarzyć na przykład wtedy, gdy kluczem jest indywidualny identyfikator i wiele osób mieć tylko jedną lub dwie obserwacje.

 6
Author: JBecker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-08 23:12:22