【问题标题】:How to mutate multiple columns following name pattern?如何按照名称模式改变多个列?
【发布时间】:2021-11-12 08:14:36
【问题描述】:

我有一个数据集,其中包含多个遵循名称模式的列,我需要计算新列,它是其他两列的乘积。我正在寻找一个 tidyverse 选项,但我想避免做一个 pivot_longer,因为数据集有 >百万行。

示例数据集

library(dplyr)

df <- tibble(
  jan_mean = runif(10),
  feb_mean = runif(10),
  mar_mean = runif(10),
  jan_sd = runif(10),
  feb_sd = runif(10),
  mar_sd = runif(10),
)

我可以像这样手动完成:

df2 <- df %>% 
  mutate(jan_cv= jan_mean/jan_sd,
         feb_cv= feb_mean/feb_sd,
         mar_cv= mar_mean/mar_sd
         )

这是一个简单的例子,但我对月值有类似的操作。

编辑 1

我需要为大型数据集执行此操作,我担心pivot_longer 会非常消耗,所以我对这三种方法进行了快速比较。

Method 1 是手动方式,Method 2 是@Tarjae 建议的短版本,Method 3 是使用更长的pivot 方式:


tic("Method 1: manual option")
df2 <- df %>% 
  mutate(jan_cv= jan_mean/jan_sd,
         feb_cv= feb_mean/feb_sd,
         mar_cv= mar_mean/mar_sd
  )
toc()

tic("Method 2: Short option")
df2 <- df %>%
  mutate(across(ends_with('_mean'), ~ . /
                  get(str_replace(cur_column(), "mean$", "sd")), .names = "{.col}_cv")) %>%
  rename_at(vars(ends_with('cv')), ~ str_remove(., "\\_mean"))
toc()

tic("Method 3: pivot wider option")
df2 <- df %>% 
  mutate(id = row_number()) %>% 
  pivot_longer(-id, names_to = c("month", ".value"), names_sep = "_") %>% 
  mutate(cv = mean / sd) %>% 
  pivot_wider(names_from = "month", values_from = c(mean, sd, cv), names_glue = "{month}_{.value}") %>% 
  select(-id)
toc()



结果是:

Method 1: manual option: 0.05 sec elapsed
Method 2: Short option: 0.01 sec elapsed
Method 3: pivot wider option: 0.19 sec elapsed

所以方法 2 比手动处理每一列还要快

【问题讨论】:

  • 我认为您希望在最后一行的输出应该是:mar_cv = mar_mean/mar_sd 而不是 mar_cv= jan_mean/mar_sd?

标签: r dplyr


【解决方案1】:

在这种情况下,我们可以使用across 并通过stringr 进行一些字符串操作:

library(dplyr)
library(stringr)
df %>%
  mutate(across(ends_with('_mean'), ~ . /
                  get(str_replace(cur_column(), "mean$", "sd")), .names = "{.col}_cv")) %>%
  rename_at(vars(ends_with('cv')), ~ str_remove(., "\\_mean"))
   jan_mean feb_mean mar_mean jan_sd feb_sd mar_sd jan_cv feb_cv mar_cv
      <dbl>    <dbl>    <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
 1   0.838     0.401   0.131  0.329  0.0292  0.911  2.55  13.7    0.144
 2   0.595     0.173   0.0935 0.313  0.105   0.247  1.90   1.64   0.378
 3   0.0546    0.934   0.983  0.536  0.618   0.292  0.102  1.51   3.36 
 4   0.543     0.802   0.569  0.585  0.901   0.742  0.928  0.891  0.766
 5   0.899     0.761   0.245  0.932  0.506   0.526  0.965  1.50   0.466
 6   0.832     0.875   0.947  0.390  0.613   0.607  2.13   1.43   1.56 
 7   0.268     0.421   0.930  0.869  0.873   0.612  0.308  0.483  1.52 
 8   0.475     0.217   0.330  0.0473 0.826   0.903 10.0    0.262  0.366
 9   0.379     0.425   0.479  0.931  0.381   0.223  0.407  1.12   2.15 
10   0.616     0.922   0.707  0.976  0.241   0.619  0.631  3.82   1.14 

【讨论】:

  • 谢谢,这真的很有用。我确实很难理解across 这个应用程序中的不同论点。 ends_with('mean') 很清楚,但操作 get(str_replace(cur_column(), "mean$", "sd")), 很难从辅助函数中理解。有什么快速的解释吗?
  • 基本上我们这样做: 1. 我们从列名中删除_mean(cur_colum()) 2. 将_sd 替换为str_replace 3. 获取值 4. 除法 5. 新建具有新名称的列使用 .names 并添加 _cv
【解决方案2】:

实现您想要的结果的一个选项是将您的数据转换为长格式,这样可以轻松地每月进行计算,如果需要,之后再转换回宽格式。为此,我首先在您的数据中添加了一个标识符列:

library(dplyr)
library(tidyr)

set.seed(42)

df %>% 
  mutate(id = row_number()) %>% 
  pivot_longer(-id, names_to = c("month", ".value"), names_sep = "_") %>% 
  mutate(cv = mean / sd) %>% 
  pivot_wider(names_from = "month", values_from = c(mean, sd, cv), names_glue = "{month}_{.value}") %>% 
  select(-id)
#> # A tibble: 10 × 9
#>    jan_mean feb_mean mar_mean  jan_sd feb_sd mar_sd  jan_cv feb_cv mar_cv
#>       <dbl>    <dbl>    <dbl>   <dbl>  <dbl>  <dbl>   <dbl>  <dbl>  <dbl>
#>  1    0.915    0.458   0.904  0.738   0.380  0.333    1.24   1.21   2.71 
#>  2    0.937    0.719   0.139  0.811   0.436  0.347    1.16   1.65   0.400
#>  3    0.286    0.935   0.989  0.388   0.0374 0.398    0.737 25.0    2.48 
#>  4    0.830    0.255   0.947  0.685   0.974  0.785    1.21   0.262  1.21 
#>  5    0.642    0.462   0.0824 0.00395 0.432  0.0389 163.     1.07   2.12 
#>  6    0.519    0.940   0.514  0.833   0.958  0.749    0.623  0.982  0.687
#>  7    0.737    0.978   0.390  0.00733 0.888  0.677  100.     1.10   0.576
#>  8    0.135    0.117   0.906  0.208   0.640  0.171    0.648  0.184  5.29 
#>  9    0.657    0.475   0.447  0.907   0.971  0.261    0.725  0.489  1.71 
#> 10    0.705    0.560   0.836  0.612   0.619  0.514    1.15   0.905  1.63

【讨论】:

    猜你喜欢
    • 2014-01-26
    • 1970-01-01
    • 2022-12-07
    • 1970-01-01
    • 2022-11-28
    • 2020-08-21
    • 1970-01-01
    • 1970-01-01
    • 2020-08-15
    相关资源
    最近更新 更多