将多个函数应用于数据框的每一行

Loo*_*eft 20 r transform rows apply dataframe

每次我认为我理解使用向量时,似乎是一个简单的问题会让我内心深处.很多阅读和尝试不同的例子在这个场合没有帮助.请勺子喂我这里...

我想将两个自定义函数应用于数据帧的每一行,并将结果添加为两个新列.这是我的示例代码:

# Required packages:
library(plyr)

FindMFE <- function(x) {
    MFE <- max(x, na.rm = TRUE) 
    MFE <- ifelse(is.infinite(MFE ) | (MFE  < 0), 0, MFE)
    return(MFE)
}

FindMAE <- function(x) {
    MAE <- min(x, na.rm = TRUE) 
    MAE <- ifelse(is.infinite(MAE) | (MAE> 0), 0, MAE)
    return(MAE)
}

FindMAEandMFE <- function(x){
        # I know this next line is wrong...
    z <- apply(x, 1, FindMFE, FindMFE)
        return(z)
}

df1 <- data.frame(Bar1=c(1,2,3,-3,-2,-1),Bar2=c(3,1,3,-2,-3,-1))

df1 = transform(df1, 
    FindMAEandMFE(df1)  
)

#DF1 should end up with the following data...
#Bar1   Bar2    MFE MAE
#1      3       3   0
#2      1       2   0
#3      3       3   0
#-3     -2      0   -3
#-2     -3      0   -3
#-1     -1      0   -1
Run Code Online (Sandbox Code Playgroud)

使用plyr库和类似基础的方法获得答案会很棒.两者都有助于我的理解.当然,如果显而易见,请指出我出错的地方.;-)

现在回到我的帮助文件!

编辑:我想要一个多变量解决方案,因为列名可能会随着时间的推移而改变和扩展.它还允许将来重用代码.

Rei*_*son 19

我觉得你在这里想的太复杂了.两个单独的apply()电话有什么问题?然而,有一个更好的方法来做你在这里做的事情,不涉及循环/应用调用.我将单独处理这些,但第二种解决方案更可取,因为它是真正的矢量化.

两个申请电话版本

前两个使用all-Base R函数的单独应用调用:

df1 <- data.frame(Bar1=c(1,2,3,-3,-2,-1),Bar2=c(3,1,3,-2,-3,-1))
df1 <- transform(df1, MFE = apply(df1, 1, FindMFE), MAE = apply(df1, 1, FindMAE))
df1
Run Code Online (Sandbox Code Playgroud)

这使:

> df1
  Bar1 Bar2 MFE MAE
1    1    3   3   0
2    2    1   2   0
3    3    3   3   0
4   -3   -2   0  -3
5   -2   -3   0  -3
6   -1   -1   0  -1
Run Code Online (Sandbox Code Playgroud)

好吧,循环df1两次的行可能效率不高,但即使是大问题,你花了更多的时间在一次通过中考虑巧妙地做这个,而不是通过这样做来节省.

使用矢量化函数pmax()pmin()

