如何缩短这个长的 dplyr 语法?

How to shorten this long dplyr syntax?

小题大做,我希望能够更正变量 nbeta_dep01nbeta_dep02 ...

所取的某些值

下面是我正在做的一个可重现的例子。

我想知道是否有缩短语法的方法(因为在我的示例中,我复制并粘贴了与变量 nbeta_depXX 一样多的修正指令)

suppressMessages(library(dplyr))

test <- tribble(
  ~ent, ~dep_impl, ~nbeta_dep01, ~nbeta_dep02, ~nbeta_dep03, ~nbeta_dep04, ~nbeta_dep05,
  "a",  "01",  0,   0,   0,   0,   0,  
  "b",  "03",  2,   0,   3,   0,   1,
  "c",  "05",  0,   0,   0,   1,   0,
  "d",  "02",  0,   0,   0,   0,   0
)

test %>% 
  rowwise() %>% 
  mutate(
    nbeta_dep01 = ifelse(
      nbeta_dep01==0 & nbeta_dep02==0 & nbeta_dep03==0 & nbeta_dep04==0 & nbeta_dep05==0 & dep_impl=="01",
      1,
      nbeta_dep01),
    nbeta_dep02 = ifelse(
      nbeta_dep01==0 & nbeta_dep02==0 & nbeta_dep03==0 & nbeta_dep04==0 & nbeta_dep05==0 & dep_impl=="02",
      1,
      nbeta_dep02),
    nbeta_dep03 = ifelse(
      nbeta_dep01==0 & nbeta_dep02==0 & nbeta_dep03==0 & nbeta_dep04==0 & nbeta_dep05==0 & dep_impl=="03",
      1,
      nbeta_dep03),
    nbeta_dep04 = ifelse(
      nbeta_dep04==0 & nbeta_dep02==0 & nbeta_dep03==0 & nbeta_dep04==0 & nbeta_dep05==0 & dep_impl=="04",
      1,
      nbeta_dep04),
  )
#> # A tibble: 4 x 7
#> # Rowwise: 
#>   ent   dep_impl nbeta_dep01 nbeta_dep02 nbeta_dep03 nbeta_dep04 nbeta_dep05
#>   <chr> <chr>          <dbl>       <dbl>       <dbl>       <dbl>       <dbl>
#> 1 a     01                 1           0           0           0           0
#> 2 b     03                 2           0           3           0           1
#> 3 c     05                 0           0           0           1           0
#> 4 d     02                 0           1           0           0           0
Created on 2021-10-25 by the reprex package (v2.0.1)

您可以使用函数 starts_with:

来引用名称以相同方式开头的列
test %>% 
  mutate(across(starts_with("nbeta"),
         ~ifelse(
      nbeta_dep01==0 & nbeta_dep02==0 & nbeta_dep03==0 & nbeta_dep04==0 & nbeta_dep05==0 & dep_impl=="01",
      1,
      nbeta_dep01)))

你可以使用

library(dplyr)
library(stringr)

test %>% 
  mutate(across(matches("dep\d+$"), 
       ~ifelse(rowSums(across(nbeta_dep01:nbeta_dep05)) == 0 & dep_impl == str_extract(cur_column(), "\d+$"),
               1,
               .x)))

这个returns

# A tibble: 4 x 7
  ent   dep_impl nbeta_dep01 nbeta_dep02 nbeta_dep03 nbeta_dep04 nbeta_dep05
  <chr> <chr>          <dbl>       <dbl>       <dbl>       <dbl>       <dbl>
1 a     01                 1           0           0           0           0
2 b     03                 2           0           3           0           1
3 c     05                 0           0           0           1           0
4 d     02                 0           1           0           0           0
  • 我们使用正则表达式识别要更改的列:"dep\d+$" 匹配所有以“dep”结尾且后跟两位数字的列。这些列用于 across() 函数。
  • if 语句被简化:因为所有 nbeta_dep 列都需要是 0 我们通过使用 rowSum 函数结合选择 across() 函数。此外,我们检查当前列名中的数字是否与列 dep_impl.
  • 中的数字匹配
  • 如果满足这些条件,我们 return 1 否则当前 column/row 中已有的值是 returned .x.

在没有 matches()rowSums() 的情况下也可以 rowwise:

test %>% 
rowwise %>% 
mutate(across(3:7, ~ifelse(grepl(dep_impl, cur_column()) && sum(across(3:7)) == 0, 1, .)))
# A tibble: 4 x 7
# Rowwise: 
  ent   dep_impl nbeta_dep01 nbeta_dep02 nbeta_dep03 nbeta_dep04 nbeta_dep05
  <chr> <chr>          <dbl>       <dbl>       <dbl>       <dbl>       <dbl>
1 a     01                 1           0           0           0           0
2 b     03                 2           0           3           0           1
3 c     05                 0           0           0           1           0
4 d     02                 0           1           0           0           0