각 행의 여러 인수를 사용하여 각 데이터 프레임 행에서 apply-like 함수 호출
여러 열이있는 데이터 프레임이 있습니다. 데이터 프레임의 각 행에 대해 행에서 함수를 호출하고 싶습니다. 함수 입력은 해당 행의 여러 열을 사용하고 있습니다. 예를 들어,이 데이터와 두 개의 인수를 허용하는 testFunc가 있다고 가정 해 봅시다.
> df <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6))
> df
x y z
1 1 3 5
2 2 4 6
> testFunc <- function(a, b) a + b
이 testFunc를 x 및 z 열에 적용하고 싶다고 가정 해 봅시다. 따라서 행 1의 경우 1 + 5를 원하고 행 2의 경우 2 + 6을 원합니다. for 루프를 작성하지 않고 적용 함수 패밀리를 사용 하여이 작업을 수행하는 방법이 있습니까?
나는 이것을 시도했다 :
> df[,c('x','z')]
x z
1 1 5
2 2 6
> lapply(df[,c('x','z')], testFunc)
Error in a + b : 'b' is missing
그러나 어떤 아이디어라도 오류가 있습니까?
편집 : 호출하려는 실제 함수는 단순한 합계가 아니지만 power.t.test입니다. 예제 목적으로 a + b를 사용했습니다. 최종 목표는 의사 코드로 작성된 다음과 같은 작업을 수행하는 것입니다.
df = data.frame(
delta=c(delta_values),
power=c(power_values),
sig.level=c(sig.level_values)
)
lapply(df, power.t.test(delta_from_each_row_of_df,
power_from_each_row_of_df,
sig.level_from_each_row_of_df
))
여기서 결과는 df의 각 행에 대한 power.t.test에 대한 출력 벡터입니다.
apply원본 데이터의 하위 집합에 적용 할 수 있습니다 .
dat <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6))
apply(dat[,c('x','z')], 1, function(x) sum(x) )
또는 함수가 합계라면 벡터화 된 버전을 사용하십시오.
rowSums(dat[,c('x','z')])
[1] 6 8
사용하고 싶다면 testFunc
testFunc <- function(a, b) a + b
apply(dat[,c('x','z')], 1, function(x) testFunc(x[1],x[2]))
편집 색인이 아닌 이름으로 열에 액세스하려면 다음과 같이 할 수 있습니다.
testFunc <- function(a, b) a + b
apply(dat[,c('x','z')], 1, function(y) testFunc(y['z'],y['x']))
A data.frame는 list이므로 ...
들어 벡터화 기능 do.call 일반적으로 좋은 내기이다. 그러나 논쟁의 이름이 등장합니다. 여기서 testFunca와 b 대신 args x와 y로 호출됩니다. 는 ...관련이없는 인수가 오류를 발생시키지 않고 통과 할 수 있습니다 :
do.call( function(x,z,...) testFunc(x,z), df )
들어 비 벡터화 기능 , mapply작동하지만 당신은 그 이름을 명시 적으로 인수의 순서를 일치하거나해야합니다 :
mapply(testFunc, df$x, df$z)
때로는 apply모든 인수가 동일한 유형이므로 data.frame행렬에 강제로 적용 해도 데이터 유형을 변경해도 문제가 발생하지 않습니다. 당신의 예는 이런 종류였습니다.
인수가 모두 전달되는 다른 함수 내에서 함수를 호출해야하는 경우에는 그보다 훨씬 더 매끄러운 방법이 있습니다. lm()그 길을 가고 싶다면 몸의 첫 줄을 연구하십시오 .
사용하다 mapply
> df <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6))
> df
x y z
1 1 3 5
2 2 4 6
> mapply(function(x,y) x+y, df$x, df$z)
[1] 6 8
> cbind(df,f = mapply(function(x,y) x+y, df$x, df$z) )
x y z f
1 1 3 5 6
2 2 4 6 8
dplyr패키지 와 함께 새로운 답변
적용하려는 함수가 벡터화 mutate된 경우 dplyr패키지 에서 해당 함수를 사용할 수 있습니다 .
> library(dplyr)
> myf <- function(tens, ones) { 10 * tens + ones }
> x <- data.frame(hundreds = 7:9, tens = 1:3, ones = 4:6)
> mutate(x, value = myf(tens, ones))
hundreds tens ones value
1 7 1 4 14
2 8 2 5 25
3 9 3 6 36
plyr패키지 와 함께 옛 대답
겸손한 견해로는, 작업에 가장 적합한 도구 mdply는 plyr패키지입니다.
예:
> library(plyr)
> x <- data.frame(tens = 1:3, ones = 4:6)
> mdply(x, function(tens, ones) { 10 * tens + ones })
tens ones V1
1 1 4 14
2 2 5 25
3 3 6 36
Unfortunately, as Bertjan Broeksema pointed out, this approach fails if you don't use all the columns of the data frame in the mdply call. For example,
> library(plyr)
> x <- data.frame(hundreds = 7:9, tens = 1:3, ones = 4:6)
> mdply(x, function(tens, ones) { 10 * tens + ones })
Error in (function (tens, ones) : unused argument (hundreds = 7)
Many functions are vectorization already, and so there is no need for any iterations (neither for loops or *pply functions). Your testFunc is one such example. You can simply call:
testFunc(df[, "x"], df[, "z"])
In general, I would recommend trying such vectorization approaches first and see if they get you your intended results.
Alternatively, if you need to pass multiple arguments to a function which is not vectorized, mapply might be what you are looking for:
mapply(power.t.test, df[, "x"], df[, "z"])
Others have correctly pointed out that mapply is made for this purpose, but (for the sake of completeness) a conceptually simpler method is just to use a for loop.
for (row in 1:nrow(df)) {
df$newvar[row] <- testFunc(df$x[row], df$z[row])
}
Here is an alternate approach. It is more intuitive.
One key aspect I feel some of the answers did not take into account, which I point out for posterity, is apply() lets you do row calculations easily, but only for matrix (all numeric) data
operations on columns are possible still for dataframes:
as.data.frame(lapply(df, myFunctionForColumn()))
To operate on rows, we make the transpose first.
tdf<-as.data.frame(t(df))
as.data.frame(lapply(tdf, myFunctionForRow()))
The downside is that I believe R will make a copy of your data table. Which could be a memory issue. (This is truly sad, because it is programmatically simple for tdf to just be an iterator to the original df, thus saving memory, but R does not allow pointer or iterator referencing.)
Also, a related question, is how to operate on each individual cell in a dataframe.
newdf <- as.data.frame(lapply(df, function(x) {sapply(x, myFunctionForEachCell()}))
I came here looking for tidyverse function name - which I knew existed. Adding this for (my) future reference and for tidyverse enthusiasts: purrrlyr:invoke_rows (purrr:invoke_rows in older versions).
With connection to standard stats methods as in the original question, the broom package would probably help.
@user20877984's answer is excellent. Since they summed it up far better than my previous answer, here is my (posibly still shoddy) attempt at an application of the concept:
Using do.call in a basic fashion:
powvalues <- list(power=0.9,delta=2)
do.call(power.t.test,powvalues)
Working on a full data set:
# get the example data
df <- data.frame(delta=c(1,1,2,2), power=c(.90,.85,.75,.45))
#> df
# delta power
#1 1 0.90
#2 1 0.85
#3 2 0.75
#4 2 0.45
lapply the power.t.test function to each of the rows of specified values:
result <- lapply(
split(df,1:nrow(df)),
function(x) do.call(power.t.test,x)
)
> str(result)
List of 4
$ 1:List of 8
..$ n : num 22
..$ delta : num 1
..$ sd : num 1
..$ sig.level : num 0.05
..$ power : num 0.9
..$ alternative: chr "two.sided"
..$ note : chr "n is number in *each* group"
..$ method : chr "Two-sample t test power calculation"
..- attr(*, "class")= chr "power.htest"
$ 2:List of 8
..$ n : num 19
..$ delta : num 1
..$ sd : num 1
..$ sig.level : num 0.05
..$ power : num 0.85
... ...
data.table has a really intuitive way of doing this as well:
library(data.table)
sample_fxn = function(x,y,z){
return((x+y)*z)
}
df = data.table(A = 1:5,B=seq(2,10,2),C = 6:10)
> df
A B C
1: 1 2 6
2: 2 4 7
3: 3 6 8
4: 4 8 9
5: 5 10 10
The := operator can be called within brackets to add a new column using a function
df[,new_column := sample_fxn(A,B,C)]
> df
A B C new_column
1: 1 2 6 18
2: 2 4 7 42
3: 3 6 8 72
4: 4 8 9 108
5: 5 10 10 150
It's also easy to accept constants as arguments as well using this method:
df[,new_column2 := sample_fxn(A,B,2)]
> df
A B C new_column new_column2
1: 1 2 6 18 6
2: 2 4 7 42 12
3: 3 6 8 72 18
4: 4 8 9 108 24
5: 5 10 10 150 30
If data.frame columns are different types, apply() has a problem. A subtlety about row iteration is how apply(a.data.frame, 1, ...) does implicit type conversion to character types when columns are different types; eg. a factor and numeric column. Here's an example, using a factor in one column to modify a numeric column:
mean.height = list(BOY=69.5, GIRL=64.0)
subjects = data.frame(gender = factor(c("BOY", "GIRL", "GIRL", "BOY"))
, height = c(71.0, 59.3, 62.1, 62.1))
apply(height, 1, function(x) x[2] - mean.height[[x[1]]])
The subtraction fails because the columns are converted to character types.
One fix is to back-convert the second column to a number:
apply(subjects, 1, function(x) as.numeric(x[2]) - mean.height[[x[1]]])
But the conversions can be avoided by keeping the columns separate and using mapply():
mapply(function(x,y) y - mean.height[[x]], subjects$gender, subjects$height)
mapply() is needed because [[ ]] does not accept a vector argument. So the column iteration could be done before the subtraction by passing a vector to [], by a bit more ugly code:
subjects$height - unlist(mean.height[subjects$gender])
A really nice function for this is adply from plyr, especially if you want to append the result to the original dataframe. This function and its cousin ddply have saved me a lot of headaches and lines of code!
df_appended <- adply(df, 1, mutate, sum=x+z)
Alternatively, you can call the function you desire.
df_appended <- adply(df, 1, mutate, sum=testFunc(x,z))
'IT박스' 카테고리의 다른 글
| 파이썬에서 인수 목록이있는 함수 호출 (0) | 2020.06.17 |
|---|---|
| 배열 목록의 길이를 찾는 방법은 무엇입니까? (0) | 2020.06.17 |
| 파이썬 스크립트의 실행을 어떻게 중단합니까? (0) | 2020.06.17 |
| 루비에서 문자열이 다른 문자열로 시작하는지 어떻게 알 수 있습니까? (0) | 2020.06.16 |
| 객체를 문자열로 직렬화하는 방법 (0) | 2020.06.16 |