Skip to main content
  1. About
  2. For Teams
Asked
Modified 18 days ago
Viewed 109 times
Part of R Language Collective
3

I have to use across() multiple times. Is there a way to simplify the following across() code?

ori_df <- data.frame(base_1 = 1:3,base_2 = 7:9,base_3 =3:1,
                     exp_1 =c(0.4,0.1,0.7),
                     exp_2 =c(0.1,0.05,0.6),
                     exp_3 =c(0.1,0.03,0.04),
                     exp_4 =c(0.1,0.03,0.04))


ori_df %>% mutate(across(contains("base"),~ .* exp_1,.names ="{sub('base','amount_exp_1',{.col})}")) %>% 
  mutate(across(contains("base"),~ .* exp_2,.names ="{sub('base','amount_exp_2',{.col})}")) %>% 
  mutate(across(contains("base"),~ .* exp_3,.names ="{sub('base','amount_exp_3',{.col})}")) %>% 
  mutate(across(contains("base"),~ .* exp_4,.names ="{sub('base','amount_exp_4',{.col})}"))

3 Answers 3

4

across() has a .fns argument that allows you to specify multiple functions in a single across call that will be applied to each column. I had to change the sub() in names for some reason to make it work. The order of columns is slightly different but I hope this is not a big problem.

ori_df %>% 
  mutate(across(contains("base"), 
        .fns = list(amount_exp_1 = ~ .* exp_1,
                    amount_exp_2 = ~ .* exp_2, 
                    amount_exp_3 = ~ .* exp_3, 
                    amount_exp_4 = ~ .* exp_4),
        .names = "{.fn}{sub('base','',{.col})}"))
Sign up to request clarification or add additional context in comments.

Comments

1

Here is another solution with tidyr::unnest. This way, you don't need to specify the numbers for exp columns. However, one caveat is that this will result in slightly different column names than OP's example. i.e. amount_1_exp_1, amount_1_exp_2, ... as opposed to amount_exp_1_1, amount_exp_1_2... If you want to keep the names in the original format, you could change them with rename_with.

library(dplyr)
ori_df |> 
  mutate(across(
    contains("base"), 
    ~ .x * across(contains("exp")),
    .names="{sub('base','amount',{.col})}"
  )) |> 
  tidyr::unnest(cols=contains("amount"),
                names_sep = "_") 

# output
# A tibble: 3 × 19
  base_1 base_2 base_3 exp_1 exp_2 exp_3 exp_4 amount_1_exp_1 amount_1_exp_2 amount_1_exp_3
   <int>  <int>  <int> <dbl> <dbl> <dbl> <dbl>          <dbl>          <dbl>          <dbl>
1      1      7      3   0.4  0.1   0.1   0.1             0.4            0.1           0.1 
2      2      8      2   0.1  0.05  0.03  0.03            0.2            0.1           0.06
3      3      9      1   0.7  0.6   0.04  0.04            2.1            1.8           0.12
# ℹ 9 more variables: amount_1_exp_4 <dbl>, amount_2_exp_1 <dbl>, amount_2_exp_2 <dbl>,
#   amount_2_exp_3 <dbl>, amount_2_exp_4 <dbl>, amount_3_exp_1 <dbl>, amount_3_exp_2 <dbl>,
#   amount_3_exp_3 <dbl>, amount_3_exp_4 <dbl>

(Adding a call to rename_with)

... |> 
  rename_with(.cols = contains("amount"),
              .fn = ~ sub("^amount_(\\d)_exp_(\\d)",
                          "amount_exp_\\1_\\2",
                          .x))

1 Comment

using across in across, this is the most smart way . Thanks!
0

Find another code , just mark it

1:4 %>% 
  map(~ {
    exp_col <- paste0("exp_", .x)
    new_prefix <- paste0("amount_", exp_col, "_")
    ori_df %>% 
      mutate(across(contains("base"), 
                   ~ . * get(exp_col),
                   .names = "{sub('base', new_prefix, .col)}"))
  }) %>% 
  reduce(left_join, by = names(ori_df))

Comments

Your Answer

Post as a guest

Required, but never shown

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.

Morty Proxy This is a proxified and sanitized view of the page, visit original site.