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
This is often considered "best practice" since it is the method that is hardest to screw up. Passing the column names as strings is about as unambiguous as you can get.
The following two options are more advanced. Many popular packages make use of these kinds of techniques, but using them well requires more care and skill, as they can introduce subtle complexities and unanticipated points of failure. This section of Hadley's Advanced R book is an excellent reference for some of these issues.
If you really want to save the user from typing all those quotes, one option might be to convert bare, unquoted column names to strings using 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
This is, frankly, a bit silly probably, since we're really doing the same thing as in new_column1
, just with a bunch of extra work to convert bare names to strings.
Finally, if we want to get really fancy, we might decide that rather than passing in the names of two columns to add, we'd like to be more flexible and allow for other combinations of two variables. In that case we'd likely resort to using eval()
on an expression involving the two columns:
new_column3 <- function(df,col_name,expr){
col_name <- deparse(substitute(col_name))
df[[col_name]] <- eval(substitute(expr),df,parent.frame())
df
}
Just for fun, I'm still using deparse(substitute())
for the name of the new column. Here, all of the following will work:
> 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
So the short answer is basically: pass data.frame column names as strings and use [[
to select single columns. Only start delving into eval
, substitute
, etc. if you really know what you're doing.
Personally I think that passing the column as a string is pretty ugly. I like to do something like:
get.max <- function(column,data=NULL){
column<-eval(substitute(column),data, parent.frame())
max(column)
}
which will yield:
> get.max(mpg,mtcars)
[1] 33.9
> get.max(c(1,2,3,4,5))
[1] 5
Notice how the specification of a data.frame is optional. you can even work with functions of your columns:
> get.max(1/mpg,mtcars)
[1] 0.09615385
Another way is to use tidy evaluation
approach. It is pretty straightforward to pass columns of a data frame either as strings or bare column names. See more about tidyeval
here.
library(rlang)
library(tidyverse)
set.seed(123)
df <- data.frame(B = rnorm(10), D = rnorm(10))
Use column names as strings
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
Use bare column names
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
#>
Created on 2019-03-01 by the reprex package (v0.2.1.9000)
As an extra thought, if is needed to pass the column name unquoted to the custom function, perhaps match.call()
could be useful as well in this case, as an alternative to 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
If there is a typo in the column name, then would be safer to stop with an error:
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
Created on 2019-01-11 by the reprex package (v0.2.1)
I do not think I would use this approach since there is extra typing and complexity than just passing the quoted column name as pointed in the above answers, but well, is an approach.
참고URL : https://stackoverflow.com/questions/2641653/pass-a-data-frame-column-name-to-a-function
'programing tip' 카테고리의 다른 글
Go에서 fmt.Println ()과 println ()의 차이점은 무엇입니까? (0) | 2020.08.12 |
---|---|
React Enzyme은 두 번째 (또는 n 번째) 노드를 찾습니다. (0) | 2020.08.12 |
PHP-문자열 문자 반복 (0) | 2020.08.11 |
DialogFragment를 올바르게 닫는 방법은 무엇입니까? (0) | 2020.08.11 |
GoogleTest에서 특정 테스트 케이스를 실행하는 방법 (0) | 2020.08.11 |