문자열 벡터 입력을 사용하여 dplyr에서 여러 열로 그룹화


157

plyr에 대한 이해를 dplyr으로 옮기려고하지만 여러 열로 그룹화하는 방법을 알 수 없습니다.

# 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

plyr 예제를 dplyr-esque 구문으로 변환하기 위해 무엇을 놓치고 있습니까?

편집 2017 : Dplyr이 업데이트되었으므로 더 간단한 솔루션을 사용할 수 있습니다. 현재 선택된 답변을 참조하십시오.


3
Google이 최고 였으므로 여기에 도착했습니다. 당신은 group_by_지금 설명 할 수 있습니다vignette("nse")
James Owers

3
@kungfujam : 열 쌍이 아닌 첫 번째 열만 그룹화하는 것으로 보입니다
sharoz

1
를 사용해야 .dots합니다. 다음은 @hadley의 답변에서 수정 된 솔루션입니다.df %>% group_by_(.dots=list(quote(asihckhdoydk), quote(a30mvxigxkgh))) %>% summarise(n = n())
James Owers

1
아래 답변에 전체 코드를 입력하십시오
James Owers

1
누군가가 의견에 대한 답변에서 지적했듯이 목표는 하드 코드 된 열 이름을 요구하지 않는 것입니다.
sharoz

답변:


52

이 질문이 게시 된 이후 dplyr은 범위가 지정된 버전 group_by( documentation here )을 추가했습니다. 이를 통해 다음과 같은 기능을 사용할 수 있습니다 select.

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 

예제 질문의 결과는 예상대로입니다 (위의 plyr와 아래의 결과 비교 참조).

# 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

dplyr::summarize한 번에 하나의 그룹화 계층 만 제거하기 때문에 결과로 생성되는 티블에서 그룹화가 계속 진행됩니다 (이후에는 나중에 줄을 서서 사람들을 잡을 수 있음). 예기치 않은 그룹화 동작으로부터 완전히 안전 %>% ungroup하려면 요약 후 항상 파이프 라인에 추가 할 수 있습니다 .


0.7.0따옴표-인용 시스템을 여러 열과 함께 사용할 수 있도록 업데이트 합니까?
JelenaČuklina

4
.dots인수를 다음 group_by()과 같이 사용할 수도 있습니다 data %>% group_by(.dots = columns) %>% summarize(value = mean(value))..
Paul Rougieux

one_of()여기에 전화를 해야합니까? 이 컨텍스트에서는 표현식이에 대한 호출로 래핑되므로 중복되는 것으로 생각합니다 vars().
knowah

예 @Khashir,이 답변이 아직도 오른쪽에 대한 호출 당신 @knowah 작동 one_of()이 상황에서 중복
Empiromancer

