8

I am once again confused about how to achieve this:

Given this data frame:

df <- tibble(
  foo = c(1,0,1),
  bar = c(1,1,1),
  foobar = c(0,1,1)
)

And this vector:

to_sum <- c("foo", "bar")

I would like to get the row-wise sum of the values in the columns to_sum.

Desired output:

# A tibble: 3 x 4
# Rowwise: 
    foo   bar foobar   sum
  <dbl> <dbl>  <dbl> <dbl>
1     1     1      0     2
2     0     1      1     1
3     1     1      1     2

Typing it out works (obviously).

df %>% rowwise() %>% 
  mutate(
    sum = sum(foo, bar)
  )

This does not:

df %>% rowwise() %>% 
  mutate(
    sum = sum(to_sum)
  )

Which I understand, because if I were to try:

df %>% rowwise() %>% 
  mutate(
    sum = sum("foo", "bar")
  )

How can I compute the row-wise sum from a vector of column names?

Anoushiravan R
  • 21,622
  • 3
  • 18
  • 41
MKR
  • 1,620
  • 7
  • 20

5 Answers5

6

I think you are looking for rlang::syms to coerce strings to quosures:

library(dplyr)
library(rlang)
df %>% 
  rowwise() %>% 
  mutate(
    sum = sum(!!!syms(to_sum))
  )
#     foo   bar foobar   sum
#   <dbl> <dbl>  <dbl> <dbl>
# 1     1     1      0     2
# 2     0     1      1     1
# 3     1     1      1     2
user63230
  • 4,095
  • 21
  • 43
6
library(janitor)
df %>%
  adorn_totals("col",,,"sum",to_sum)

 foo bar foobar sum
   1   1      0   2
   0   1      1   1
   1   1      1   2

Why ,,, ?

If you look at ?adorn_totals, you'll see its arguments:

adorn_totals(dat, where = "row", fill = "-", na.rm = TRUE, name = "Total", ...)

The final one ... is to control column selection. There's unfortunately no way to tell R directly that to_sum should be used for that ... argument, so the ,,, in this answer is telling it to use the default values for the arguments where, fill, and na.rm. At that point, it has values for every argument besides ..., so to_sum gets applied to that.

The topic is discussed further here: Specify the dots argument when calling a tidyselect-using function without needing to specify the preceding arguments

Sam Firke
  • 21,571
  • 9
  • 87
  • 105
3

You need to use c_across and any_of. This is how it is intended to be used by the RStudio Team: check out vignette("rowwise", package = "dplyr").

library(dplyr)

df %>% 
  rowwise() %>% 
  mutate(sum = sum(c_across(any_of(to_sum))))

#> # A tibble: 3 x 4
#> # Rowwise: 
#>     foo   bar foobar   sum
#>   <dbl> <dbl>  <dbl> <dbl>
#> 1     1     1      0     2
#> 2     0     1      1     1
#> 3     1     1      1     2

c_across is specific for rowwise operations. any_of is needed to interpret to_sum as a character vector containing column names. It works even without it but it is usually preferred to be used.

You may want to ungroup() at the end to remove the rowwise.

Edo
  • 7,567
  • 2
  • 9
  • 19
3

This might help you:

library(dplyr)
library(purrr)
library(rlang)

df %>%
  bind_cols(parse_exprs(to_sum) %>%
              map_dfc(~ eval_tidy(.x, data = df)) %>%
              rowSums()) %>%
  rename(sum = ...4)

# A tibble: 3 x 4
    foo   bar foobar   sum
  <dbl> <dbl>  <dbl> <dbl>
1     1     1      0     2
2     0     1      1     1
3     1     1      1     2
Anoushiravan R
  • 21,622
  • 3
  • 18
  • 41
2

You could also consider using rowSums:

df %>% 
   mutate(sum = rowSums(across(all_of(to_sum))))

# A tibble: 3 x 4
    foo   bar foobar   sum
  <dbl> <dbl>  <dbl> <dbl>
1     1     1      0     2
2     0     1      1     1
3     1     1      1     2
Onyambu
  • 67,392
  • 3
  • 24
  • 53
  • Nice solution indeed. I accepted the `!!!syms()` solution, but I like that this does not need the `rowwise()` function. Thanks – MKR Jul 20 '21 at 07:09