使用自定义函数在R的dplyr中为多列添加新列

3

我想使用dplyr::mutate创建新的数据框列,使用自定义函数,其参数是数据框列名称的向量,但我得到了以下输出:

customFun <- function(col.vec) {
  paste0(gsub("\\s", "_", col.vec), collapse = "-")
}

df <- data.frame(A = c("x 1", "x", "x w"), B = c("E", "D", "2 w"), stringsAsFactors = FALSE)

df %>%
   mutate(C = customFun(c(A, B)))
    A   B                 C
1 x 1   E x_1-x-x_w-E-D-2_w
2   x   D x_1-x-x_w-E-D-2_w
3 x w 2 w x_1-x-x_w-E-D-2_w

使用以下内容代替:

data.table::data.table(df)[, C := customFun(c(A, B)), by = .(A, B)]
     A   B       C
1: x 1   E   x_1-E
2:   x   D     x-D
3: x w 2 w x_w-2_w

有很多种方法可以实现,但我只对dplyr解决方案感兴趣。感谢您的帮助。


Mutate只能返回与数据长度相同或仅具有长度为1的向量。到目前为止,您的自定义函数还没有做到这一点。我猜你没有做你期望做的事情,例如,你粘贴了什么?你有一个长向量,你用sep粘贴它到哪里了?尝试使用with(head(iris),customFun(c(Species, Petal.Length, Petal.Width))),你会发现这不是你想要的。c(Species, Petal.Length, Petal.Width)已经是一个向量,因此在技术上不能与另一个不存在的东西粘贴在一起。 - Onyambu
抱歉各位,我提供了一个糟糕的例子 - 我更新了我的问题,现在没问题了。 - Taz
即使使用data.tables,如果您有两个相似的行,它们也会被合并为一个。您的customFunc不正确。 - Onyambu
你需要做类似于 df%>%mutate(C = invoke(paste,data.frame(gsub('\\s+','_',as.matrix(.))),sep='-')) 的操作。 - Onyambu
3个回答

2
我们可以使用maplift_dl。我们首先对每个col.vec进行map(注意我使用了列表而不是向量作为输入,因为c会扁平化任何向量元素,而列表不会),并应用gsub。然后将列表输出输入到paste中。由于paste接受...,我们可以使用purrr::lift_dl将其输入域从...提升到list类型:
最初的回答
library(dplyr)
library(purrr)

customFun <- function(col.vec) {
  map(col.vec, ~gsub("\\s", "_", .x)) %>%
    lift_dl(paste, sep = "-")()
}

df %>%
  mutate(C = customFun(list(A, B)))

最初的回答:或者使用...作为输入:
customFun <- function(...) {
  col.vec <- list(...)
  map(col.vec, ~gsub("\\s", "_", .x)) %>%
    lift_dl(paste, sep = "-")()
}

df %>%
  mutate(C = customFun(A, B))

输出:

    A   B       C
1 x 1   E   x_1-E
2   x   D     x-D
3 x w 2 w x_w-2_w

1

只需在mutate之前添加rowwise,这样paste中仅使用每一行的A和B值,而不是所有行的向量。

library(dplyr)

df %>%
  rowwise() %>%
  mutate(C = customFun(c(A, B)))
#> Source: local data frame [3 x 3]
#> Groups: <by row>
#> 
#> # A tibble: 3 x 3
#>   A     B     C      
#>   <chr> <chr> <chr>  
#> 1 x 1   E     x_1-E  
#> 2 x     D     x-D    
#> 3 x w   2 w   x_w-2_w

reprex包 (v0.2.1)于2019年02月05日创建


1
为什么在您的data.table解决方案中使用by=.(..)?如果您有两行具有完全相似的值,则这些行将被合并为一行。您需要修改customFun。它的方式不正确。
library(tidyverse)
customFun = function(data) invoke(paste, data.frame(gsub('\\s+', '_', as.matrix(data))), sep='-')

df %>% 
    mutate(c = customFun(.))

    A   B       C
1 x 1   E   x_1-E
2   x   D     x-D
3 x w 2 w x_w-2_w

你可以用do.call或者lift等方法替换调用。
你的函数并没有完全达到你想要的效果,请阅读上面的注释。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接