Applying Operations Across Multiple Variables

When you need to apply the same transformation to many variables, the across() function helps avoid repetition. For example, we can compute average scores across sets of items representing five personality traits:

library(dplyr) # load dplyr package
tmp <- mutate(bfi,
              agree = rowMeans(across(A1:A5), na.rm = TRUE),
              consc = rowMeans(across(C1:C5), na.rm = TRUE),
              extra = rowMeans(across(E1:E5), na.rm = TRUE),
              neuro = rowMeans(across(N1:N5), na.rm = TRUE),
              open  = rowMeans(across(O1:O5), na.rm = TRUE))
head(tmp)
      A1 A2 A3 A4 A5 C1 C2 C3 C4 C5 E1 E2 E3 E4 E5 N1 N2 N3 N4 N5 O1 O2 O3 O4
61617  2  4  3  4  4  2  3  3  4  4  3  3  3  4  4  3  4  2  2  3  3  6  3  4
61618  2  4  5  2  5  5  4  4  3  4  1  1  6  4  3  3  3  3  5  5  4  2  4  3
61620  5  4  5  4  4  4  5  4  2  5  2  4  4  4  5  4  5  4  2  3  4  2  5  5
61621  4  4  6  5  5  4  4  3  5  5  5  3  4  4  4  2  5  2  4  1  3  3  4  3
61622  2  3  3  4  5  4  4  5  3  2  2  2  5  4  5  2  3  4  4  3  3  3  4  3
61623  6  6  5  6  5  6  6  6  1  3  2  1  6  5  6  3  5  2  2  3  4  3  5  6
      O5 gender education age agree consc extra neuro open
61617  3      1        NA  16   3.4   3.2   3.4   2.8  3.8
61618  3      2        NA  18   3.6   4.0   3.0   3.8  3.2
61620  2      2        NA  17   4.4   4.0   3.8   3.6  3.6
61621  5      2        NA  17   4.8   4.2   4.0   2.8  3.6
61622  3      1        NA  17   3.4   3.6   3.6   3.2  3.2
61623  1      2         3  21   5.6   4.4   4.0   3.0  3.8

If some items are reverse-coded, we need to adjust them before computing the scores. The psych package provides a convenient function, scoreItems(), which handles this automatically using predefined keys that describe how items should be scored.

library(psych) # load psych package
bfi.keys
$agree
[1] "-A1" "A2"  "A3"  "A4"  "A5" 

$conscientious
[1] "C1"  "C2"  "C3"  "-C4" "-C5"

$extraversion
[1] "-E1" "-E2" "E3"  "E4"  "E5" 

$neuroticism
[1] "N1" "N2" "N3" "N4" "N5"

$openness
[1] "O1"  "-O2" "O3"  "O4"  "-O5"
names(bfi.keys) <- c("agree", "consc", "extra", "neuro", "open")
scores <- scoreVeryFast(keys = bfi.keys, items = bfi)
bfi <- data.frame(bfi, scores)
head(bfi)
      A1 A2 A3 A4 A5 C1 C2 C3 C4 C5 E1 E2 E3 E4 E5 N1 N2 N3 N4 N5 O1 O2 O3 O4
61617  2  4  3  4  4  2  3  3  4  4  3  3  3  4  4  3  4  2  2  3  3  6  3  4
61618  2  4  5  2  5  5  4  4  3  4  1  1  6  4  3  3  3  3  5  5  4  2  4  3
61620  5  4  5  4  4  4  5  4  2  5  2  4  4  4  5  4  5  4  2  3  4  2  5  5
61621  4  4  6  5  5  4  4  3  5  5  5  3  4  4  4  2  5  2  4  1  3  3  4  3
61622  2  3  3  4  5  4  4  5  3  2  2  2  5  4  5  2  3  4  4  3  3  3  4  3
61623  6  6  5  6  5  6  6  6  1  3  2  1  6  5  6  3  5  2  2  3  4  3  5  6
      O5 gender education age agree consc extra neuro open
