跨多个列进行变异以创建新的变量集

Mutate across multiple columns to create new variable sets

我有一个国家和年份级别的面板数据集,我想在现有变量的基础上创建两个新变量。

year country var1 var2 var3 var 4 mean_var1 relmean_var1
1910 GER 1 4 10 6 3 0.333
1911 GER 2 3 11 7 1.5 1.3333
1910 FRA 5 6 8 9 3 1.66667
1911 FRA 1 4 10 9 1.5 .66667

我想做的是创建两个新变量集:(1) 每年(各个国家/地区)平均值的变量集和 (2) 相对于年份的国家/地区值变量集-平均的。例如,对于 var1(1) 会产生 mean_var1 和 (2) relmean_var1 并且我想要这些用于所有其他变量。数据集中总共有 1000 多个变量,但我只会将此函数应用于大约 6 个。

我有适用于第一部分的代码,但我想尽可能有效地将它与第二部分结合起来。

library(dplyr)
library(purrr)
df<- df%>% 
            group_by(year) %>%
            mutate_at(.funs = list(mean = ~mean(.)), .vars = c("var1", "var1", "var1", "var4"))

此代码生成名为 var1_mean 的新变量(我更喜欢 mean_var1:如何更改此名称?)

第二步,我试过:

df <- df %>%
map2_dfr(.x = d.test %>%
            select(var1, var2),
          .y = d.test %>%
            select(var1_mean, var2_mean), 
          ~ .x / .y) %>%
   setNames(c("relmean_var1", "relmean_var2"))

我收到错误

""Error in select(., var1, var2) : object 'd.test' not found."

。 (我从 得到这个设置)

我也试过:

 map2(var1, var1_mean, ~ df[[.x]] / df[[.y]]) %>% 
   set_names(cols) %>% 
   bind_cols(df, .)

得到了

"Error in map2(var1, var1_mean, ~df[[.x]]/df[[.y]]) : object 'var1' not found

结合这两个目标的最佳方式是什么?理想情况下,(1) 的命名方案 mean_var1 和 (2)

的 relmean_var1

编辑: 输入数据框应如下所示:

data <- tibble::tribble(
  ~year, ~country, ~var1, ~var2, ~var3, ~var.4,
  1910L,    "GER",    1L,    4L,   10L,     6L,
  1911L,    "GER",    2L,    3L,   11L,     7L,
  1910L,    "FRA",    5L,    6L,    8L,     9L,
  1911L,    "FRA",    1L,    4L,   10L,     9L
)

输出数据框应如下所示(对于所有变量,仅以 var1 为例,但 var2 到 var4 的格式应相同):

datanew  <- tibble::tribble(
  ~year, ~country, ~var1, ~var2, ~var3, ~var.4, ~mean_var1 , ~relmean_var1
  1910L,    "GER",    1L,    4L,   10L,     6L,     3L,        .3333L,
  1911L,    "GER",    2L,    3L,   11L,     7L,     1.5L,     1.3333L,
  1910L,    "FRA",    5L,    6L,    8L,     9L,     3L,       1.6667L,
  1911L,    "FRA",    1L,    4L,   10L,     9L      1.5L,      .6667L,
)
library(tidyverse)

data <- tibble::tribble(
  ~year, ~country, ~var1, ~var2, ~var3, ~var.4,
  1910L,    "GER",    1L,    2L,   10L,     6L,
  1911L,    "GER",    2L,    3L,   11L,     7L,
  1910L,    "FRA",    5L,    6L,    8L,     9L,
  1911L,    "FRA",    1L,    3L,   10L,     9L
)

data_long <-
  data %>%
  pivot_longer(-c(year, country))

data_long
#> # A tibble: 16 x 4
#>     year country name  value
#>    <int> <chr>   <chr> <int>
#>  1  1910 GER     var1      1
#>  2  1910 GER     var2      2
#>  3  1910 GER     var3     10
#>  4  1910 GER     var.4     6
#>  5  1911 GER     var1      2
#>  6  1911 GER     var2      3
#>  7  1911 GER     var3     11
#>  8  1911 GER     var.4     7
#>  9  1910 FRA     var1      5
#> 10  1910 FRA     var2      6
#> 11  1910 FRA     var3      8
#> 12  1910 FRA     var.4     9
#> 13  1911 FRA     var1      1
#> 14  1911 FRA     var2      3
#> 15  1911 FRA     var3     10
#> 16  1911 FRA     var.4     9

means_country <-
  data_long %>%
  group_by(country) %>%
  summarise(mean_country_value = mean(value))

means_years <-
  data_long %>%
  group_by(year) %>%
  summarise(mean_year_value = mean(value))

data %>%
  left_join(means_country) %>%
  left_join(means_years)
#> Joining, by = "country"
#> Joining, by = "year"
#> # A tibble: 4 x 8
#>    year country  var1  var2  var3 var.4 mean_country_value mean_year_value
#>   <int> <chr>   <int> <int> <int> <int>              <dbl>           <dbl>
#> 1  1910 GER         1     2    10     6               5.25            5.88
#> 2  1911 GER         2     3    11     7               5.25            5.75
#> 3  1910 FRA         5     6     8     9               6.38            5.88
#> 4  1911 FRA         1     3    10     9               6.38            5.75

