# Rowwise matrix operations

# Packages and setup

```
library(tidyverse)
set.seed(20)
```

# The `rowwise`

function

The `rowwise`

function allows for the grouping
of data by rows in order to perform some operation across the values found in its columns.

Some basic data:

```
nums <- tibble(
x1 = sample(1:5, size=6, replace=TRUE),
x2 = sample(1:5, size=6, replace=TRUE),
x3 = sample(1:5, size=6, replace=TRUE),
x4 = sample(1:5, size=6, replace=TRUE)
)
nums
```

```
## # A tibble: 6 x 4
## x1 x2 x3 x4
## <int> <int> <int> <int>
## 1 3 1 4 5
## 2 2 3 1 1
## 3 1 5 5 5
## 4 2 1 1 2
## 5 5 5 1 1
## 6 5 2 4 5
```

I won't actually be using `x4`

in the demo below, but I'm including it to make the data a bit more
realistic. Oftentimes, you will have more variables in your data set than the ones you are trying
to operate on, i.e. you can't always just `tidyselect::everything()`

!

## Using `c_across`

with `rowwise`

The `c_across`

function is often used with
`rowwise`

in order to **combine values across columns**. What happens if we use `c_across`

without
`rowwise`

?

```
nums %>%
mutate(test = mean(c_across(x1:x3)))
```

```
## # A tibble: 6 x 5
## x1 x2 x3 x4 test
## <int> <int> <int> <int> <dbl>
## 1 3 1 4 5 2.83
## 2 2 3 1 1 2.83
## 3 1 5 5 5 2.83
## 4 2 1 1 2 2.83
## 5 5 5 1 1 2.83
## 6 5 2 4 5 2.83
```

It can be seen that the `test`

column has a single value that is repeated throughout. Without
grouping the data rowwise, the mean is computed using **all** the values across `x1`

, `x2`

, and
`x3`

:

```
mean(c(nums$x1, nums$x2, nums$x3))
```

```
## [1] 2.833333
```

Now, if we group the data rowwise before computing the mean across `x1`

, `x2`

, and `x3`

:

```
nums %>%
rowwise() %>%
mutate(test = mean(c_across(x1:x3)))
```

```
## # A tibble: 6 x 5
## # Rowwise:
## x1 x2 x3 x4 test
## <int> <int> <int> <int> <dbl>
## 1 3 1 4 5 2.67
## 2 2 3 1 1 2
## 3 1 5 5 5 3.67
## 4 2 1 1 2 1.33
## 5 5 5 1 1 3.67
## 6 5 2 4 5 3.67
```

We can check that the results are what we wanted:

```
mean(c(3, 1, 4))
```

```
## [1] 2.666667
```

```
mean(c(5, 2, 4))
```

```
## [1] 3.666667
```

They match — great!

**Note:** `rowwise`

is a type of grouping. From the first two lines of the tibble output above, we
can see that after creating the `test`

variable, the data is still grouped (rowwise)! Don't forget
to `ungroup`

when you're done mutating.

## Rowwise matrix operations

### Output is a vector of length 1

Suppose you wanted to perform the following computation using the values found in each row:

$$ \mathbf{x}'\mathbf{A}\mathbf{x} $$We will first create a function that:

- Takes the combined values over a selection of columns (i.e. the result of
`c_across`

) - Converts the values to a matrix (a $n \times 1$ column vector)
- Performs the matrix operation and reduces the resulting $1 \times 1$ matrix to a vector of length one

```
quadratic <- function(x, A) {
x <- as.matrix(x)
drop(t(x) %*% A %*% x)
}
```

For the $ \mathbf{A} $ matrix, let's use:

```
A <- matrix(
sample(0:5, size=9, replace=TRUE),
nrow=3, ncol=3
)
A
```

```
## [,1] [,2] [,3]
## [1,] 1 2 4
## [2,] 2 0 5
## [3,] 4 3 4
```

Putting it all together:

```
nums %>%
rowwise() %>%
mutate(test = quadratic(c_across(x1:x3), A)) %>%
ungroup()
```

```
## # A tibble: 6 x 5
## x1 x2 x3 x4 test
## <int> <int> <int> <int> <dbl>
## 1 3 1 4 5 213
## 2 2 3 1 1 72
## 3 1 5 5 5 361
## 4 2 1 1 2 40
## 5 5 5 1 1 209
## 6 5 2 4 5 353
```

Checking our work:

```
quadratic(c(3, 1, 4), A)
```

```
## [1] 213
```

```
quadratic(c(5, 2, 4), A)
```

```
## [1] 353
```

### Output is a vector of length greater than 1

Suppose you wanted to perform the following computation using the values found in each row:

$$ \mathbf{A}\mathbf{x} $$and wanted each value to go into its own column. This can be accomplished by first creating a
function similar to the previous, but returning a list containing a named vector instead. Then we
can use `unnest_wider`

to unnest the values
within the list into their own columns.

Creating the right-multiplying function:

```
right_mult <- function(x, A) {
x <- as.matrix(x)
drop(A %*% x) %>%
set_names(., paste0("new_x", 1:length(.))) %>%
list()
}
```

Creating the new list-column:

```
nums %>%
rowwise() %>%
mutate(test = right_mult(c_across(x1:x3), A)) %>%
ungroup()
```

```
## # A tibble: 6 x 5
## x1 x2 x3 x4 test
## <int> <int> <int> <int> <list>
## 1 3 1 4 5 <dbl [3]>
## 2 2 3 1 1 <dbl [3]>
## 3 1 5 5 5 <dbl [3]>
## 4 2 1 1 2 <dbl [3]>
## 5 5 5 1 1 <dbl [3]>
## 6 5 2 4 5 <dbl [3]>
```

Unnesting the list contents into their own columns:

```
nums %>%
rowwise() %>%
mutate(test = right_mult(c_across(x1:x3), A)) %>%
ungroup() %>%
unnest_wider(test)
```

```
## # A tibble: 6 x 7
## x1 x2 x3 x4 new_x1 new_x2 new_x3
## <int> <int> <int> <int> <dbl> <dbl> <dbl>
## 1 3 1 4 5 21 26 31
## 2 2 3 1 1 12 9 21
## 3 1 5 5 5 31 27 39
## 4 2 1 1 2 8 9 15
## 5 5 5 1 1 19 15 39
## 6 5 2 4 5 25 30 42
```

Checking our work:

```
right_mult(c(3, 1, 4), A)
```

```
## [[1]]
## new_x1 new_x2 new_x3
## 21 26 31
```

```
right_mult(c(5, 2, 4), A)
```

```
## [[1]]
## new_x1 new_x2 new_x3
## 25 30 42
```