在R中,将函数应用于数据帧的行并返回数据帧

时间:2018-04-06 16:07:06

标签: r dataframe vectorization

我正在尝试将自编函数应用于数据帧的行。

library(dplyr) # only used for data_frame
DF = data_frame(x = c(50, 49, 20), y = c(132, 124, 130), z = c(0.82, 1, 0.63))

     x     y     z
   <dbl> <dbl> <dbl>
1    50   132  0.82
2    49   124  1.00
3    20   130  0.63

实际数据框有数千行,这只是一个样本。

我的功能非常复杂并且做了很多事情,最后我为DF的每一行获得了一个新行。 让我们说简单来说,函数将1加1到第2列,第3列加到第3列(当然这可以是矢量化的,但我的函数,我们称之为Funct,做得更多)。 所以:

Funct = function(DF) {
   DF[1]= DF[1]+1
   DF[2] = DF[2]+2
   DF[3] = DF[3]+3
   return(DF)
}

如何以最有效的方式应用此函数以使输出结束新的数据框:

> DF
     x     y     z
   <dbl> <dbl> <dbl>
1    51   134  3.82
2    50   126  4.00
3    21   132  3.63

3 个答案:

答案 0 :(得分:3)

categories.sort_values(['college','level','points'],ascending=False) 对于数据帧来说是一个不好的选择,因为它是为矩阵设计的,因此会在迭代之前强制将数据帧输入到矩阵。除了偶尔进行昂贵的转换(之后必须反转)之外,真正的问题是R中的矩阵只能处理单个类型,而数据帧对于每个变量可以有不同的类型。因此,虽然它可以很好地处理这里的数据,但是当数字被强制转换为字符因为另一列是一个因素时,你常常会在矩阵中发生类型强制。如果你真的想使用apply,请事先明确强制使用矩阵,这样你就可以看到它的工作原理,并且你可以避免很多恼人的错误。

但是有一个比apply更好的选择:相反,在变量(列)上并行迭代,然后将结果列表强制转换回数据帧。 apply将处理这两个部分:

purrr::pmap_dfr

您可以使用

在基础R中执行相同的操作
library(tidyverse)

DF = data_frame(x = c(50, 49, 20), 
                y = c(132, 124, 130), 
                z = c(0.82, 1, 0.63))

DF %>% 
    pmap_dfr(~list(x = ..1 + 1,
                   y = ..2 + 2,
                   z = ..3 + 3))
#> # A tibble: 3 x 3
#>       x     y     z
#>   <dbl> <dbl> <dbl>
#> 1   51.  134.  3.82
#> 2   50.  126.  4.00
#> 3   21.  132.  3.63

......虽然它不是很漂亮。

请注意,尽可能使用矢量化解决方案会更快,更快。

do.call(rbind, do.call(Map, 
                       c(function(...){
                           data.frame(x = ..1 + 1,
                                      y = ..2 + 2,
                                      z = ..3 + 3)
                       }, 
                       DF)
))
#>    x   y    z
#> 1 51 134 3.82
#> 2 50 126 4.00
#> 3 21 132 3.63

答案 1 :(得分:0)

只需使用apply ...

DF2 <- as.data.frame(t(apply(DF, 1, Funct)))

DF2
   x   y    z
1 51 134 3.82
2 50 126 4.00
3 21 132 3.63

答案 2 :(得分:0)

如果这完全是numeric,您就可以轻松使用

as.data.frame(t(apply(as.matrix(DF), 1, `+`, c(1,2,3))))
as.data.frame(t(apply(DF, 1, Funct))) # better, per AndrewGustar's answer

这可能是你能做的最快的。但是,如果数据中包含numeric以外的任何内容(例如integer或* gasp * character),则使用apply将导致转换{{1不是你想要的。 (我在第一个示例中包含numeric来演示as.matrix中实际发生的情况,而不是您在代码中实际需要的内容。此矩阵转换是apply可能导致问题的原因非同质框架。)

正如其他评论中所述,如果您的数据真的全部 - apply,您将通过将其转换为numeric并处理来获得显着的性能(以及相关的存储)改进就这样。

对于异构类框架(或者如果您只是希望在将来的更改中保持健壮),请尝试以下方法:

matrix

修改

如果您需要在汇总每一行时包含所有数据:

  1. 将整个do.call(rbind, by(DF, seq_len(nrow(DF)), Funct)) # # A tibble: 3 × 3 # x y z # * <dbl> <dbl> <dbl> # 1 51 134 3.82 # 2 50 126 4.00 # 3 21 132 3.63 作为另一个参数传递,例如DF。这将被称为Funct(DF1, DFall);

  2. 如果您对所有行的访问仅仅是一个可以计算一次并作为附加参数传递给by(DF, seq_len(nrow(DF)), Funct, DFall=DF)的聚合(想想Funct),那么执行一次该计算,然后如上所述传递它代替整个框架;

  3. 否则,请使用Funct(DF1, DFall)循环。所提供的解决方案(我现在都不能想到)都没有促进这种观点。