reprex package (v2.0.1)

于 2021-11-24 创建

这在长格式中可能更容易,但这里有一个您可以选择宽数据的选项。

使用最新版本的 dplyr,您可以 mutate across 并包含 .names 参数来定义您希望新列的外观。

library(tidyverse)

my_col <- c("var1", "var2", "var3", "var4")

df %>%
  group_by(year) %>%
  mutate(across(my_col, mean, .names = "mean_{col}")) %>%
  mutate(across(my_col, .names = "relmean_{col}") / across(paste0("mean_", my_col)))

输出

   year country  var1  var2  var3  var4 mean_var1 mean_var2 mean_var3 mean_var4 relmean_var1 relmean_var2 relmean_var3 relmean_var4
  <int> <chr>   <int> <int> <int> <int>     <dbl>     <dbl>     <dbl>     <dbl>        <dbl>        <dbl>        <dbl>        <dbl>
1  1910 GER         1     4    10     6       3         5         9         7.5        0.333        0.8          1.11         0.8  
2  1911 GER         2     3    11     7       1.5       3.5      10.5       8          1.33         0.857        1.05         0.875
3  1910 FRA         5     6     8     9       3         5         9         7.5        1.67         1.2          0.889        1.2  
4  1911 FRA         1     4    10     9       1.5       3.5      10.5       8          0.667        1.14         0.952        1.12

这是@danlooo 方法的扩展,因此国家/地区级别的平均值和年份级别的平均值位于同一数据集中(如果需要的话)。显着的区别是在管道链中使用两个 mutate,而不是两个 summarize,然后加入。

考虑一下你是否真的想要它再次变宽。通常保持较长时间更容易(例如,删除对tidyr::pivot_wider()的最后调用)。

ds <- tibble::tribble(
  ~year, ~country, ~var1, ~var2, ~var3,  ~var4,
  1910L,    "GER",    1L,    4L,   10L,     6L,
  1911L,    "GER",    2L,    3L,   11L,     7L,
  1910L,    "FRA",    5L,    6L,    8L,     9L,
  1911L,    "FRA",    1L,    4L,   10L,     9L
)  

ds |> 
  dplyr::mutate(
    year = as.character(year)   # To help the pivot below
  ) |> 
  tidyr::pivot_longer(
    cols         = -c(year, country), 
    names_to     = "key",
    names_prefix = "^var"
  ) |> 
  dplyr::group_by(country, key) |> 
  dplyr::mutate(
    m_c   = mean(value),  # Mean for the Country (and variable)
    r_c   = value / m_c,  # Relative mean for the Country (and variable)
  ) |> 
  dplyr::ungroup() |> 
  dplyr::group_by(year, key) |> 
  dplyr::mutate(
    m_y   = mean(value),  # Mean for the Year (and variable)
    r_y   = value / m_y,  # Relative mean for the Year (and variable)
  ) |> 
  dplyr::ungroup() |> 
  dplyr::mutate(
    year  = as.integer(year)  # Return it to a number
  ) |> 
  tidyr::pivot_wider(
    id_cols = c(year, country),
    names_from = key,
    names_glue = "{.value}_{key}",
    values_from = c(value, m_c, r_c, m_y, r_y)
  )

输出(宽)我更喜欢像@danlooo 这样的较长的描述性变量名称,但我希望所有内容都适合 SO 屏幕:

   year country value_1 value_2 value_3 value_4 m_c_1 m_c_2 m_c_3 m_c_4 r_c_1 r_c_2 r_c_3 r_c_4 m_y_1 m_y_2 m_y_3 m_y_4 r_y_1 r_y_2 r_y_3 r_y_4
  <int> <chr>     <int>   <int>   <int>   <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1  1910 GER           1       4      10       6   1.5   3.5  10.5   6.5 0.667 1.14  0.952 0.923   3     5     9     7.5 0.333 0.8   1.11  0.8  
2  1911 GER           2       3      11       7   1.5   3.5  10.5   6.5 1.33  0.857 1.05  1.08    1.5   3.5  10.5   8   1.33  0.857 1.05  0.875
3  1910 FRA           5       6       8       9   3     5     9     9   1.67  1.2   0.889 1       3     5     9     7.5 1.67  1.2   0.889 1.2  
4  1911 FRA           1       4      10       9   3     5     9     9   0.333 0.8   1.11  1       1.5   3.5  10.5   8   0.667 1.14  0.952 1.12 

输出(长--没有最后的tidyr::pivot_wider()

# A tibble: 16 x 8
    year country key   value   m_c   r_c   m_y   r_y
   <int> <chr>   <chr> <int> <dbl> <dbl> <dbl> <dbl>
 1  1910 GER     1         1   1.5 0.667   3   0.333
 2  1910 GER     2         4   3.5 1.14    5   0.8  
 3  1910 GER     3        10  10.5 0.952   9   1.11 
 ...
15  1911 FRA     3        10   9   1.11   10.5 0.952
16  1911 FRA     4         9   9   1       8   1.12