[r] data.frame 열 이름을 함수에 전달
나는 data.frame ( x
) 및 a 를 받아들이는 함수를 작성하려고 column
합니다. 이 함수는 x에서 일부 계산을 수행하고 나중에 다른 data.frame을 반환합니다. 열 이름을 함수에 전달하는 모범 사례 방법을 고수하고 있습니다.
두 개의 최소 예제 fun1
와 fun2
아래 는 예제로 x$column
사용하여에서 작업을 수행 할 수있는 원하는 결과를 생성합니다 max()
. 그러나 둘 다 겉보기에 (적어도 나에게는) 우아하지 않은 것에 의존합니다.
- 전화를 걸
substitute()
거나eval()
- 열 이름을 문자형 벡터로 전달해야합니다.
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()
합니까?
답변
열 이름을 직접 사용할 수 있습니다.
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")
답변
이 답변은 기존 답변과 동일한 요소를 많이 다루지 만,이 문제 (열 이름을 함수에 전달)는 좀 더 포괄적으로 다루는 답변이 있기를 원할 정도로 자주 발생합니다.
매우 간단한 데이터 프레임이 있다고 가정합니다.
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
입니다. 단순히 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
당신이 정말 당신이 무슨 일을하는지 알고있는 경우 등.
답변
개인적으로 열을 문자열로 전달하는 것은 매우 추한 것이라고 생각합니다. 나는 다음과 같은 것을 좋아합니다.
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
답변
또 다른 방법은 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에 생성됨
답변
추가 생각으로 인용되지 않은 열 이름을 사용자 지정 함수에 전달해야하는 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에 생성됨
위의 답변에서 지적한 것처럼 인용 된 열 이름을 전달하는 것보다 추가 입력과 복잡성이 있기 때문에이 접근 방식을 사용하지 않을 것이라고 생각하지만 접근 방식입니다.