data.frame 열 이름을 함수에 전달


119

나는 data.frame ( x) 및 a 를 받아들이는 함수를 작성하려고 column합니다. 이 함수는 x에서 일부 계산을 수행하고 나중에 다른 data.frame을 반환합니다. 열 이름을 함수에 전달하는 모범 사례 방법을 고수하고 있습니다.

두 개의 최소 예제 fun1fun2아래 는 예제로 x$column사용하여에서 작업을 수행 할 수있는 원하는 결과를 생성합니다 max(). 그러나 둘 다 겉보기에 (적어도 나에게는) 우아하지 않은 것에 의존합니다.

  1. 전화를 걸 substitute()거나eval()
  2. 열 이름을 문자형 벡터로 전달해야합니다.

fun1 <- function(x, column){
  do.call("max", list(substitute(x[a], list(a = column))))
}

fun2 <- function(x, column){
  max(eval((substitute(x[a], list(a = column)))))
}

df <- data.frame(B = rnorm(10))
fun1(df, "B")
fun2(df, "B")

fun(df, B)예를 들어 함수를로 호출하고 싶습니다 . 내가 고려했지만 시도하지 않은 다른 옵션 :

  • 통과 column열 번호의 정수로. 나는 이것이 피할 것이라고 생각한다 substitute(). 이상적으로는 함수가 둘 중 하나를 받아 들일 수 있습니다.
  • with(x, get(column)),하지만 작동하더라도 여전히 필요하다고 생각합니다. substitute
  • 의 사용을 확인 formula()하고 match.call()내가 가진 많은 경험을 가지고, 어느 쪽도 아니합니다.

Subquestion : do.call()더 선호 eval()합니까?

답변:


108

열 이름을 직접 사용할 수 있습니다.

df <- data.frame(A=1:10, B=2:11, C=3:12)
fun1 <- function(x, column){
  max(x[,column])
}
fun1(df, "B")
fun1(df, c("B","A"))

대체, 평가 등을 사용할 필요가 없습니다.

원하는 함수를 매개 변수로 전달할 수도 있습니다.

fun1 <- function(x, column, fn) {
  fn(x[,column])
}
fun1(df, "B", max)

