`dplyr`에서 동적 변수 이름 사용


168

dplyr::mutate()데이터 프레임에서 여러 개의 새 열을 만드는 데 사용하고 싶습니다 . 열 이름과 해당 내용은 동적으로 생성되어야합니다.

홍채의 데이터 예 :

library(dplyr)
iris <- tbl_df(iris)

Petal.Width변수 에서 새 열을 변경하는 함수를 만들었습니다 .

multipetal <- function(df, n) {
    varname <- paste("petal", n , sep=".")
    df <- mutate(df, varname = Petal.Width * n)  ## problem arises here
    df
}

이제 열을 만들기 위해 루프를 만듭니다.

for(i in 2:5) {
    iris <- multipetal(df=iris, n=i)
}

그러나 mutate는 varname이 리터럴 변수 이름이라고 생각하기 때문에 루프는 4 개 대신 petal.2-petal.5라는 하나의 새 변수 (varname이라고 함) 만 만듭니다.

mutate()동적 이름을 변수 이름으로 사용하려면 어떻게 해야합니까?


1
나는 변이를 고집하지 않고 가능한지 묻고있다. 어쩌면 내가 모르는 약간의 트릭 일 수도 있습니다. 다른 방법이 있다면 들으십시오.
Timm S.

나는 lazyeval 패키지 에서 볼 공간 이 있다고 생각 합니다
baptiste


16
비 네트에는조차 언급되어 있지 않으며 mutate_다른 기능에서는 그것을 사용하는 방법이 분명하지 않습니다.
nacnudus

답변:


191

변수 이름을 문자 값으로 동적으로 작성하므로 표준 data.frame 인덱싱을 사용하여 열 이름에 문자 값을 지정할 수 있습니다. 예를 들면 다음과 같습니다.

multipetal <- function(df, n) {
    varname <- paste("petal", n , sep=".")
    df[[varname]] <- with(df, Petal.Width * n)
    df
}

mutate함수를 사용하면 명명 된 매개 변수를 통해 새 열의 이름을 쉽게 지정할 수 있습니다. 그러나 명령을 입력 할 때 이름을 알고 있다고 가정합니다. 열 이름을 동적으로 지정하려면 명명 된 인수도 작성해야합니다.


dplyr 버전> = 0.7

최신 버전 dplyr(0.7)은 :=매개 변수 이름을 동적으로 할당 하는 데 사용하여이 작업을 수행 합니다. 함수를 다음과 같이 작성할 수 있습니다.

# --- dplyr version 0.7+---
multipetal <- function(df, n) {
    varname <- paste("petal", n , sep=".")
    mutate(df, !!varname := Petal.Width * n)
}

자세한 내용은 사용 가능한 설명서를 참조하십시오 vignette("programming", "dplyr").


dplyr (> = 0.3 및 <0.7)

약간 이전 버전 dplyr(> = 0.3 <0.7)에서는 많은 기능에 대한 "표준 평가"대안을 사용하도록 권장했습니다. 자세한 내용은 비표준 평가 비 네트를 참조하십시오 ( vignette("nse")).

따라서 여기에 대한 대답은 사용 mutate_()하기보다는 사용 하는 것입니다 mutate().

# --- dplyr version 0.3-0.5---
multipetal <- function(df, n) {
    varname <- paste("petal", n , sep=".")
    varval <- lazyeval::interp(~Petal.Width * n, n=n)
    mutate_(df, .dots= setNames(list(varval), varname))
}

dplyr <0.3

이것은 dplyr질문이 처음 제기되었을 때 존재했던 이전 버전에서도 가능합니다 . 그것은의 신중한 사용을 필요로 quote하고을 setName:

# --- dplyr versions < 0.3 ---
multipetal <- function(df, n) {
    varname <- paste("petal", n , sep=".")
    pp <- c(quote(df), setNames(list(quote(Petal.Width * n)), varname))
    do.call("mutate", pp)
}

24
감사합니다. 도움이됩니다. btw, 나는 항상 정말 극적인 변수를 만듭니다.
Timm S.

27
헤헤 아마 내가 한동안 내가 만든 가장 좋아하는 오타 중 하나 일 것입니다. 나는 그것을 떠날 것 같아요.
MrFlick

1
do.call()rpubs.com/hadley/do-call2 : 아마도 당신이 생각하는 것을하지 않을 것입니다 . dplyr의 dev 버전에서 nse 비네팅을 참조하십시오.
hadley

4
따라서 귀하의 포인트 @hadley를 이해 하면 목록에서 do.call사용 do.call("mutate")하고 인용 하도록 위 의 내용을 업데이트했습니다 df. 그게 당신이 제안한 것입니까? 그리고의 lazyeval버전이 dplyr출시 된 버전 일 때 mutate_(df, .dots= setNames(list(~Petal.Width * n), varname))더 나은 솔루션이 될까요?
MrFlick

