Grupowanie przez wiele kolumn w dplyr, przy użyciu wprowadzania wektora łańcuchowego
Próbuję przenieść moje zrozumienie plyr do dplyr, ale nie mogę wymyślić, jak grupować po wielu kolumnach.
# make data with weird column names that can't be hard coded
data = data.frame(
asihckhdoydkhxiydfgfTgdsx = sample(LETTERS[1:3], 100, replace=TRUE),
a30mvxigxkghc5cdsvxvyv0ja = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
# get the columns we want to average within
columns = names(data)[-3]
# plyr - works
ddply(data, columns, summarize, value=mean(value))
# dplyr - raises error
data %.%
group_by(columns) %.%
summarise(Value = mean(value))
#> Error in eval(expr, envir, enclos) : index out of bounds
Czego mi brakuje, aby przetłumaczyć przykład plyr na składnię dplyr?
Edit 2017: Dplyr został zaktualizowany, więc dostępne jest prostsze rozwiązanie. Zobacz aktualnie wybraną odpowiedź.
8 answers
Ponieważ to pytanie zostało opublikowane, dplyr dodał wersje scoped group_by
(dokumentacja tutaj ). Pozwala to na korzystanie z tych samych funkcji, z których można korzystać z select
, Jak tak:
data = data.frame(
asihckhdoydkhxiydfgfTgdsx = sample(LETTERS[1:3], 100, replace=TRUE),
a30mvxigxkghc5cdsvxvyv0ja = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
# get the columns we want to average within
columns = names(data)[-3]
library(dplyr)
df1 <- data %>%
group_by_at(vars(one_of(columns))) %>%
summarize(Value = mean(value))
#compare plyr for reference
df2 <- plyr::ddply(data, columns, plyr::summarize, value=mean(value))
table(df1 == df2, useNA = 'ifany')
## TRUE
## 27
Wynik z przykładowego pytania jest zgodny z oczekiwaniami (Zobacz porównanie do plyr powyżej i wynik poniżej):
# A tibble: 9 x 3
# Groups: asihckhdoydkhxiydfgfTgdsx [?]
asihckhdoydkhxiydfgfTgdsx a30mvxigxkghc5cdsvxvyv0ja Value
<fctr> <fctr> <dbl>
1 A A 0.04095002
2 A B 0.24943935
3 A C -0.25783892
4 B A 0.15161805
5 B B 0.27189974
6 B C 0.20858897
7 C A 0.19502221
8 C B 0.56837548
9 C C -0.22682998
Zauważ, że ponieważ dplyr::summarize
usuwa tylko jedną warstwę grupowania na raz, nadal masz pewne grupowanie w wynikowym tibble (które może czasami złapać ludzi przez zaskoczenie później w dół linii). Jeśli chcesz być całkowicie bezpieczny przed nieoczekiwanym zachowaniem grupowania, zawsze możesz dodać %>% ungroup
do potoku po podsumowaniu.
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
2017-08-17 19:27:55
Aby napisać kod w całości, oto aktualizacja odpowiedzi Hadleya z nową składnią:
library(dplyr)
df <- data.frame(
asihckhdoydk = sample(LETTERS[1:3], 100, replace=TRUE),
a30mvxigxkgh = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
# Columns you want to group by
grp_cols <- names(df)[-3]
# Convert character vector to list of symbols
dots <- lapply(grp_cols, as.symbol)
# Perform frequency counts
df %>%
group_by_(.dots=dots) %>%
summarise(n = n())
Wyjście:
Source: local data frame [9 x 3]
Groups: asihckhdoydk
asihckhdoydk a30mvxigxkgh n
1 A A 10
2 A B 10
3 A C 13
4 B A 14
5 B B 10
6 B C 12
7 C A 9
8 C B 12
9 C C 10
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
2015-01-28 18:08:05
Wsparcie dla tego w dplyr jest obecnie dość słabe, w końcu myślę, że składnia będzie coś w stylu:
df %.% group_by(.groups = c("asdfgfTgdsx", "asdfk30v0ja"))
Ale tego pewnie nie będzie przez jakiś czas (bo muszę przemyśleć wszystkie konsekwencje).
W międzyczasie możesz użyć regroup()
, która pobiera listę symboli:
library(dplyr)
df <- data.frame(
asihckhdoydk = sample(LETTERS[1:3], 100, replace=TRUE),
a30mvxigxkgh = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
df %.%
regroup(list(quote(asihckhdoydk), quote(a30mvxigxkgh))) %.%
summarise(n = n())
Jeśli masz wektor znaków nazw kolumn, możesz przekonwertować je na właściwą strukturę za pomocą lapply()
i as.symbol()
:
vars <- setdiff(names(df), "value")
vars2 <- lapply(vars, as.symbol)
df %.% regroup(vars2) %.% summarise(n = n())
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
2014-01-20 20:42:30
String Specyfikacja kolumn w dplyr
jest teraz obsługiwana przez warianty funkcji dplyr
z nazwami kończącymi się podkreśleniem. Na przykład, odpowiadająca funkcji group_by
istnieje funkcja group_by_
, która może przyjmować argumenty łańcuchowe. ta winieta szczegółowo opisuje składnię tych funkcji.
Poniższy fragment rozwiązuje problem, który pierwotnie postawił @sharoz (zwróć uwagę na konieczność wypisania argumentu .dots
):
# Given data and columns from the OP
data %>%
group_by_(.dots = columns) %>%
summarise(Value = mean(value))
(zauważ, że dplyr używa teraz operatora %>%
i %.%
jest przestarzały).
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
2014-11-19 15:59:56
Dopóki dplyr nie ma pełnego wsparcia dla argumentów łańcuchowych, być może ten gist jest przydatny:
Https://gist.github.com/skranz/9681509
Zawiera kilka funkcji wrappera, takich jak s_group_by, s_mutate, s_filter, itp., które używają argumentów łańcuchowych. Można je mieszać z normalnymi funkcjami dplyr. Na przykład
cols = c("cyl","gear")
mtcars %.%
s_group_by(cols) %.%
s_summarise("avdisp=mean(disp), max(disp)") %.%
arrange(avdisp)
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
2014-03-21 08:04:42
To działa, jeśli przekażesz go obiektom (cóż, nie jesteś, ale...) zamiast jako wektor postaci:
df %.%
group_by(asdfgfTgdsx, asdfk30v0ja) %.%
summarise(Value = mean(value))
> df %.%
+ group_by(asdfgfTgdsx, asdfk30v0ja) %.%
+ summarise(Value = mean(value))
Source: local data frame [9 x 3]
Groups: asdfgfTgdsx
asdfgfTgdsx asdfk30v0ja Value
1 A C 0.046538002
2 C B -0.286359899
3 B A -0.305159419
4 C A -0.004741504
5 B B 0.520126476
6 C C 0.086805492
7 B C -0.052613078
8 A A 0.368410146
9 A B 0.088462212
Gdzie df
było twoje data
.
?group_by
says:
...: variables to group by. All tbls accept variable names, some
will also accept functons of variables. Duplicated groups
will be silently dropped.
Które interpretuję nie jako wersje znaków imion, ale jak byś się do nich odnosił w foo$bar
; bar
nie jest tu cytowany. Albo jak odnosisz się do zmiennych we wzorze: foo ~ bar
.
@Arun wspomina również, że możesz zrobić:
df %.%
group_by("asdfgfTgdsx", "asdfk30v0ja") %.%
summarise(Value = mean(value))
Ale nie można przekazać czegoś, co unevaluated {[22] } nie jest nazwą zmiennej w obiekcie data.
Zakładam, że jest to spowodowane wewnętrznymi metodami, których używa Hadley do wyszukiwania rzeczy, które przekazujesz za pomocą argumentu ...
.
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
2014-01-18 20:00:44
data = data.frame(
my.a = sample(LETTERS[1:3], 100, replace=TRUE),
my.b = sample(LETTERS[1:3], 100, replace=TRUE),
value = rnorm(100)
)
group_by(data,newcol=paste(my.a,my.b,sep="_")) %>% summarise(Value=mean(value))
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
2014-10-24 16:53:22
Jeden (malutki) przypadek, którego brakuje w odpowiedziach tutaj, że chciałem wyraźnie powiedzieć, jest wtedy, gdy zmienne do grupy przez są generowane dynamicznie w połowie strumienia w potoku:
library(wakefield)
df_foo = r_series(rnorm, 10, 1000)
df_foo %>%
# 1. create quantized versions of base variables
mutate_each(
funs(Quantized = . > 0)
) %>%
# 2. group_by the indicator variables
group_by_(
.dots = grep("Quantized", names(.), value = TRUE)
) %>%
# 3. summarize the base variables
summarize_each(
funs(sum(., na.rm = TRUE)), contains("X_")
)
To w zasadzie pokazuje, jak używać grep
w połączeniu z group_by_(.dots = ...)
, aby to osiągnąć.
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-11-07 07:40:12