또는를 사용 [[하면 한 번에 하나의 열을 선택할 수도 있습니다.

df <- data.frame(A=1:10, B=2:11, C=3:12)
fun1 <- function(x, column){
  max(x[[column]])
}
fun1(df, "B")

14
열 이름을 문자열이 아닌 전달하는 방법이 있습니까?
kmm

2
문자로 인용 된 열 이름이나 열의 정수 인덱스를 전달해야합니다. 그냥 통과 B하면 B가 객체 자체라고 가정합니다.
Shane

내가 참조. 복잡한 대체, eval 등으로 어떻게 끝났는지 잘 모르겠습니다.
kmm

3
감사! 나는 [[해결책이 나를 위해 일한 유일한 해결책이라는 것을 알았 습니다.
EcologyTom

1
안녕하세요 @Luis, 이 답변을
EcologyTom

78

이 답변은 기존 답변과 동일한 요소를 많이 다루지 만,이 문제 (열 이름을 함수에 전달)는 좀 더 포괄적으로 다루는 답변이 있기를 원할 정도로 자주 발생합니다.

매우 간단한 데이터 프레임이 있다고 가정합니다.

dat <- data.frame(x = 1:4,
                  y = 5:8)

우리는 새로운 열을 생성하는 기능을 쓰고 싶은 z컬럼의 합 xy.

여기서 매우 일반적인 걸림돌은 자연 스럽지만 잘못된 시도가 종종 다음과 같이 보인다는 것입니다.

foo <- function(df,col_name,col1,col2){
      df$col_name <- df$col1 + df$col2
      df
}

#Call foo() like this:    
foo(dat,z,x,y)

여기서 문제 df$col1는 표현식을 평가하지 않는다는 것 col1입니다. 단순히 df라는 열을 찾습니다 col1. 이 동작은 ?Extract"재귀 (목록 형) 개체"섹션에 설명되어 있습니다.

가장 간단하고 가장 자주 권장되는 솔루션은 단순히에서 $로 전환 [[하여 함수 인수를 문자열로 전달하는 것입니다.

new_column1 <- function(df,col_name,col1,col2){
    #Create new column col_name as sum of col1 and col2
    df[[col_name]] <- df[[col1]] + df[[col2]]
    df
}

> new_column1(dat,"z","x","y")
  x y  z
1 1 5  6
2 2 6  8
3 3 7 10
4 4 8 12

이것은 가장 망치기 어려운 방법이기 때문에 종종 "모범 사례"로 간주됩니다. 열 이름을 문자열로 전달하는 것은 가능한 한 모호하지 않습니다.

다음 두 가지 옵션이 더 고급입니다. 많은 인기있는 패키지가 이러한 종류의 기술을 사용하지만이를 잘 사용 하려면 미묘한 복잡성과 예상치 못한 실패 지점을 도입 할 수 있으므로 더 많은주의와 기술이 필요합니다. Hadley의 Advanced R 책 의이 섹션은 이러한 문제 중 일부에 대한 훌륭한 참고 자료입니다.

당신이 경우 정말 모든 따옴표를 입력에서 사용자를 저장하려면, 하나의 옵션을 사용하여 문자열로 베어, 인용 부호로 둘러싸이지 않은 열 이름을 변환 할 수 있습니다 deparse(substitute()):

new_column2 <- function(df,col_name,col1,col2){
    col_name <- deparse(substitute(col_name))
    col1 <- deparse(substitute(col1))
    col2 <- deparse(substitute(col2))

    df[[col_name]] <- df[[col1]] + df[[col2]]
    df
}

> new_column2(dat,z,x,y)
  x y  z
1 1 5  6
2 2 6  8
3 3 7 10
4 4 8 12

솔직히 말하면 어리석은 일입니다.에서와 같은 일을하고 있기 때문입니다 new_column1. 단지 이름을 문자열로 변환하기위한 추가 작업을 많이합니다.

마지막으로, 정말 멋지게 추가 할 두 열의 이름을 전달하는 것보다 더 유연하고 두 변수의 다른 조합을 허용하는 것으로 결정할 수 있습니다. 이 경우 eval()두 개의 열을 포함하는 표현식 을 사용 하는 것이 좋습니다.

new_column3 <- function(df,col_name,expr){
    col_name <- deparse(substitute(col_name))
    df[[col_name]] <- eval(substitute(expr),df,parent.frame())
    df
}

재미로 나는 여전히 deparse(substitute())새 열의 이름을 사용하고 있습니다. 여기에서 다음이 모두 작동합니다.

> new_column3(dat,z,x+y)
  x y  z
1 1 5  6
2 2 6  8
3 3 7 10
4 4 8 12
> new_column3(dat,z,x-y)
  x y  z
1 1 5 -4
2 2 6 -4
3 3 7 -4
4 4 8 -4
> new_column3(dat,z,x*y)
  x y  z
1 1 5  5
2 2 6 12
3 3 7 21
4 4 8 32

따라서 짧은 대답은 기본적으로 data.frame 열 이름을 문자열로 전달하고 [[단일 열을 선택 하는 데 사용 하는 것입니다. 만에 탐구하기 시작 eval, substitute당신이 정말 당신이 무슨 일을하는지 알고있는 경우 등.


1
이것이 왜 선택된 베스트 답변이 아닌지 잘 모르겠습니다.
Ian

나도! 훌륭한 설명!
Alfredo G Marquez

22

개인적으로 열을 문자열로 전달하는 것은 매우 추한 것이라고 생각합니다. 나는 다음과 같은 것을 좋아합니다.

get.max <- function(column,data=NULL){
    column<-eval(substitute(column),data, parent.frame())
    max(column)
}

결과는 다음과 같습니다.

> get.max(mpg,mtcars)
[1] 33.9
> get.max(c(1,2,3,4,5))
[1] 5

data.frame의 사양이 선택 사항임을 확인하십시오. 열의 기능으로 작업 할 수도 있습니다.

> get.max(1/mpg,mtcars)
[1] 0.09615385

9
따옴표를 사용하는 것이 추악하다고 생각하는 습관에서 벗어나야합니다. 그것들을 사용하지 않는 것은 추합니다! 왜? 대화식으로 만 사용할 수있는 함수를 만들었 기 때문에 프로그래밍하기가 매우 어렵습니다.
hadley

27
더 나은 방법을 보여줘서 기쁘지만 이것과 qplot (x = mpg, data = mtcars)의 차이를 보지 못했습니다. ggplot2는 열을 문자열로 전달하지 않으며 더 나은 방법이라고 생각합니다. 왜 이것이 대화식으로 만 사용될 수 있다고 말합니까? 어떤 상황에서 바람직하지 않은 결과를 초래합니까? 프로그래밍이 어떻게 더 어렵습니까? 게시물 본문에 더 유연한 방법을 표시합니다.
Ian Fellows

4
5 년 후-) .. 필요한 이유 : parent.frame ()?
mql4beginner 2015-06-21

15
7 년 후 : 여전히 추악한 따옴표를 사용하지 않습니까?
Spacedman

11

또 다른 방법은 tidy evaluation접근 방식 을 사용 하는 것입니다. 데이터 프레임의 열을 문자열 또는 베어 열 이름으로 전달하는 것은 매우 간단합니다. tidyeval 여기 에 대해 자세히 알아 보십시오 .

library(rlang)
library(tidyverse)

set.seed(123)
df <- data.frame(B = rnorm(10), D = rnorm(10))

열 이름을 문자열로 사용

fun3 <- function(x, ...) {
  # capture strings and create variables
  dots <- ensyms(...)
  # unquote to evaluate inside dplyr verbs
  summarise_at(x, vars(!!!dots), list(~ max(., na.rm = TRUE)))
}

fun3(df, "B")
#>          B
#> 1 1.715065

fun3(df, "B", "D")
#>          B        D
#> 1 1.715065 1.786913

베어 열 이름 사용

fun4 <- function(x, ...) {
  # capture expressions and create quosures
  dots <- enquos(...)
  # unquote to evaluate inside dplyr verbs
  summarise_at(x, vars(!!!dots), list(~ max(., na.rm = TRUE)))
}

fun4(df, B)
#>          B
#> 1 1.715065

fun4(df, B, D)
#>          B        D
#> 1 1.715065 1.786913
#>

reprex 패키지 (v0.2.1.9000)에 의해 2019-03-01에 생성됨



1

추가 생각으로 인용되지 않은 열 이름을 사용자 지정 함수에 전달해야하는 match.call()경우이 경우에도 유용 할 수 있습니다 deparse(substitute()).

df <- data.frame(A = 1:10, B = 2:11)

fun <- function(x, column){
  arg <- match.call()
  max(x[[arg$column]])
}

fun(df, A)
#> [1] 10

fun(df, B)
#> [1] 11

열 이름에 오타가있는 경우 오류와 함께 중지하는 것이 더 안전합니다.

fun <- function(x, column) max(x[[match.call()$column]])
fun(df, typo)
#> Warning in max(x[[match.call()$column]]): no non-missing arguments to max;
#> returning -Inf
#> [1] -Inf

# Stop with error in case of typo
fun <- function(x, column){
  arg <- match.call()
  if (is.null(x[[arg$column]])) stop("Wrong column name")
  max(x[[arg$column]])
}

fun(df, typo)
#> Error in fun(df, typo): Wrong column name
fun(df, A)
#> [1] 10

reprex 패키지 (v0.2.1)에 의해 2019-01-11에 생성됨

위의 답변에서 지적한 것처럼 인용 된 열 이름을 전달하는 것보다 추가 입력과 복잡성이 있기 때문에이 접근 방식을 사용하지 않을 것이라고 생각하지만 접근 방식입니다.

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