使用dplyr和purrr重复变异变量

5
我是自学R语言的,这是我在StackOverflow上第一次提问。如果我的问题很简单,请多多包涵。
问题简述:我编写了一个自定义函数来计算变量年度百分比变化。我想使用purrr的map_at函数将我的自定义函数应用于变量名称向量。当我将自定义函数应用于单个变量时,它可以正常工作,但在使用map_a链接时失败。
我的自定义函数如下:
calculate_delta <- function(df, col) {

  #generate variable name
  newcolname = paste("d", col, sep="")

  #get formula for first difference.
  calculate_diff <- lazyeval::interp(~(a + lag(a))/a, a = as.name(col))

  #pass formula to mutate, name new variable the columname generated above
  df %>% 
        mutate_(.dots = setNames(list(calculate_diff), newcolname)) }

当我将此函数应用于mtcars数据集中的单个变量时,输出结果如预期所示(尽管显然结果的含义是无意义的)。

calculate_delta(mtcars, "wt")

尝试使用Purrr将函数应用于字符向量

我认为我在理解map_at如何传递参数给函数方面遇到了困难。我在网上找到的所有示例片段都使用像is.character这样不需要其他参数的函数进行map_at操作。以下是我尝试使用purrr应用函数的方法。

vars <- c("wt", "mpg")
mtcars %>% map_at(vars, calculate_delta)

这给我返回了以下错误信息:

Error in paste("d", col, sep = "") : argument "col" is missing, with no default

我猜测这是因为map_at将vars作为df传递,但没有传递col参数。为了解决这个问题,我尝试了以下方法:

vars <- c("wt", "mpg") 
mtcars %>% map_at(vars, calculate_delta, df = .)

这让我出现了这个错误:
Error: unrecognised index type

我已经尝试了很多不同版本,包括从calculate_delta函数中删除df参数,但是都没有成功。

其他可能的解决方案

1)使用sapply而不是purrr的版本。 我尝试过这种方法来解决问题,但是遇到了类似的问题。我的目标是找出一种使用purrr的方法,如果可能的话。 根据我对purrr的理解,这似乎是一个典型的用例。

2)显然,我可以想到如何使用for循环来实现这一点,但是出于类似的原因,我正在尽可能避免这样做。

显然,我的思考方式是错误的。 请帮忙!

编辑1

为了澄清,我想知道是否有一种重复转换变量的方法,可以完成以下两件事情。

1)在原始的tbl_df中生成新变量,而不替换被突变的列(就像使用dplyrmutate_at时所发生的那样)。

2)自动生成新的变量标签。

3)如果可能的话,通过使用map_at应用单个函数来完成我所描述的内容。

可能这是不可能的,但我觉得应该有一种优雅的方法来实现我所描述的内容。


2
你的函数还没有准备好放在 mutate 或类似的结构中。尝试 mtcars %>% mutate(calculate_delta(wt)),即使没有 purrrmap,它也无法工作。如果在普通的 dplyr 调用中无法工作,则在该结构中也无法工作。它应该被重新编写。可以从删除数据框规范的必要性开始。考虑一下 summean 不需要数据框作为调用的一部分,它们是为向量构建的。 - Pierre L
谢谢,这是一个有帮助的思路来解决这个问题。@PierreLafortune 下面的这个函数可以作为 dplyr mutate 调用的一部分:delta <- function(x) (x + dplyr::lag(x)) /x,它也可以与 purrr 一起使用。正如我在下面提到的,让我困扰的部分是动态地重命名变量。 - Sean Williams
1个回答

10

尝试简化该过程:

delta <- function(x) (x + dplyr::lag(x)) /x
cols <- c("wt", "mpg")

#This
library(dplyr)
mtcars %>% mutate_at(cols, delta)
#Or
library(purrr)
mtcars %>% map_at(cols, delta)

#If necessary, in a function
f <- function(df, cols) {
  df %>% mutate_at(cols, delta)
}

f(iris, c("Sepal.Width", "Petal.Length"))
f(mtcars, c("wt", "mpg"))

编辑

如果您想在后面嵌入新的名称,我们可以编写一个自定义管道函数:

Rename <- function(object, old, new) {
  names(object)[names(object) %in% old] <- new
  object
}

mtcars %>% 
  mutate_at(cols, delta) %>% 
  Rename(cols, paste0("lagged",cols))

如果您想要重命名生成的滞后变量:

mtcars %>% mutate_at(cols, funs(lagged = delta))

感谢您的回复。这些解决方案大多数情况下可以产生我想要的结果,但是它们是通过用延迟变量替换原始变量来实现的。[这篇帖子](https://dev59.com/YJjga4cB1Zd3GeqPQeCg)展示了一个在`mutate_each`内部动态重命名变量的方法,但我无法将字符向量作为传递给 vars的参数。 - Sean Williams
你不必动态重命名。只需在之后重命名即可。或者如果需要在管道中使用,请编写自定义函数。 - Pierre L
再次感谢你,皮埃尔。你所描述的方法有一个缺点,就是用滞后变量替换被改变的变量。正如我在原帖的“编辑1”中所描述的那样,我的目标是在不替换原始变量的情况下应用函数,并通过动态生成名称来完成单个步骤。 - Sean Williams
2
如果给出后缀名以添加,mutate_at不必替换列:mtcars%>% mutate_at(cols,funs(lagged = delta)) - aosmith
@aomsith 很棒,这正是我正在寻找的。谢谢! - Sean Williams

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