programing

함수에 data.frame 열 이름 전달

easyjava 2023. 6. 8. 22:41
반응형

함수에 data.frame 열 이름 전달

저는frame(data.frame)을 함수를 x및 a ) 및acolumn data합니다.함수는 x에 대해 일부 계산을 수행하고 나중에 다른 data.frame을 반환합니다.열 이름을 함수에 전달하는 모범 사례 방법을 고수하고 있습니다.

의 예는 두지최 의예한 소가입니다.fun1그리고.fun2아래는 작업을 수행할 수 있는 원하는 결과를 생성합니다.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")

는 그 함수를 다같호출수있좋겠다니습으면할이함수음를과▁as▁the▁다라고 부를 수 있으면 좋겠습니다.fun(df, B)를 들면 를들면예면.고려했지만 시도하지 않은 다른 옵션:

  • 통과하다column열 번호의 정수로 사용할 수 있습니다.제 생각에 이것은 피할 수 있을 것 같습니다.substitute()이상적으로 함수는 다음 중 하나를 허용할 수 있습니다.
  • with(x, get(column))하지만, 효과가 있다 하더라도, 저는 이것이 여전히 필요하다고 생각합니다.substitute
  • 을 합니다.formula()그리고.match.call()둘 다 경험이 별로 없어요

질문:아이즈do.call()다보선는보다 eval()?

이 답변은 기존 답변과 동일한 요소를 많이 다루겠지만, 이 문제(열 이름을 함수로 전달)가 자주 나와 조금 더 포괄적으로 다루는 답변이 있었으면 했습니다.

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

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

열을 새로 를 작성하려고 z은 열의 입니다.x그리고.y.

여기서 매우 일반적인 장애물은 자연스러운(잘못된) 시도가 종종 다음과 같이 보인다는 것입니다.

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▁column▁looks다▁for에 있는 열을 찾기만 하면 .df문자 그대로 라고 하는col1은 이동은다설있습다니어에 .?Extract"재귀적(목록과 유사한) 개체" 섹션 아래에 있습니다.

