Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mutate multiple columns with different functions using a "metadata" tibble

Tags:

r

dplyr

I'd like to use a "metadata" tibble to rename & recode another tibble. I've got the renaming piece, but I'm struggling to get mutate to apply a distinct function to each column. I'm guessing that across will be involved, but not certain.

Here's a MRE:

library(tidyverse)

#Recode everything as a character.. just so we can change it back :)
bad <- iris %>% 
    as_tibble() %>% 
    mutate_all(as.character)

#Create a "metadata" tibble of what we want to rename/recode the columns as
meta <- tibble(
    name = colnames(bad),
    rename_as = c("s_l", "s_w", "p_l", "p_w", "spec"),
    format_as = c(rep("as.numeric", 4), "as.character")
)

#Use the metadata tibble to rename/recode the bad tibble
bad %>% 
    #rename works fine
    rename_all(~ meta$rename_as) %>% 
    #But how to specify which function applies to which column?
    mutate(across(meta$rename_as, ~ meta$format_as))

Desired output:

'data.frame':   150 obs. of  5 variables:
$ s_l : num  5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
$ s_w : num  3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
$ p_l : num  1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
$ p_w : num  0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
$ spec: chr  "setosa" "setosa" "setosa" "setosa" ...
like image 610
Jordo82 Avatar asked Oct 20 '25 03:10

Jordo82


2 Answers

  1. In across(), you can use cur_column() to match meta$name and find the corresponding format_as.
  2. format_as are character function names. do.call() is used to trigger it off.
  3. .names in across() is a glue specification that describes how to name the output columns. This can use {.col} to stand for the selected column name. You can use it to achieve the renaming step.
bad %>%
  mutate(across(everything(), ~ do.call(meta$format_as[meta$name == cur_column()], list(.x)),
                .names = "{meta$rename_as[meta$name == .col]}"),
         .keep = "unused")

# # A tibble: 150 × 5
#      s_l   s_w   p_l   p_w spec  
#    <dbl> <dbl> <dbl> <dbl> <chr> 
#  1   5.1   3.5   1.4   0.2 setosa
#  2   4.9   3     1.4   0.2 setosa
#  3   4.7   3.2   1.3   0.2 setosa
#  4   4.6   3.1   1.5   0.2 setosa
#  5   5     3.6   1.4   0.2 setosa
#  6   5.4   3.9   1.7   0.4 setosa
#  7   4.6   3.4   1.4   0.3 setosa
#  8   5     3.4   1.5   0.2 setosa
#  9   4.4   2.9   1.4   0.2 setosa
# 10   4.9   3.1   1.5   0.1 setosa
# ℹ 140 more rows
# ℹ Use `print(n = ...)` to see more rows
like image 192
Darren Tsai Avatar answered Oct 22 '25 18:10

Darren Tsai


You can also create a named list of expressions to inject into the mutate verb:

lexprs <- set_names(parse_exprs(glue_data(meta, "{format_as}({name})")), meta$rename_as)

bad |> 
  mutate(!!!lexprs, .keep = "unused")
> lexprs
$s_l
as.numeric(Sepal.Length)

$s_w
as.numeric(Sepal.Width)

$p_l
as.numeric(Petal.Length)

$p_w
as.numeric(Petal.Width)

$spec
as.character(Species)
like image 34
LMc Avatar answered Oct 22 '25 17:10

LMc