因此,更好的方法是注意pmax()pmin()功能,并意识到他们可以做每个apply(df1, 1, FindFOO()调用正在做的事情.例如:

> (tmp <- with(df1, pmax(0, Bar1, Bar2, na.rm = TRUE)))
[1] 3 2 3 0 0 0
Run Code Online (Sandbox Code Playgroud)

将是你问题的MFE.如果你有两列并且它们是Bar1和/ Bar2或前两列,那么这非常简单df1.但它不是很一般; 如果你想要计算多个列,等等怎么办?pmax(df1[, 1:2], na.rm = TRUE)不会做我们想要的:

> pmax(df1[, 1:2], na.rm = TRUE)
  Bar1 Bar2
1    1    3
2    2    1
3    3    3
4   -3   -2
5   -2   -3
6   -1   -1
Run Code Online (Sandbox Code Playgroud)

该窍门能让使用通用的解决方案pmax(),并pmin()为使用do.call()安排来为我们这两个函数的调用.更新您的功能以使用此想法我们有:

FindMFE2 <- function(x) {
   MFE <- do.call(pmax, c(as.list(x), 0, na.rm = TRUE))
   MFE[is.infinite(MFE)] <- 0
   MFE
}

FindMAE2 <- function(x) {
   MAE <- do.call(pmin, c(as.list(x), 0, na.rm = TRUE))
   MAE[is.infinite(MAE)] <- 0
   MAE
}
Run Code Online (Sandbox Code Playgroud)

给出:

> transform(df1, MFE = FindMFE2(df1), MAE = FindMAE2(df1))
  Bar1 Bar2 MFE MAE
1    1    3   3   0
2    2    1   2   0
3    3    3   3   0
4   -3   -2   0  -3
5   -2   -3   0  -3
6   -1   -1   0  -1
Run Code Online (Sandbox Code Playgroud)

而不是apply()在望.如果您想在一个步骤中执行此操作,现在可以更轻松地进行换行:

FindMAEandMFE2 <- function(x){
    cbind(MFE = FindMFE2(x), MAE = FindMAE2(x))
}
Run Code Online (Sandbox Code Playgroud)

可以用作:

> cbind(df1, FindMAEandMFE2(df1))
  Bar1 Bar2 MFE MAE
1    1    3   3   0
2    2    1   2   0
3    3    3   3   0
4   -3   -2   0  -3
5   -2   -3   0  -3
6   -1   -1   0  -1
Run Code Online (Sandbox Code Playgroud)


And*_*rie 19

我展示了三种替代单线:

  • 使用的each功能plyr
  • 使用plyr each基本R 的功能
  • 使用矢量pmin和和pmax函数

解决方案1:plyr和每个

plyr包定义了each执行您想要的功能.From ?each:将多个函数聚合到一个函数中. 这意味着您可以使用单行解决问题:

library(plyr)
adply(df1, 1, each(MAE=function(x)max(x, 0), MFE=function(x)min(x, 0)))

  Bar1 Bar2 MAE MFE
1    1    3   3   0
2    2    1   2   0
3    3    3   3   0
4   -3   -2   0  -3
5   -2   -3   0  -3
6   -1   -1   0  -1
Run Code Online (Sandbox Code Playgroud)

解决方案2:每个和基础R

当然,您可以使用each基本功能.以下是如何使用它apply- 只需注意在添加到原始data.frame之前必须转置结果.

library(plyr)
data.frame(df1, 
  t(apply(df1, 1, each(MAE=function(x)max(x, 0), MFE=function(x)min(x, 0)))))

  Bar1 Bar2 MAE MFE
1    1    3   3   0
2    2    1   2   0
3    3    3   3   0
4   -3   -2   0  -3
5   -2   -3   0  -3
6   -1   -1   0  -1
Run Code Online (Sandbox Code Playgroud)

解决方案3:使用矢量化函数

使用矢量化功能pminpmax,您可以使用此一班轮:

transform(df1, MFE=pmax(0, Bar1, Bar2), MAE=pmin(0, Bar1, Bar2))

  Bar1 Bar2 MFE MAE
1    1    3   3   0
2    2    1   2   0
3    3    3   3   0
4   -3   -2   0  -3
5   -2   -3   0  -3
6   -1   -1   0  -1
Run Code Online (Sandbox Code Playgroud)


Joh*_*ohn 6

这里有很多好的答案.我是在Gavin Simpson编辑的时候开始的,所以我们介绍了一些相似的内容.并行最小值和最大值(pmin和pmax)几乎就是你正在编写函数的内容.它在pmax(0,Bar1,Bar2)中的作用可能有点不透明,但基本上0会被回收,这就像做的那样

pmax(c(0,0,0,0,0,0), Bar1, Bar2)
Run Code Online (Sandbox Code Playgroud)

这将通过三件事中的每一项,并找到它们的最大值.因此,如果它是负数,则max将为0,并完成ifelse语句所做的大部分工作.你可以重写,这样你就可以得到向量,并将事物与你正在做的事情相结合,这可能会使它更加透明.在这种情况下,我们只需将数据帧传递给一个新的并行快速findMFE函数,该函数可以与任何数值数据帧一起使用并得到一个向量.

findMFE <- function(dataf){
    MFE <- do.call( pmax, c(dataf, 0, na.rm = TRUE))
}

MFE <- findMFE(df1)
Run Code Online (Sandbox Code Playgroud)

这个函数的作用是在传递的数据帧中添加一个0的额外列,然后调用pmax传递df1的每个单独列,就像它是一个列表一样(数据帧是列表,所以这很容易).

现在,我注意到您实际上想要更正数据中不在您的示例中的Inf值...我们可以为您的函数添加额外的行...

findMFE <- function(dataf){
    MFE <- do.call( pmax, c(dataf, 0, na.rm = TRUE))
    ifelse(is.infinite(MFE), 0, MFE)
}
Run Code Online (Sandbox Code Playgroud)

现在,正确使用向量上的ifelse()函数.我这样做是为了你的例子,但Gavin Simpson使用MFE [is.infinite(MFE)] < - 0更有效率.请注意,此findMFE函数不在循环中使用,它只是传递整个数据帧.

可比较的findMAE是......

findMAE <- function(dataf){
    MAE <- do.call( pmin, c(dataf, 0, na.rm = TRUE))
    ifelse(is.infinite(MAE), 0, MAE)
}
Run Code Online (Sandbox Code Playgroud)

而且组合的功能很简单......

findMFEandMAE <- function(dataf){
    MFE <- findMFE(dataf)
    MAE <- findMAE(dataf)
    return(data.frame(MFE, MAE))
}
Run Code Online (Sandbox Code Playgroud)

MFEandMAE < - findMFEandMAE(df1)df1 < - cbind(df1,MFEandMAE)

一些技巧

如果你有一个标量if语句不使用ifelse(),请使用if()else.它在标量情况下要快得多.并且,您的函数是标量,并且您正在尝试对它们进行矢量化.ifelse()已经被向量化并且在使用时以非常快的速度运行,但是当使用标量时比if()else慢得多.

另外,如果你要把东西放在一个循环中,或者把声明放在尽可能少的地方.例如,在你的情况下,ifelse()确实需要从循环中取出并随后应用于整个MFE结果.