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

10

我有一个按国家和年份水平的面板数据集,我想基于现有变量创建两个新变量。

年份 国家 变量1 变量2 变量3 变量4 变量1平均值 相对平均变量1
1910 德国 1 4 10 6 3 0.333
1911 德国 2 3 11 7 1.5 1.3333
1910 法国 5 6 8 9 3 1.66667
1911 法国 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"))

我遇到了错误。

""select(., var1, var2) 中的错误:对象 'd.test' 未找到。"

(我是根据这个问题设置的。)

我还尝试了:

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

并得到:

"map2(var1, var1_mean, ~df[[.x]]/df[[.y]]) 中的错误:未找到对象 'var1'

最好的方法是如何结合这两个目标?理想情况下,对于(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,
)

2
你能澄清一下你最终的两个数据框会是什么样子吗?你能编辑你的帖子并包含最终的数据集将会是什么样子,考虑到你的示例数据吗?那将非常有帮助。 - Ben
@Ben 我为var1添加了示例列。如果有意义,请告诉我。基本上,mean_var1 = mean(var1),按年份分组,因此对于给定年份,所有国家都是相同的。然后,relmean_var1是该国值相对于整体平均值的比率(var1/mean_var1),因此它对于每个国家-年份都有不同的值。这个计算将针对所有变量进行。 - PierreRoubaix
1
Pierre,你的第一张表格对于输入数据看起来很不错。@Ben要求你为期望的数据集做一个类似的第二个表格。我们可以有几种方式来解释你的请求,但展示所需的输出将会明确它。 - wibeasley
1
另外,对于未来的请求,请使用代码定义您的输入数据集。请参见@danloo下面的回复,了解一种实现方式。 - wibeasley
嗨@wibeasley,感谢您的回复。我将来会使用danloos格式。最终数据集应该看起来像第一个,有8个额外的列用于mean_var1,...,mean_var4,....; relmean_var1,relmean_var4。让我看看是否可以将其发布为两个数据框,如果更清晰的话。 - PierreRoubaix
显示剩余3条评论
3个回答

18

可能使用长格式会更容易,但这里有一个您可以使用宽格式的选项。

使用最新版本的 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

1
使用dplyr :: across()非常简洁而灵活。看起来您可以通过重复最后三行并将它们管道化来包含年份和国家细分(只需对“group_by”和新变量名称进行轻微调整)。 - wibeasley
是的,这正是我想要的,而且用了一种很好、简洁明了的方式。谢谢@Ben! - PierreRoubaix

3
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 软件包 (v2.0.1) 于2021-11-24 创建


0

这是对@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 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接