각 행의 여러 인수를 사용하여 각 데이터 프레임 행에서 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
일반적으로 좋은 내기이다. 그러나 논쟁의 이름이 등장합니다. 여기서 testFunc
a와 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 |