【问题标题】:R: Custom Function - Mutate Existing ColumnR:自定义函数 - 改变现有列
【发布时间】:2020-11-10 06:09:13
【问题描述】:

我想创建一个自定义函数来尝试标准化多个不同列、多个不同数据框中的字符串,最终目的是将它们中的数据连接在一起。

为了做到这一点,我希望能够将列名传递给自定义函数,并让该函数对该列执行操作。在下面的示例中,我想在将列 ac 连接在一起之前清理它们,使其看起来像这样:

library(tidyverse)

df1 <- tibble(a = c("apple & pear", "kiwi", "plum"), b = c("cat", "dog", "cow"))
df2 <- tibble(c = c("apple and pear", "kiwi.", "plum"), d = c("car", "bike", "truck")) 

full_join(df1, df2, by = c("a" = "c") )

  a              b     d    
1 apple & pear   cat   car   
2 kiwi           dog   bike   
3 plum           cow   truck

而不是现在的结果,是这样的:

# A tibble: 5 x 3
  a              b     d
1 apple & pear   cat   NA   
2 kiwi           dog   NA   
3 plum           cow   truck
4 apple and pear NA    car  
5 kiwi.          NA    bike 

为此,我知道我需要构建自定义函数,但我相对缺乏经验,尤其是使用curly-curly。下面的两个函数应该更改符号并删除尾随标点符号,理想情况下应该将它们组合成一个函数,如果需要,可以灵活地添加更多函数,如下所示:

add_symbol <- function(col.name){
  mutate({{col.name}} = gsub(" & ", " and ", {{col.name}}))
}

rm_trail_punc <- function(col.name){
  mutate({{col.name}} = gsub("[[:punct:]]$", "", {{col.name}}))
}

standardise_col <- function(df, col.name){
  df %>%
    add_symbol({{col.name}}) %>%
    rm_trail_punc({{col.name}})
}

df1 <- standardise_col(df1)
standardise_col(df2) %>%
  full_join(., df1, by = c("a" = "c"))

但是,这些函数无法创建,并返回错误unexpected '=',因为无法将列名传递到等号的左侧。有什么方法可以将这些值传递给mutate 而无需对其进行硬编码?

【问题讨论】:

  • 请提供更好的样本数据,你有 df1 & df2,然后加入后你的数据集中突然有 Cat、Cow 和 dog,而 df2 无处出现
  • 抱歉,在原始代码中进行了更改,但未在此处正确发布内容。现在更新了

标签: r dplyr tidyverse


【解决方案1】:

我认为您可以通过以下方式更简单地实现这一目标:

library(dplyr)

clean_func <- function(df){
    df %>% mutate(across(everything(), ~gsub(" & ", " and ", .) %>% 
                    gsub("[[:punct:]]$", "", .))) 
    }

df1 <- clean_func(df1)
df2 <- clean_func(df2)

您可以根据需要添加额外的gsubstr_replace 或其他调用来更新函数。

编辑:

根据更新,您可以执行以下操作来专门针对您的变量:

add_symbol <- function(col.name){
  gsub(" & ", " and ", col.name)
}

rm_trail_punc <- function(col.name){
  gsub("[[:punct:]]$", "", col.name)
}

standardise_col <- function(df, col.name){
  
    col.name <- enquo(col.name)
    
  df %>% 
    mutate(!!col.name := add_symbol(!!col.name),
           !!col.name := rm_trail_punc(!!col.name))
}

你的代码永远不会像写的那样工作,但你可以这样做:

new_df <- standardise_col(df1, a) %>% 
left_join(., standardise_col(df2, c), by = c("a"="c"))

这给了我们:

# A tibble: 3 x 3
  a              b     d    
  <chr>          <chr> <chr>
1 apple and pear cat   car  
2 kiwi           dog   bike 
3 plum           cow   truck

你可以在这里阅读整洁的评估:https://tidyeval.tidyverse.org/dplyr.html

【讨论】:

  • 对原始问题中的不良数据表示歉意,并感谢您的回答。寻找一些更有针对性的东西。
  • 不用担心 - 我添加了一个新的解决方案,希望能满足您的目的
  • 完美,干得好。实际上之前有类似的东西,但不知道:= 运算符。
【解决方案2】:

正如@1k monkeys 和一台 PC 的评论中所说,您的示例数据与您显示的不同,因此结果可能会有所不同,但我们假设您有一些这样的数据:

df1 <- tibble(a = c("apple & pear", "kiwi", "plum"),
              b = c("cat","dog","cow")) 
df2 <- tibble(c = c("apple and pear", "kiwi.", "orange"),
              d = c("truck","bike","car")) 

你可以设法使用包fuzzyjoin来合并它们:

library(fuzzyjoin)
library(dplyr)
df1 %>% 
stringdist_full_join(df2, by = c(a = "c") ,
                          max_dist = 3,
                          distance_col = "DIST")

# A tibble: 4 x 5
  a            b     c              d      DIST
  <chr>        <chr> <chr>          <chr> <dbl>
1 apple & pear cat   apple and pear truck     3
2 kiwi         dog   kiwi.          bike      1
3 plum         cow   <NA>           <NA>     NA
4 <NA>         <NA>  orange         car      NA

结果不同,因为我根据您的示例提供了数据,并且“plum”和“orange”不匹配(因此牛和汽车未对齐)。显然,使用select() 可以选择所需的列,或者使用mutate() 可以重命名它们。

【讨论】:

  • 关于数据的道歉,现在更新了。感谢您的回答,但字符串距离连接并不真正适用于现实生活中的用例,因为数据的大小使得之后手动对类似连接进行重复数据删除的时间过长。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
相关资源
最近更新 更多