R:行方式dplyr :: mutate使用带有数据帧行并返回整数的函数

use*_*672 8 row r dplyr mutate

我试图使用自定义函数管道mutate语句.我看起来有点类似的SO帖子但是徒劳无功.假设我有一个这样的数据框(其中blob一些变量与特定任务无关,但是是整个数据的一部分):

df <- 
  data.frame(exclude=c('B','B','D'), 
             B=c(1,0,0), 
             C=c(3,4,9), 
             D=c(1,1,0), 
             blob=c('fd', 'fs', 'sa'), 
             stringsAsFactors = F)
Run Code Online (Sandbox Code Playgroud)

我有一个使用变量名的函数,所以根据exclude列中的值选择一些,例如计算未指定的变量的总和exclude(总是单个字符).

FUN <- function(df){
  sum(df[c('B', 'C', 'D')] [!names(df[c('B', 'C', 'D')]) %in% df['exclude']] )
}
Run Code Online (Sandbox Code Playgroud)

当我给出一行(第1行)时,FUN我得到预期的CD(和那些未提及的exclude),即4:

FUN(df[1,])
Run Code Online (Sandbox Code Playgroud)

如何在具有mutate的管道中进行类似操作(将结果添加到变量中s).这两次尝试不起作用:

df %>% mutate(s=FUN(.))
df %>% group_by(1:n()) %>% mutate(s=FUN(.))
Run Code Online (Sandbox Code Playgroud)

更新 这也不能按预期工作:

df %>% rowwise(.) %>% mutate(s=FUN(.))
Run Code Online (Sandbox Code Playgroud)

这是有原因的,但不在dplyr的mutate(和管道)中:

df$s <- sapply(1:nrow(df), function(x) FUN(df[x,]))
Run Code Online (Sandbox Code Playgroud)

kon*_*vas 8

如果你想使用,dplyr你可以使用rowwise和你的功能FUN.

df %>% 
    rowwise %>% 
    do({
        result = as_data_frame(.)
        result$s = FUN(result)
        result
    })
Run Code Online (Sandbox Code Playgroud)

使用group_by而不是rowwise(就像你已经尝试过的那样)而do不是使用它来实现同样的目的mutate

df %>% 
    group_by(1:n()) %>% 
    do({
        result = as_data_frame(.)
        result$s = FUN(result)
        result
    })
Run Code Online (Sandbox Code Playgroud)

原因mutate在这种情况下不起作用,就是你将整个tibble传递给它,所以就像调用一样FUN(df).

一种更有效的方法是做同样的事情就是只需要包含一个列矩阵然后使用rowSums.

cols <- c('B', 'C', 'D')
include_mat <- outer(function(x, y) x != y, X = df$exclude, Y = cols)
# or outer(`!=`, X = df$exclude, Y = cols) if it's more readable to you
df$s <- rowSums(df[cols] * include_mat)
Run Code Online (Sandbox Code Playgroud)


Dav*_*ald 6

purrr 方法

我们可以使用的组合nest,并map_dbl为此:

library(tidyverse)
df %>% 
  rowwise %>% 
  nest(-blob) %>% 
  mutate(s = map_dbl(data, FUN)) %>% 
  unnest
Run Code Online (Sandbox Code Playgroud)

让我们分解一下。首先,rowwise允许我们应用每个后续函数来支持需要应用于每一行的任意复杂操作。

接下来,nest将创建一个新列,该列是要馈入的我们的数据的列表FUN(详细信息与data.frames的对比!)。由于我们正在应用此rowwise格式,因此每一行都包含一个单行标题exclude:D

最后,我们使用map_dbl映射FUN到每个小贴士。 因为我们的预期输出是数字(即双精度),所以它map_dbl在其他map_*函数系列中使用。

unnest 使我们的小标题变成更标准的结构。

purrrlyr 方法

虽然purrrlyr可能不如其父项dplyr和父项“受欢迎” purrr,但它的by_row功能在这里有一定用途。

在上面的示例中,我们将通过以下方式使用您的数据框df和用户定义的函数FUN

df %>% 
  by_row(..f = FUN, .to = "s", .collate = "cols")
Run Code Online (Sandbox Code Playgroud)

而已!给你:

# tibble [3 x 6]
  exclude     B     C     D  blob     s
    <chr> <dbl> <dbl> <dbl> <chr> <dbl>
1       B     1     3     1    fd     4
2       B     0     4     1    fs     5
3       D     0     9     0    sa     9
Run Code Online (Sandbox Code Playgroud)

诚然,语法有点奇怪,但是它的分解方式如下:

  • ..f =应用于每一行的函数
  • .to =输出列的名称,在这种情况下 s
  • .collate=按列表,行或列整理结果的方式。由于FUN只有一个输出,因此可以使用"cols""rows"

有关使用...的更多信息,请参见此处purrrlyr


性能

预警,尽管我喜欢的功能by_row,但这并不总是最佳的性能方法! purrr更直观,但速度损失也很大。请参阅以下microbenchmark测试:

library(microbenchmark)
mbm <- microbenchmark(
  purrr.test = df %>% rowwise %>% nest(-blob) %>% 
    mutate(s = map_dbl(data, FUN)) %>% unnest,
  purrrlyr.test = df %>% by_row(..f = FUN, .to = "s", .collate = "cols"),
  rowwise.test = df %>% 
    rowwise %>% 
    do({
      result = as_tibble(.)
      result$s = FUN(result)
      result
    }),
  group_by.test = df %>% 
    group_by(1:n()) %>% 
    do({
      result = as_tibble(.)
      result$s = FUN(result)
      result
    }),
  sapply.test = {df$s <- sapply(1:nrow(df), function(x) FUN(df[x,]))}, 
  times = 1000
)
autoplot(mbm)
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

您可以看到,该purrrlyr方法比使用dowith rowwisegroup_by(1:n())(见@konvas答案)的组合要快,并且与该sapply方法相当。但是,该程序包并不是最直观的。标准purrr方法似乎是最慢的,但也可能更易于使用。不同的用户定义功能可能会更改速度顺序。