1
@Sos를 사용하여 여러 컬럼에 걸쳐 기능을 적용하려면 select구문을, 새로운보고 across기능 : dplyr.tidyverse.org/reference/across.html 귀하의 경우를, 그것은 같이 보일 것이다summarize(across(all_of(c(''value_A", "value_B")), mean))
Empiromancer의

102

코드를 완전히 작성하기 위해 Hadley의 답변에 대한 새로운 구문이 업데이트되었습니다.

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

산출:

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

1
이것은 여전히 ​​수식에서 열 이름을 하드 코딩하는 것 같습니다. 질문의 요점은 문자열을 사용하여 입력하지 않아도되는 방법입니다 asihckhdoydk.
Gregor Thomas

1
인수 dots <- lapply(names(df)[-3], function(x) as.symbol(x))를 만들기 위해 솔루션을 업데이트했습니다.dots
James Owers

4
이러한 답변을 정리하는 .dots=것이 중요한 단계였습니다. 누군가 group_by통화에 필요한 이유를 잘 알고 있다면 이 답변을 편집 할 수 있습니까? 지금은 조금 까다 롭습니다.
Andrew

12
vignette("nse")인용 할 수있는 인용 방법에는 수식, 인용 및 문자의 세 가지가 있음을 나타냅니다. 어떤 환경에서 벗어날 지 걱정이 없다면 다음과 같이 도망 갈 수 있습니다.group_by_(.dots=grp_cols)
Ari B. Friedman

58

dplyr에서 이것에 대한 지원은 현재 매우 약합니다. 결국 구문은 다음과 같습니다.

df %.% group_by(.groups = c("asdfgfTgdsx", "asdfk30v0ja"))

그러나 아마도 한동안은 없을 것입니다 (모든 결과를 생각해야하기 때문에).

그 동안에는 regroup()기호 목록을 사용 하는을 사용할 수 있습니다 .

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

열 이름의 문자형 벡터가있는 경우 lapply()and 를 사용하여 올바른 구조로 변환 할 수 있습니다 as.symbol().

vars <- setdiff(names(df), "value")
vars2 <- lapply(vars, as.symbol)

df %.% regroup(vars2) %.% summarise(n = n())

6
as.symbol그것을 해결합니다. 감사! 개발에 도움이되는 경우 :이 시나리오는 나에게 매우 일반적인 시나리오입니다. 다른 변수의 모든 조합에 대한 수치 결과를 집계합니다.
sharoz

분명히 이것은이 특정 예에서만 작동하며 다른 것은 아닙니다.
Paulo E. Cardoso

3
나는 원래 이것을 답으로 표시했지만 dplyr로 업데이트하면 kungfujam의 대답이 작동합니다.
sharoz

regroup(더 이상 버전 0.4.3부터) 더 이상 사용되지 않습니다.
Berk U.

27

밑줄로 끝나는 이름을 가진 함수의 dplyr변형을 통해 열의 문자열 지정이 지원됩니다 dplyr. 예를 들어, group_by함수에 대응하여 group_by_문자열 인수를 취할 수 있는 함수가 있습니다. 이 비 네트 에는 이러한 함수의 구문이 자세히 설명되어 있습니다.

다음 스 니펫은 @sharoz가 원래 제기 한 문제를 완전히 해결합니다 ( .dots인수 를 작성할 필요가 있음 ).

# Given data and columns from the OP

data %>%
    group_by_(.dots = columns) %>%
    summarise(Value = mean(value))

dplyr은 이제 %>%연산자를 사용하므로 %.%더 이상 사용되지 않습니다.


17

dplyr이 문자열 인수를 완전히 지원할 때까지이 요지가 유용 할 수 있습니다.

https://gist.github.com/skranz/9681509

문자열 인수를 사용하는 s_group_by, s_mutate, s_filter 등과 같은 래퍼 함수를 ​​포함합니다. 일반적인 dplyr 기능과 함께 사용할 수 있습니다. 예를 들어

cols = c("cyl","gear")
mtcars %.%
  s_group_by(cols) %.%  
  s_summarise("avdisp=mean(disp), max(disp)") %.%
  arrange(avdisp)

11

캐릭터 벡터가 아닌 객체 (잘, 그렇지는 않지만 ...)를 전달하면 작동합니다.

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

어디 df있었어 data?

?group_by 말한다 :

 ...: variables to group by. All tbls accept variable names, some
      will also accept functons of variables. Duplicated groups
      will be silently dropped.

나는 이름의 문자 버전이 아니라 당신이 그것을 어떻게 참조 하는지를 해석합니다 foo$bar. bar여기에 인용되지 않았습니다. 또는 수식에서 변수를 참조하는 방법 : foo ~ bar.

@Arun은 또한 할 수 있다고 언급합니다.

df %.%
    group_by("asdfgfTgdsx", "asdfk30v0ja") %.%
    summarise(Value = mean(value))

그러나 평가되지 않은 것은 데이터 객체의 변수 이름이 아닌 것을 전달할 수 없습니다 .

나는 이것이 Hadley가 당신이 ...인수 를 통해 전달하는 것들을 찾는 데 사용하는 내부 방법 때문이라고 생각합니다 .


1
@Arun 감사합니다. 나는 그것을 알아 차리지 못했지만 너무 말이됩니다. 나는 당신과 당신의 의견을 인용하여 이와 관련하여 메모를 추가했습니다.
Gavin Simpson

4
불행히도 열 이름을 하드 코딩하지 않아도됩니다. 지정하지 않고이 작업을 수행하려고합니다.
sharoz

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

4

여기에 대한 답변에서 누락 된 한 가지 작은 사례는 그룹화 할 변수가 파이프 라인에서 동적으로 미드 스트림으로 생성되는 경우입니다.

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

이것은 기본적으로 이것을 달성하기 위해 grep함께 사용하는 방법을 보여줍니다 group_by_(.dots = ...).


3

.dots함수에 문자 벡터 입력으로 인수 를 사용하는 일반적인 예 dplyr::group_by:

iris %>% 
    group_by(.dots ="Species") %>% 
    summarise(meanpetallength = mean(Petal.Length))

또는 그룹화 변수의 하드 코딩 된 이름이없는 경우 (OP의 요청에 따라) :

iris %>% 
    group_by(.dots = names(iris)[5]) %>% 
    summarise_at("Petal.Length", mean)

OP의 예를 들면 다음과 같습니다.

data %>% 
    group_by(.dots =names(data)[-3]) %>% 
    summarise_at("value", mean)

대명사, 준 따옴표, 문턱 및 단 정도를 설명 하는 프로그래밍에 관한 dplyr 비네팅을 참조하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.