使用复合在自定义构建函数中动态调用变量 dplyr (!!paste0, , as.name(), eval(parse(text=)

Posted

技术标签:

【中文标题】使用复合在自定义构建函数中动态调用变量 dplyr (!!paste0, , as.name(), eval(parse(text=)【英文标题】:using a composite to call a variable dynmically in custom build function with dplyr (!!paste0, , as.name(), eval(parse(text=)使用复合在自定义构建函数中动态调用变量 dplyr (!!paste0, , as.name(), eval(parse(text=) 【发布时间】:2021-10-30 08:53:32 【问题描述】:

这是对以下问题的延伸:(1)、(2) 并在 cmets 中由Mario Reutter 向(2) 提问。

library(dplyr, tidyverse)
string <- c("car", "train", 'bike', 'plain')
speed1 <- runif(4, min = 0, max = 10000)
speed2 <- runif(4, min = 0, max = 10000)
n1  <- sample(1:100, 4)
n1_plus  <- sample(1:100, 4)
n1_minus <- sample(1:100, 4)
n2  <- sample(1:100, 4)
df <- data.frame(string, speed1, speed2, n1, n2, n1_plus, n1_minus)

感谢akrun的answer我可以构建以下函数:

my_fun <- function(dataf, V1, V2)
dataf %>%
dplyr::mutate("V1_V2" := paste0(format(V1, big.mark   = ",") ,
  '\n(' , format(V2, big.mark   = ",") , ')'))

df<-df%>%my_fun(speed1, n1)

使用"V1_V2" := 定义的复合名称创建一个新变量。

但是,如何在等式右侧调用复合变量名称?例如。将format(V2, big.mark = ",") 替换为format('V2_plus', big.mark = ",")。我试过了(不工作):

my_fun <- function(dataf, V1, V2)
dataf %>%
dplyr::mutate("V1_V2_plus" := paste0(format(V1, big.mark   = ",") ,
  '\n(' , format('V2_plus', big.mark   = ",") , ')'))

df<-df%>%my_fun(speed1, n1)

所需的输出:我希望有一个新列 speed1_n1_plus 结合来自 speed1n1_plus 的值:

  string   speed1   speed2 n1 n2 n1_plus n1_minus       speed1_n1_plus
1    car 3958.415 1049.172 70 91      25       53 3,958.415\n(25)
2  train 6203.919 8639.160 52 92      14       91 6,203.919\n(14)
3   bike 2966.391 2997.303 35 55      46       61 2,966.391\n(46)
4  plain 2755.266 1627.379 98 66       8       49 2,755.266\n( 8)

我只需要对具有相似名称的多个变量进行操作。变量名称是“核心”名称(在本例中为“n1”,V2)以及后缀和前缀的组合。我想避免为每个变量名称添加额外的参数,因为它只为核心名称添加了一个后缀。

我正在尝试:!!paste0, as.name(), eval(parse(text=), ...,它可能在函数之外工作,但对我来说不在函数内部。

【问题讨论】:

你是否已经在数据中创建了_plus _plust 是我之前在数据中创建的后缀示例。可能是_SD_skew、……为什么?我不想改变我以前所做的一切的一般结构,但只有在我必须这样做的情况下。 即类似df$speed1_SD &lt;- 100000 嗯,它是一个数值向量;基本上是数据框中的一列。 我的意思是你在全局环境中有一个名为n1_plus 的向量对象,它不是数据的一部分。您是否希望将该对象创建为列(因为它不是“df”的一部分 【参考方案1】:
my_fun <- function(dataf, V1, V2)
           dataf %>%
              dplyr::mutate("V1_V2_plus" := paste0(format(V1, big.mark   = ","),
                  "\n(", format(!! rlang::sym(paste0(rlang::as_string(ensym(V2)), "_plus")), big.mark  = ","), ")"))

-测试

df %>%
  my_fun(speed1, n1)
 string   speed1    speed2 n1 n2 n1_plus n1_minus  speed1_n1_plus
1    car 4453.441 3336.7287 92 97      28       56 4,453.441\n(28)
2  train 7718.381  638.5120 82 61       9       13 7,718.381\n( 9)
3   bike 4648.093 4267.8390  7 92      83       29 4,648.093\n(83)
4  plain 3815.145  793.6886 18 56      30       46 3,815.145\n(30)

【讨论】:

@MsGISRocker 第二个选项会更动态 @MsGISRocker 在您的更新中,我也看到了n1_minus,它不是在原始数据中创建的。这也是你预期的一部分吗 n1_plus, n1_minus, ... 是通过后缀扩展 n1 的变量名称的变量示例。这将在函数中通过后缀扩展变量名称n1 来调用。 @MsGISRocker 是的,但是我发现在您的 globalenv 中创建的那些对象是向量,而不是“df”的一部分。因此,我希望该函数从 globalenv 中选择这些对象并创建为列。如果这是您的意思,请更新帖子 @arkun:我明天再看看,因为我现在该睡觉了。【参考方案2】:

我同意在mutate 内的赋值右侧使用变量名会很有帮助。未实现此功能的原因是您可以通过将data_frame 适当地格式化为更长的格式来更有效地执行此操作。

在我看来,speed1 & n1speed2 & n2 似乎是成对出现的。因此,您可以将 df 从包含 4 行(每个车辆类型,即汽车、火车等)转换为 8 行(每个车辆实例 ,即car1、car2等)。

在您的示例中,以这种较长格式构建 data_frame 会更容易,但由于您可能必须使用您指定格式的数据库,让我们重新格式化(注意:这非常乏味,因为有些信息存储在变量names中,需要转换回单个单元格):

df_long = df %>% pivot_longer(-string) %>% #expand on everything but the column "string" (super long format but we need this to grab the information from the column names)
  mutate(number = gsub("\\D+", "", name), name = gsub("\\d+", "", name)) #separate the numbers from the variable names

#separate speed and everything starting with "n" and get them into a wider format
df_n = df_long %>% filter(grepl("^n", name)) %>% pivot_wider(names_from=name)
df_rest = df_long %>% filter(grepl("^n", name)==F) %>% pivot_wider(names_from=name)

df_tidy = full_join(df_rest, df_n) #join the data frames together
View(df_tidy) #take a look how the df looks differently now (including explicit NAs since n2_plus and n2_minus don't exist in your example)

现在您可以简单地执行此操作以获得您想要的结果:

df_tidy = df_tidy %>% mutate(result = paste0(format(speed, big.mark=","), "\n(", format(n_plus, big.mark=","), ")"))

注意:选择更长的格式可能是有意义的,这样nn_plusn_minus 不是不同的列,而是在另一个列中编码n_kind,因子级别为“标准”,“加”和“减”。但我无法从你的例子中判断。

【讨论】:

有趣的想法。我可能会更深入地考虑它。

以上是关于使用复合在自定义构建函数中动态调用变量 dplyr (!!paste0, , as.name(), eval(parse(text=)的主要内容,如果未能解决你的问题,请参考以下文章

使用 dplyr 在自定义函数中无法识别默认参数

如何在自定义 R 函数中使用文本字符串作为变量

python中怎么在自定义函数调用另外一个函数中的参数

ThinkPHP模板之变量输出、自定义函数与判断语句用法

函数R中的Dplyr变量名称

如何将 dplyr 中的动态列名传递给自定义函数?