61617  3      1        NA  16   4.0   2.8   3.8   2.8  3.0
61618  3      2        NA  18   4.2   4.0   5.0   3.8  4.0
61620  2      2        NA  17   3.8   4.0   4.2   3.6  4.8
61621  5      2        NA  17   4.6   3.0   3.6   2.8  3.2
61622  3      1        NA  17   4.0   4.4   4.8   3.2  3.6
61623  1      2         3  21   4.6   5.6   5.6   3.0  5.0

We can then apply additional transformations, such as taking the square root of all items starting with “A” or “E”:

bfi <- mutate(bfi,
              across(starts_with(c("A", "E"), ignore.case = FALSE),
                     sqrt,
                     .names = "{.col}_sqrt"))
head(bfi)
      A1 A2 A3 A4 A5 C1 C2 C3 C4 C5 E1 E2 E3 E4 E5 N1 N2 N3 N4 N5 O1 O2 O3 O4
61617  2  4  3  4  4  2  3  3  4  4  3  3  3  4  4  3  4  2  2  3  3  6  3  4
61618  2  4  5  2  5  5  4  4  3  4  1  1  6  4  3  3  3  3  5  5  4  2  4  3
61620  5  4  5  4  4  4  5  4  2  5  2  4  4  4  5  4  5  4  2  3  4  2  5  5
61621  4  4  6  5  5  4  4  3  5  5  5  3  4  4  4  2  5  2  4  1  3  3  4  3
61622  2  3  3  4  5  4  4  5  3  2  2  2  5  4  5  2  3  4  4  3  3  3  4  3
61623  6  6  5  6  5  6  6  6  1  3  2  1  6  5  6  3  5  2  2  3  4  3  5  6
      O5 gender education age agree consc extra neuro open  A1_sqrt  A2_sqrt
61617  3      1        NA  16   4.0   2.8   3.8   2.8  3.0 1.414214 2.000000
61618  3      2        NA  18   4.2   4.0   5.0   3.8  4.0 1.414214 2.000000
61620  2      2        NA  17   3.8   4.0   4.2   3.6  4.8 2.236068 2.000000
61621  5      2        NA  17   4.6   3.0   3.6   2.8  3.2 2.000000 2.000000
61622  3      1        NA  17   4.0   4.4   4.8   3.2  3.6 1.414214 1.732051
61623  1      2         3  21   4.6   5.6   5.6   3.0  5.0 2.449490 2.449490
       A3_sqrt  A4_sqrt  A5_sqrt  E1_sqrt  E2_sqrt  E3_sqrt  E4_sqrt  E5_sqrt
61617 1.732051 2.000000 2.000000 1.732051 1.732051 1.732051 2.000000 2.000000
61618 2.236068 1.414214 2.236068 1.000000 1.000000 2.449490 2.000000 1.732051
61620 2.236068 2.000000 2.000000 1.414214 2.000000 2.000000 2.000000 2.236068
61621 2.449490 2.236068 2.236068 2.236068 1.732051 2.000000 2.000000 2.000000
61622 1.732051 2.000000 2.236068 1.414214 1.414214 2.236068 2.000000 2.236068
61623 2.236068 2.449490 2.236068 1.414214 1.000000 2.449490 2.236068 2.449490
Practice

Use the dplyr::mutate() function to create standardized versions of the five scales scores we just created.

bfi <- mutate(bfi,
              agree_z = (agree - mean(agree, na.rm = TRUE)) / sd(agree, na.rm = TRUE),
              consc_z = (consc - mean(consc, na.rm = TRUE)) / sd(consc, na.rm = TRUE),
              extra_z = (extra - mean(extra, na.rm = TRUE)) / sd(extra, na.rm = TRUE),
              neuro_z = (neuro - mean(neuro, na.rm = TRUE)) / sd(neuro, na.rm = TRUE),
              open_z  = (open  - mean(open,  na.rm = TRUE)) / sd(open,  na.rm = TRUE))
Back to top