간단하고 " 간단고가자권주솔는장루것다전다입니는환서하음에가"에서 입니다.$[[인수를 string으로 합니다.

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의 고급 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열하고 따서기본간같대단다습답다음라니한은.data.frame열이문사다니를 사용하는 것입니다.[[단일 열을 선택합니다.에 대한 탐구만 시작합니다.eval,substitute자신이 무엇을 하고 있는지 정말로 알고 있다면 등등.

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

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

개인적으로 저는 그 칼럼을 끈으로 넘기는 것은 꽤 추하다고 생각합니다.저는 다음과 같은 일을 하는 것을 좋아합니다.

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

또 다른 방법은 접근법을 사용하는 것입니다.데이터 프레임의 열을 문자열 또는 베어 열 이름으로 전달하는 것은 매우 간단합니다.자세한 내용 보기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에 생성되었습니다.

와 함께dplyr이제 단순히 이중 곱슬곱슬한 중괄호를 사용하여 데이터 프레임의 특정 열에 액세스할 수도 있습니다.{{...}}본문 열를 들어 " 예들어내본, 함수서원하는열이주위름에문를"col_name:

library(tidyverse)

fun <- function(df, col_name){
   df %>% 
     filter({{col_name}} == "test_string")
} 

Tung의 답변mgrund의 답변깔끔한 평가를 제시했습니다.이 답변에서 저는 우리가 이러한 개념을 사용하여 조란의 답변(특히 그의 기능)과 유사한 것을 할 수 있는 방법을 보여줄 것입니다.new_column3은 기본 평가의 하고 정리 할 수 있는 구문을 이를 위한 목적은 기본 평가와 정리 평가의 차이를 쉽게 확인하고 정리 평가에 사용할 수 있는 다른 구문을 확인하는 것입니다.은 필할것다니가 필요할 입니다.rlang그리고.dplyr이를 위하여

기본 평가 도구 사용(조란의 답변):

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

번째 줄에, 번째줄에서첫,서,substitute우리가 평가하게 만들고 있습니다.col_name도 함)로, 랑의 는 다음과 더 구 체 때 적 를 기 이 호 아 도 표 고 라 함 다 니 합 현 이 름 는 로 닌 객 가 체 으 로 는 ▁be :▁as 표 다 ' 니 랑의 대체물은 다음과 같습니다.

  • ensym그것을 상징으로 바꿉니다;
  • enexpr그것을 표현으로 바꿉니다;
  • enquo이 값은 R이 값을 평가할 변수를 찾아야 하는 환경을 가리키는 표현식인 퀘스큐스로 변환됩니다.

대부분의 경우 환경에 대한 포인터를 사용해야 합니다.특별히 필요하지 않을 때, 가지고 있는 것은 거의 문제를 일으키지 않습니다.따라서 대부분의 시간을 사용할 수 있습니다.enquo이 경우 다음을 사용할 수 있습니다.ensym코드를 읽기 쉽게 하기 위해, 그것은 무엇을 더 명확하게 하기 때문에.col_name사실은.

번째에도, 첫번째줄도서에도▁also줄서에.deparse식/키워드를 문자열로 변환하는 중입니다.은 또한 수있다습니도를 사용할 .as.character또는rlang::as_string.

줄, 두번줄에서째,서,substitute 있어요 돌있니다습고다▁is니.expr (으)ㄹ 수 요, ㄹ 수 있어요.ensym더 이상 선택사항이 아닙니다.

또한 두 번째 줄에서, 우리는 이제 바꿀 수 있습니다.evalrlang::eval_tidy은 Eval과 합니다.enexpr하지만 비밀로 하지는 않습니다.있을 때, 평가 가 없습니다. (이 했던 처럼)parent.frame()).

위에서 제안한 대체 방법의 한 가지 조합은 다음과 같습니다.

new_column3 <- function(df,col_name,expr){
  col_name <- as_string(ensym(col_name))
  df[[col_name]] <- eval_tidy(enquo(expr), df)
  df
}

우리는 또한 사용할 수 있습니다.dplyr연산자 - 데이터 프레임의 열을 변수로 변환하고 이름으로 호출할 수 있습니다.기호를 + 부분 집합 + 부분 집합으로 변환하는 방법을 바꿀 수 .df용사를 [[와 함께mutate:

new_column3 <- function(df,col_name,expr){
  col_name <- ensym(col_name)
  df %>% mutate(!!col_name := eval_tidy(enquo(expr), df))
}

이 "col_name와 "col_name"으로하게 평가합니다(lazy-evaluate 반의, !!교환니다. 에 '을 사용할 수 .=그리고 새로운 구문을 사용해야 합니다.:=.

에는 바로: 을기열바로꾼다호음뱅뱅불로안으하게평가는하일반인적작업바는에로한가다기:니가슬있슬곱곱습이름▁the한슬곱곱▁a▁of:▁common▁the:▁name▁into다▁then▁operation▁turning▁shortcut슬.{{연산자:

new_column3 <- function(df,col_name,expr){
  df %>% mutate({{col_name}} := eval_tidy(enquo(expr), df))
}

저는 R에 대한 평가 전문가가 아니기 때문에 지나치게 단순화하거나 잘못된 용어를 사용했을 수도 있으니 댓글에서 수정 부탁드립니다.이 질문에 대한 답변에 사용된 여러 가지 도구를 비교하는 데 도움이 되었으면 합니다.

추가적으로 열 이름을 사용자 정의 함수에 따옴표로 묶지 않고 전달할 필요가 있다면, 아마도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에 생성되었습니다.

위의 답변에서 지적한 대로 인용된 열 이름을 전달하는 것 외에 추가적인 유형과 복잡성이 있기 때문에 이 접근 방식을 사용하지 않을 것이라고 생각합니다.

R 패키지 내에서 이 기능을 구축하려는 경우 또는 복잡성을 줄이려는 경우 다음을 수행할 수 있습니다.

test_func <- function(df, column) {
  if (column %in% colnames(df)) {
    return(max(df[, column, with=FALSE])) 
  } else {
    stop(cat(column, "not in data.frame columns."))
  }
}

»with=FALSE"열이 변수인 것처럼 열을 참조하는 기능을 비활성화하여 "data.frame 모드"(CRAN 문서별)를 복원합니다.if 문은 제공된 열 이름이 data.frame 내에 있는 경우 쉽게 찾을 수 있는 방법입니다.여기서 tryCatch 오류 처리를 사용할 수도 있습니다.

언급URL : https://stackoverflow.com/questions/2641653/pass-a-data-frame-column-name-to-a-function

반응형