Как программно рассчитать разницу между парой переменных с помощью dplyr?

#r #dplyr

Вопрос:

Допустим, у меня есть проблема, подобная следующей:

 tib <- tribble(~id, ~var1_countrya, ~var2_countrya, ~var1_countryb, ~var2_countryb,
1, 1, 2, 1, 2,
2, 5, 3, 1, 3,
3, 6, 3, 1, 3
)
 

Я хотел бы создать новые столбцы delta_countrya = var2_countrya - var1_countrya и delta_countryb = var2_countryb - var2_countryb , выбрав пары переменных, используя регулярное выражение и каналы dplyr. Финальная игра должна выглядеть так

 id var1_countrya var2_countrya delta_countrya var1_countryb var2_countryb delta_countryb
1             1             2              1             1             2              1
2             5             3             -2             1             3              2
3             6             3             -3             1             3              2
 

Я изо всех сил пытался даже настроить предложение mutate таким образом, чтобы оно создавало несколько новых переменных, не говоря уже о том, чтобы найти пары переменных, которые я ищу, и учесть разницу.

Ответ №1:

В вашем примере вы можете преобразовать свои столбцы в переменные, разделить столбцы так, чтобы var и страна были разделены, затем повторно объединить, чтобы разные переменные находились в столбцах, и, наконец, вычислить дельту

 library(dplyr) 
tib <- tribble(~id, ~var1_countrya, ~var2_countrya, ~var1_countryb, ~var2_countryb,
1, 1, 2, 1, 2,
2, 5, 3, 1, 3,
3, 6, 3, 1, 3
)  # your code had an error, need to replace ')' with comma

tib2 = tib %>%
      pivot_longer(-id, names_to = "var") %>%
      separate(var, c("Var", "Country")) %>%
      pivot_wider(names_from = Var) %>%
      mutate(Delta = var2-var1)
tib2
# # A tibble: 6 x 5
#      id Country   var1  var2 Delta
#    <dbl> <chr>    <dbl> <dbl> <dbl>
# 1     1 countrya     1     2     1
# 2     1 countryb     1     2     1
# 3     2 countrya     5     3    -2
# 4     2 countryb     1     3     2
# 5     3 countrya     6     3    -3
# 6     3 countryb     1     3     2
 

Комментарии:

1. Отлично, это сработало как заклинание. Очевидно, что затем нам понадобится pivot_wider, чтобы получить окончательный результат.

Ответ №2:

base

 tib[paste0("delta_country", letters[1:2])] <- tib[paste0("var2_country", letters[1:2])] - tib[paste0("var1_country", letters[1:2])]
    #      id var1_countrya var2_countrya var1_countryb var2_countryb delta_countrya delta_countryb
    #   <dbl>         <dbl>         <dbl>         <dbl>         <dbl>          <dbl>          <dbl>
    # 1     1             1             2             1             2              1              1
    # 2     2             5             3             1             3             -2              2
    # 3     3             6             3             1             3             -3              2
 

tidyverse

 library(tidyverse)
tib[paste0("delta_country", letters[1:2])] <- map2(tib %>% select(starts_with("var2_country")), tib %>% select(starts_with("var1_country")),
                                     ~.x - .y)
tib
# #      id var1_countrya var2_countrya var1_countryb var2_countryb delta_countrya delta_countryb
# #   <dbl>         <dbl>         <dbl>         <dbl>         <dbl>          <dbl>          <dbl>
# # 1     1             1             2             1             2              1              1
# # 2     2             5             3             1             3             -2              2
# # 3     3             6             3             1             3             -3              2
 

Ответ №3:

Мы также можем воспользоваться base::Reduce здесь:

 Reduce(function(x, y) {
  cbind(x, x[, y] - x[, y - 1])
}, seq(1, ncol(tib), 2)[-1], init = tib) |>
  setNames(c(names(tib), paste0("delta_country", unique(gsub(".*([[:alpha:]]$)", "\1", names(tib)[-1])))))

  id var1_countrya var2_countrya var1_countryb var2_countryb delta_countrya delta_countryb
1  1             1             2             1             2              1              1
2  2             5             3             1             3             -2              2
3  3             6             3             1             3             -3              2