1
과제의 왼쪽뿐만 아니라 오른쪽에도 변수 열 머리글이 필요한 경우 어떻게합니까? 예 : mutate(df, !!newVar := (!!var1 + !!var2) / 2)작동하지 않습니다 :(
Mario Reutter

55

dplyr( 0.6.02017 년 4 월 대기 중)의 새로운 릴리스에서는 할당하지 :=않고 ( !!)를 지정하여 변수를 열 이름으로 전달하고 ( ) 변수를 열 이름으로 전달할 수도 있습니다.

 library(dplyr)
 multipetalN <- function(df, n){
      varname <- paste0("petal.", n)
      df %>%
         mutate(!!varname := Petal.Width * n)
 }

 data(iris)
 iris1 <- tbl_df(iris)
 iris2 <- tbl_df(iris)
 for(i in 2:5) {
     iris2 <- multipetalN(df=iris2, n=i)
 }   

@ iris1 '에 multipetal적용된 @MrFlick을 기반으로 출력 확인

identical(iris1, iris2)
#[1] TRUE

26

많은 시행 착오 끝에 패턴 UQ(rlang::sym("some string here")))이 문자열 및 dplyr 동사 작업에 실제로 유용하다는 것을 알았습니다 . 많은 놀라운 상황에서 작동하는 것 같습니다.

의 예는 다음과 같습니다 mutate. 두 개의 열을 더하는 함수를 만들고 싶습니다. 두 개의 열 이름을 모두 문자열로 전달합니다. 할당 연산자와 함께이 패턴을 사용하여이를 수행 할 수 있습니다 :=.

## Take column `name1`, add it to column `name2`, and call the result `new_name`
mutate_values <- function(new_name, name1, name2){
  mtcars %>% 
    mutate(UQ(rlang::sym(new_name)) :=  UQ(rlang::sym(name1)) +  UQ(rlang::sym(name2)))
}
mutate_values('test', 'mpg', 'cyl')

이 패턴은 다른 dplyr기능과도 작동 합니다. 여기 있습니다 filter:

## filter a column by a value 
filter_values <- function(name, value){
  mtcars %>% 
    filter(UQ(rlang::sym(name)) != value)
}
filter_values('gear', 4)

또는 arrange:

## transform a variable and then sort by it 
arrange_values <- function(name, transform){
  mtcars %>% 
    arrange(UQ(rlang::sym(name)) %>%  UQ(rlang::sym(transform)))
}
arrange_values('mpg', 'sin')

의 경우 select패턴을 사용할 필요가 없습니다. 대신 다음을 사용할 수 있습니다 !!.

## select a column 
select_name <- function(name){
  mtcars %>% 
    select(!!name)
}
select_name('mpg')

귀하의 팁은 잘 작동하지만 약간의 문제가 있습니다. 초기 열 myCol을 URL로 변경하고 (예 myColInitialValue:) 데이터 프레임 끝에 있는 이전 열 을 df새 이름으로 복사합니다 . 그러나 which(colnames(df)=='myCol')의 열 #를 다시 보냅니다 myColInitialValue. reprex를 찾지 못해 아직 문제를 작성하지 않았습니다. 내 목표는의 escape매개 변수입니다 DT::datatable(). 나는 그것을 escape=FALSE기다리는 데 사용 합니다. 상수를 사용하면 작동하지 않지만 DT 패키지 도 잘못된 # 열을 얻는 것처럼 보입니다. :)
phili_b


동적 변수가 원인이 아닌 것 같습니다. (btw reprex 추가)
phili_b

이 답변에 감사드립니다! 다음은 내가 어떻게 사용했는지에 대한 아주 간단한 예입니다.varname = sym("Petal.Width"); ggplot(iris, aes(x=!!varname)) + geom_histogram()
bdemarest

이것은 !! varname이 작동하지 않는 수식 내부에서 나를 위해 일했습니다.
daknowles

12

여기 또 다른 버전이 있으며 약간 더 간단합니다.

multipetal <- function(df, n) {
    varname <- paste("petal", n, sep=".")
    df<-mutate_(df, .dots=setNames(paste0("Petal.Width*",n), varname))
    df
}

for(i in 2:5) {
    iris <- multipetal(df=iris, n=i)
}

> head(iris)
Sepal.Length Sepal.Width Petal.Length Petal.Width Species petal.2 petal.3 petal.4 petal.5
1          5.1         3.5          1.4         0.2  setosa     0.4     0.6     0.8       1
2          4.9         3.0          1.4         0.2  setosa     0.4     0.6     0.8       1
3          4.7         3.2          1.3         0.2  setosa     0.4     0.6     0.8       1
4          4.6         3.1          1.5         0.2  setosa     0.4     0.6     0.8       1
5          5.0         3.6          1.4         0.2  setosa     0.4     0.6     0.8       1
6          5.4         3.9          1.7         0.4  setosa     0.8     1.2     1.6       2

8

함께 rlang 0.4.0우리는 곱슬 곱슬 연산자 (이 {{}}) 매우 쉽게이를 수있다.

library(dplyr)
library(rlang)

iris1 <- tbl_df(iris)

multipetal <- function(df, n) {
   varname <- paste("petal", n , sep=".")
   mutate(df, {{varname}} := Petal.Width * n)
}

multipetal(iris1, 4)

