在函数中使用“mutate()”将默认值拼接到数据框中的正确方法?

问题描述 投票:0回答:2

背景:

我的最终目标是这里指定的 json 文件:Trimble Batch Geocoding JSON Spec。我引用了该规范中与地址相关的部分,希望它能澄清我正在尝试做的事情。

问题:

如何从

mutate()
内的向量中拼接默认值?

非工作功能

我有一个函数,它接受具有可变列数的数据帧。某些列(由

A_dots
表示)可能是不同的命名字段,并且并不总是存在(例如,来自 Trimble Geocode 链接:使用“Zip”而不是“StreetAddress”),但总会有一个或多个字段,并且我在函数中使用
...
涵盖了它们。还有一些其他字段(由
B, C, D
表示,可能 出现在数据框中(例如“GeoList”或“Region”),但大多数情况下不会出现,并且可以默认表示函数的
formals()
中定义的值。这是我的 not-working 函数,我知道它不起作用,因为我不知道如何正确调用
mutate()
来拼接默认值 (这确实是我的问题):

library(tidyverse)  # solution does not need to be tidyverse

myfun <- function(df,
                   ...,
                   B = 4,
                   C = TRUE,
                   D = "a_default_value"){

  vec_field_defaults <- c("B", "C", "D")
  vfd_match <- intersect(vec_field_defaults, names(df))
  vfd_nomatch <- setdiff(vec_field_defaults, names(df))

  if (length(vfd_match) > 0){
    df2 <- df[,vfd_match]
    if (length(vfd_nomatch) > 0){
      df2 <- mutate(df2, !!!vfd_nomatch := !!!vfd_nomatch)  # I need help here
    }
  } else {
    df2 <- data.frame(B = rep(B, nrow(df)),
                      C = rep(C, nrow(df)),
                      D = rep(D, nrow(df)))  # Could this be done better?
  }

  return(df2)

示例

如果我的意图从上面的函数中不清楚,你只需要阅读这一点。

基线数据框

这是一个起始数据框,我将把它切开以用于下面的示例。

df_baseline <- data.frame(A = c("Address1", "Address2", "Address3"),
                      B = c(1L, 2L, 3L),
                      C = c(TRUE, FALSE, FALSE),
                      D = c("Some", "different", "values."))

> df_baseline
    A_dots B     C         D
1 Address1 1  TRUE      Some
2 Address2 2 FALSE different
3 Address3 3 FALSE   values.

A_dots
栏:

myfun()
应始终删除
A_dots
列(但保留它代表输出的行数!)并填写
B, C, D
的默认值(在提供的数据框中未给出这些值的地方):

df_1 <- select(df_baseline, A_dots)

> myfun(df_1)
  B    C               D
1 4 TRUE a_default_value
2 4 TRUE a_default_value
3 4 TRUE a_default_value

保留
A_dots, B
列:

df_2 <- select(df_baseline, A_dots, B)

myfun(df_2)  # function breaks, but showing intended output:
  B    C               D
1 1 TRUE a_default_value
2 2 TRUE a_default_value
3 3 TRUE a_default_value

保留
A_dots, B, C
列:

df_3 <- select(df_baseline, A_dots, B, C)

myfun(df_3)  # function breaks, but showing intended output:
  B    C               D
1 1 TRUE  a_default_value
2 2 FALSE a_default_value
3 3 FALSE a_default_value


保留
A_dots, B, C, D
列:

df_4 <- select(df_baseline, A_dots, B, C, D)

myfun(df_4)  # function breaks, but showing intended output:
  B    C     D
1 1 TRUE  Some
2 2 FALSE different
3 3 FALSE values.

我认为我的问题只是如何为缺少的默认值调用

!!!
,但我不确定,如果有任何帮助,我将不胜感激。谢谢你。

r dplyr rlang
2个回答
0
投票

这是一个有点 hacky 的版本,它使用 rlang::fn_fmls 来获取默认参数列表并使用它们。

library(dplyr)

myfun <- function(df,
                  ...,
                  B = 4,
                  C = TRUE,
                  D = "a_default_value"){
  
  vec_field_defaults <- c("B", "C", "D")
  vfd_match <- intersect(vec_field_defaults, names(df))
  vfd_nomatch <- setdiff(vec_field_defaults, names(df))
  # get a named list of the default arguments that are missing
  list_of_needed_defaults <- rlang::fn_fmls()
  list_of_needed_defaults <- list_of_needed_defaults[vfd_nomatch]
  
  if (length(vfd_match) > 0){
    df2 <- df[,vfd_match, drop = FALSE]
    if (length(vfd_nomatch) > 0){
      # create a df with the missing default arguments and add them to the
      # remaining df
      df_temp <- do.call(bind_cols, list_of_needed_defaults)
      df_temp <- df_temp[rep(1, nrow(df)), ]
      df2 <- bind_cols(df2, df_temp)
    }
  } else {
    df2 <- data.frame(B = rep(B, nrow(df)),
                      C = rep(C, nrow(df)),
                      D = rep(D, nrow(df))) 
  }
  
  return(df2)
}

df_baseline <- data.frame(A_dots = c("Address1", "Address2", "Address3"),
                          B = c(1L, 2L, 3L),
                          C = c(TRUE, FALSE, FALSE),
                          D = c("Some", "different", "values."))

df_2 <- select(df_baseline, A_dots, B)
myfun(df_2)
#>   B    C               D
#> 1 1 TRUE a_default_value
#> 2 2 TRUE a_default_value
#> 3 3 TRUE a_default_value

reprex 包于 2023 年 9 月 1 日创建(v1.0.0)


0
投票

我们可以使用命名列表在

mutate
中创建新列。创建此列表的一种方法是在列名称的命名向量上使用
lapply
并使用参数中的默认值:

get

其中 

lapply(vec_field_defaults2, \(x) rep(get(x), nrow(df)))

vec_field_defaults2
。或者,我们可以使用
setNames(vec_field_defaults, nm = vec_field_defaults)
,它会自动以其自身命名输入向量。
这是完整的代码。

purrr::set_names(vec_field_defaults)

创建于 2023-09-01,使用 

reprex v2.0.2

© www.soinside.com 2019 - 2024. All rights reserved.