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

Posted

技术标签:

【中文标题】跨多个列变异以创建新的变量集【英文标题】:Mutate across multiple columns to create new variable sets 【发布时间】:2022-01-02 21:04:51 【问题描述】:

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

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"))

我得到错误

""选择错误(., var1, var2) : 找不到对象 'd.test'。"

。 (我从this question 得到这个设置)

我也试过了:

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

得到了

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

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

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

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 个 data.frame 会是什么样子吗?鉴于您的示例数据,您能否编辑您的帖子并包含最终的数据集?那会很有帮助。 @Ben 我为 var1 添加了示例列。让我知道这是否有意义。基本上,mean_var1 = mean(var1), by(year) 所以在给定年份的所有国家/地区都是相同的。那么 relmean_var1 是该国家相对于总体平均值的值 (var1/mean_var1),因此每个国家/地区的年份都采用不同的值。将对所有变量进行此计算。 皮埃尔,您的第一张表看起来很适合输入。 @Ben 要求您为 expected 数据集制作第二个类似的表格。有几种方法可以解释您的请求,但显示所需的输出会确定它。 另外,对于未来的请求,使用代码定义您的输入数据集。请参阅下面@danloo 的回复,了解一种方法。 嗨@wibeasley,感谢您的回复。以后我会使用danloos格式。最终数据集应该看起来像第一个数据集,其中有 8 个附加列,分别代表 mean_var1、...、mean_var4、...; relmean_var1,relmean_var4。让我看看是否可以将其作为两个数据框发布,如果这样会更清楚 【参考方案1】:

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

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

【讨论】:

使用dplyr::across() 非常简洁紧凑。看起来您可以通过重复最后三行并将它们传递给它们(只需对 group_by 和新变量名称进行细微调整)来包含年份和国家/地区的细分。 是的,这正是我想要的一种简洁明了的方式。谢谢@Ben!【参考方案2】:
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 日创建

【讨论】:

【参考方案3】:

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

考虑一下你是否真的想要再宽一点。通常保持较长时间更容易(例如,删除对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's 这样更长的描述性变量名称,但我希望所有内容都适合 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 

【讨论】:

以上是关于跨多个列变异以创建新的变量集的主要内容,如果未能解决你的问题,请参考以下文章

现场填坑系列:mongodb 复制集跨机房同步网络问题探查

现场填坑系列:mongodb 复制集跨机房同步网络问题探查

dplyr 变异()。变异一个变量的问题,取决于匹配由matches()选择的特定名称的其他列中的非缺失值

大数据集群跨多版本升级业务0中断,只因背后有TA

Pandas 按列分组并检查多个条件以创建新的分类列

Angular项目间的模板共享:如何跨多项目重用公共HTML