2
set.seed(3)
library(dplyr)
x <- tibble(Measure = c("Height","Weight","Width","Length"),
        AD1_1= rpois(4,10),
        AD1_2= rpois(4,9),
        AD2_1= rpois(4,10),
        AD2_2= rpois(4,9),
        AD3_1= rpois(4,10),
        AD3_2= rpois(4,9))

假设我有看起来像这样的数据。我希望为每个 AD 运行一个函数,并与下划线数字配对,即 AD1fun、AD2fun、AD3fun。

而不是写作,

fun <- function(x,y){x-y}
dat %>%
mutate(AD1fun = fun(AD1_1,AD1_2),
       AD2fun = fun(AD2_1,AD2_2),
...)

使用 dplyr 查找配对列的差异表明

x_minus <- x %>%
  mutate(fun(across(ends_with("_1"), .names = "{col}_minus"), across(ends_with("_2")))) %>%
  rename_with(~ sub("_\\d+", "", .), ends_with("_minus"))

可用于生产

# A tibble: 4 x 10
  Measure AD1_1 AD1_2 AD2_1 AD2_2 AD3_1 AD3_2 AD1_minus AD2_minus AD3_minus
  <chr>   <int> <int> <int> <int> <int> <int>     <int>     <int>     <int>
1 Height      6    10    10     3    12     8        -4         7         4
2 Weight      8     9    13     6    14     7        -1         7         7
3 Width      10     9    11     5    12     8         1         6         4
4 Length      8     9     8     7     8    13        -1         1        -5

但是,如果我们要使非操作功能,

fun <- function(x,y){
  case <- case_when(
    x == y ~ "Agree",
    x == 0 & y != 0 ~ "Disagreement",
    x != 0 & y == 0 ~ "Disagreement",
    x-y <= 1 & x-y >= -1 ~ "Agree",
    TRUE ~ "Disagree"
  )
  return(case)
}

x_case <- x %>%
  mutate(fun(across(ends_with("_1"), .names = "{col}_case"), across(ends_with("_2")))) %>%
  rename_with(~ sub("_\\d+", "", .), ends_with("_case"))

它将产生一个错误,因为引用,

此过程实质上意味着您比较两个数据集:一个带有以 _1 结尾的变量,另一个带有 _2。因此,它与 dat %>% select(ends_with("_1")) - dat %>% select(ends_with("_2")) 相同。由于这些是列表,因此您无法以这种方式比较它们

如果是这样,可以做些什么来包含使用cross() 的函数?

4

1 回答 1

3

我们可以循环across名称为ends_with“_1”的列,然后用于cur_column()提取列名,将后缀部分替换为_2get值并将其用作fun当前列的参数和对应的对_2

library(dplyr)
library(stringr)
x %>% 
   mutate(across(ends_with("_1"), ~
     fun(., get(str_replace(cur_column(), "_1$", "_2"))), .names = "{.col}_case"))

-输出

# A tibble: 4 x 10
#  Measure AD1_1 AD1_2 AD2_1 AD2_2 AD3_1 AD3_2 AD1_1_case AD2_1_case AD3_1_case
#  <chr>   <int> <int> <int> <int> <int> <int> <chr>      <chr>      <chr>     
#1 Height      6    10    10     3    12     8 Disagree   Disagree   Disagree  
#2 Weight      8     9    13     6    14     7 Agree      Disagree   Disagree  
#3 Width      10     9    11     5    12     8 Agree      Disagree   Disagree  
#4 Length      8     9     8     7     8    13 Agree      Agree      Disagree  

或者另一种选择是split.default/map。在这里,我们将数据集拆分listdata.frame具有与列名相同前缀的每个数据集,然后fun在每个list元素上应用 withmap/reduce并将输出绑定回原始数据集bind_cols

library(purrr)
x %>% 
  select(-Measure) %>% 
  split.default(str_remove(names(.), "_\\d+$")) %>%
  map_dfr(reduce, fun) %>% 
  rename_all(~ str_c(., "_case")) %>%
  bind_cols(x, .)

-输出

# A tibble: 4 x 10
#  Measure AD1_1 AD1_2 AD2_1 AD2_2 AD3_1 AD3_2 AD1_case AD2_case AD3_case
#  <chr>   <int> <int> <int> <int> <int> <int> <chr>    <chr>    <chr>   
#1 Height      6    10    10     3    12     8 Disagree Disagree Disagree
#2 Weight      8     9    13     6    14     7 Agree    Disagree Disagree
#3 Width      10     9    11     5    12     8 Agree    Disagree Disagree
#4 Length      8     9     8     7     8    13 Agree    Agree    Disagree

关于OP的方法,fun不是Vectorized。如果我们这样做,它可以应用于多个成对列

x %>%
  mutate(Vectorize(fun)(across(ends_with("_1"), 
         .names = "{col}_minus"), across(ends_with("_2"))))%>%
   do.call(data.frame, .) %>% 
   rename_at(vars(contains('minus')),
         ~ str_extract(., 'AD\\d+_\\d+_minus'))
#  Measure AD1_1 AD1_2 AD2_1 AD2_2 AD3_1 AD3_2 AD1_1_minus AD2_1_minus AD3_1_minus
#1  Height     6    10    10     3    12     8    Disagree    Disagree    Disagree
#2  Weight     8     9    13     6    14     7       Agree    Disagree    Disagree
#3   Width    10     9    11     5    12     8       Agree    Disagree    Disagree
#4  Length     8     9     8     7     8    13       Agree       Agree    Disagree
于 2021-01-22T20:56:41.523 回答