# A tibble: 150 x 6
#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species petal.4
#          <dbl>       <dbl>        <dbl>       <dbl> <fct>     <dbl>
# 1          5.1         3.5          1.4         0.2 setosa      0.8
# 2          4.9         3            1.4         0.2 setosa      0.8
# 3          4.7         3.2          1.3         0.2 setosa      0.8
# 4          4.6         3.1          1.5         0.2 setosa      0.8
# 5          5           3.6          1.4         0.2 setosa      0.8
# 6          5.4         3.9          1.7         0.4 setosa      1.6
# 7          4.6         3.4          1.4         0.3 setosa      1.2
# 8          5           3.4          1.5         0.2 setosa      0.8
# 9          4.4         2.9          1.4         0.2 setosa      0.8
#10          4.9         3.1          1.5         0.1 setosa      0.4
# … with 140 more rows

또한 인용 / 따옴표없는 변수 이름을 전달하여 열 이름으로 지정할 수 있습니다.

multipetal <- function(df, name, n) {
   mutate(df, {{name}} := Petal.Width * n)
}

multipetal(iris1, temp, 3)

# A tibble: 150 x 6
#   Sepal.Length Sepal.Width Petal.Length Petal.Width Species  temp
#          <dbl>       <dbl>        <dbl>       <dbl> <fct>   <dbl>
# 1          5.1         3.5          1.4         0.2 setosa  0.6  
# 2          4.9         3            1.4         0.2 setosa  0.6  
# 3          4.7         3.2          1.3         0.2 setosa  0.6  
# 4          4.6         3.1          1.5         0.2 setosa  0.6  
# 5          5           3.6          1.4         0.2 setosa  0.6  
# 6          5.4         3.9          1.7         0.4 setosa  1.2  
# 7          4.6         3.4          1.4         0.3 setosa  0.900
# 8          5           3.4          1.5         0.2 setosa  0.6  
# 9          4.4         2.9          1.4         0.2 setosa  0.6  
#10          4.9         3.1          1.5         0.1 setosa  0.3  
# … with 140 more rows

그것은 같은 작동합니다

multipetal(iris1, "temp", 3)

4

나는 또한 답변을 검색 할 때이 항목에 왔기 때문에 이것을 조금 증가시키는 답변을 추가하고 있습니다. 이것은 거의 필요한 것이었지만 조금 더 필요했습니다. R 게으른 짤막한.

문자열에서 Date 객체로 변환하려는 데이터 프레임과 열 이름 (문자열) 벡터를 취할 수있는 함수를 만들고 싶었습니다. 만드는 법을 알 수 없었습니다as.Date()문자열 인 인수를 가져 와서 열로 변환 아래 표시된 것처럼 수행했습니다.

아래는 SE mutate ( mutate_())와 .dots인수를 통해이 작업을 수행 한 방법 입니다. 더 나은 비판은 환영합니다.

library(dplyr)

dat <- data.frame(a="leave alone",
                  dt="2015-08-03 00:00:00",
                  dt2="2015-01-20 00:00:00")

# This function takes a dataframe and list of column names
# that have strings that need to be
# converted to dates in the data frame
convertSelectDates <- function(df, dtnames=character(0)) {
    for (col in dtnames) {
        varval <- sprintf("as.Date(%s)", col)
        df <- df %>% mutate_(.dots= setNames(list(varval), col))
    }
    return(df)
}

dat <- convertSelectDates(dat, c("dt", "dt2"))
dat %>% str

3

대화 형 사용을 위해 dplyr을 사용하는 것이 좋지만 lazyeval :: interp (), setNames 등의 해결 방법을 사용하기 위해 농구대를 거쳐야하기 때문에 dplyr을 사용 하여이 작업을 수행하는 것은 매우 까다로운 방법입니다.

여기에 기본 R을 사용하는 간단한 버전이 있습니다.이 기능은 루프를 함수 안에 넣고 @MrFlicks의 솔루션을 확장하는 것이 더 직관적입니다.

multipetal <- function(df, n) {
   for (i in 1:n){
      varname <- paste("petal", i , sep=".")
      df[[varname]] <- with(df, Petal.Width * i)
   }
   df
}
multipetal(iris, 3) 

2
+1, dplyr비 대화식 설정에서 여전히 많이 사용하지만 함수 내에서 variabel 입력과 함께 사용하면 매우 복잡한 구문이 사용됩니다.
Paul Hiemstra

3

friendlyeval깔끔한 eval API와 신규 / 캐주얼 dplyr사용자를 위한 문서를 제공하는 패키지 를 즐길 수 있습니다 .

mutate열 이름으로 취급 하려는 문자열을 작성 중 입니다. 따라서 friendlyeval당신은 쓸 수 있습니다 :

multipetal <- function(df, n) {
  varname <- paste("petal", n , sep=".")
  df <- mutate(df, !!treat_string_as_col(varname) := Petal.Width * n)
  df
}

for(i in 2:5) {
  iris <- multipetal(df=iris, n=i)
}

후드 호출 rlang에서 확인하는 함수 varname는 열 이름으로 유효한 것입니다.

friendlyeval 코드는 언제든지 RStudio addin을 사용하여 동등한 일반 깔끔한 평가 코드로 변환 할 수 있습니다.

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