case_match() on several variables in a data frame

I have 7 variables (GAD_1 through GAD_7) in a data frame named emu_dirty. I need to recode the values (from 1-4 to 0-3) for all seven, but I'd rather do this in a more efficient way than listing each variable statically. Is there a way to mutate this, or loop through each of these variables?

# GAD-7 Items
recode_gad_items <-  list("GAD_1", "GAD_2", "GAD_3", "GAD_4", "GAD_5", "GAD_6",
                     "GAD_7")

emu_dirty$GAD_1 = case_match(emu_dirty$GAD_1,
                                                      4 ~ 3, 
                                                      3 ~ 2, 
                                                      2 ~ 1, 
                                                      1 ~ 0)

Since your recoding can be done by subtracting one, I would do it as follows with the mutate() and across() functions from dplyr.

library(dplyr)

emu_dirty <- data.frame(A = 1:4,
                        GAD_1 = c(2,4,3,1),
                        GAD_2 = c(4,4,1,3),
                        GAD_3 = c(4,1,3,2))
emu_dirty
#>   A GAD_1 GAD_2 GAD_3
#> 1 1     2     4     4
#> 2 2     4     4     1
#> 3 3     3     1     3
#> 4 4     1     3     2
emu_dirty <- emu_dirty |> mutate(across(.cols = starts_with("GAD"), 
                                        .fns = ~.x - 1))
emu_dirty
#>   A GAD_1 GAD_2 GAD_3
#> 1 1     1     3     3
#> 2 2     3     3     0
#> 3 3     2     0     2
#> 4 4     0     2     1

Created on 2023-03-23 with reprex v2.0.2

1 Like

Exploit the fact that a data frame that does not contain both numeric and character variable types can be treated as a matrix, with which can be identified to an arithmetical operator very simply.

set.seed(42)
d <- data.frame(
  GAD_1 = sample(1:4,4),
  GAD_2 = sample(1:4,4),
  GAD_3 = sample(1:4,4),
  GAD_4 = sample(1:4,4),
  GAD_5 = sample(1:4,4),
  GAD_6 = sample(1:4,4),
  GAD_7 = sample(1:4,4))

d 
#>   GAD_1 GAD_2 GAD_3 GAD_4 GAD_5 GAD_6 GAD_7
#> 1     1     2     4     4     4     3     4
#> 2     4     4     3     1     2     1     1
#> 3     3     3     2     3     3     4     3
#> 4     2     1     1     2     1     2     2
d - 1
#>   GAD_1 GAD_2 GAD_3 GAD_4 GAD_5 GAD_6 GAD_7
#> 1     0     1     3     3     3     2     3
#> 2     3     3     2     0     1     0     0
#> 3     2     2     1     2     2     3     2
#> 4     1     0     0     1     0     1     1

Created on 2023-03-24 with reprex v2.0.2

Whenever the need to perform arithmetic operations on an all numeric object, this approach should be the first resort for its simplicity. Because of the extreme simplicity of the required operation, subtracting one from each element in the matrix, the transformation can be accomplished in the five characters of

d - 1

Transformations will not always be so simple. Suppose the rule is to add one to odd values or subtract one from even.

tf <- function(x) ifelse(x%%2 == 0,x-1,x+1)
apply(d,2,tf)
#>      GAD_1 GAD_2 GAD_3 GAD_4 GAD_5 GAD_6 GAD_7
#> [1,]     2     1     3     3     3     4     3
#> [2,]     3     3     4     2     1     2     2
#> [3,]     4     4     1     4     4     3     4
#> [4,]     1     2     2     1     2     1     1

This is very elegant and works very well! Thank you!

I am curious, instead of using starts_with(), can I use exact matches() and feed my "recode_gad_items" into matches?

There are probably various ways to use a list. One method is to collapse the list into a regular expression.

library(dplyr)
recode_gad_items <-  list("GAD_1", "GAD_2", "GAD_3", "GAD_4", "GAD_5", "GAD_6",
                          "GAD_7")
recode_gad_items <- paste(recode_gad_items, collapse = "|")
emu_dirty <- data.frame(A = 1:4,
                        GAD_1 = c(2,4,3,1),
                        GAD_2 = c(4,4,1,3),
                        GAD_3 = c(4,1,3,2))

emu_dirty <- emu_dirty |> mutate(across(.cols = matches(recode_gad_items), 
                                        .fns = ~case_when(
                                          .x == 4 ~ 3,
                                          .x == 3 ~ 2,
                                          .x == 2 ~ 1,
                                          .x == 1 ~ 0)))
1 Like

This is precisely what I was trying to do. Beautiful!

Thank you so much, you've ascended to being my favorite person of the day!

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.

If you have a query related to it or one of the replies, start a new topic and refer back with a link.