4

I have a 4x4 tibble, and I'm practicing various dplyr functions on it.

I would like to calculate the range of each row, and display the range of that row as a single number in a new column.

Here is my code:

my_tibble <- data.frame(col1 = c(1:5), col2 = c(6:10), col3 = c(11:15), col4 = c(16:20))
my_tibble <- as_tibble(my_tibble)

I attempted a for loop to solve this problem but can't understand how the for loop interacts with the subsetted tibble data:

for (rows in 1:4)
  my_range <- max(my_tibble[rows, 1:4]) - min(my_tibble[rows, 1:4])

In summary, I would like as many ways possible to display the max-min (i.e. range) in a new column in the tibble.

Harrison Jones
  • 2,256
  • 5
  • 27
  • 34

4 Answers4

5

In base R, you could use diff with range in an apply statement:

my_tibble$rngdiff <- apply(my_tibble, 1, function(x) diff(range(x)))

diff(range(x)) finds the range of an input and take the difference. The apply function with index set to 1 performs the function across each row. Output:

# A tibble: 5 × 5
   col1  col2  col3  col4  rngdiff
  <int> <int> <int> <int>    <int>
1     1     6    11    16       15
2     2     7    12    17       15
3     3     8    13    18       15
4     4     9    14    19       15
5     5    10    15    20       15

Your proposed for loop doesn't work for two primary reasons: (1) you are not indexing, my_range so it overwrites every iteration and (2) your rows index does not cover the total number of rows (there are 5). A corrected for loop statement could be:

my_tibble$diffloop <- NA
for(rows in seq_len(nrow(my_tibble))){
  my_tibble$diffloop[rows] <- max(my_tibble[rows, 1:4]) - min(my_tibble[rows, 1:4])
  # or
  # my_tibble$diffloop[rows] <- diff(range(my_tibble[rows, 1:4]))
}

(though for the record I dont think you should use for loops here)

jpsmith
  • 11,023
  • 5
  • 15
  • 36
4

Using rowwise can let you use min and max directly:

library(tidyverse)

my_tibble %>%
  rowwise() %>%
  mutate(range = max(c_across(col1:col4)) - min(c_across(col1:col4)))

# A tibble: 5 × 5
# Rowwise: 
   col1  col2  col3  col4 range
  <int> <int> <int> <int> <int>
1     1     6    11    16    15
2     2     7    12    17    15
3     3     8    13    18    15
4     4     9    14    19    15
5     5    10    15    20    15
Harrison Jones
  • 2,256
  • 5
  • 27
  • 34
1

Using pmax/pmin

library(dplyr)
 my_tibble %>% 
  mutate(range = do.call(pmax, pick(everything())) - 
   do.call(pmin, pick(everything())))

-output

# A tibble: 5 × 5
   col1  col2  col3  col4 range
  <int> <int> <int> <int> <int>
1     1     6    11    16    15
2     2     7    12    17    15
3     3     8    13    18    15
4     4     9    14    19    15
5     5    10    15    20    15
akrun
  • 874,273
  • 37
  • 540
  • 662
1

Another option with pmax/pmin

my_tibble$range <- do.call(`-`, lapply(c(pmax, pmin), \(f) do.call(f, my_tibble)))

gives

> my_tibble
# A tibble: 5 × 5
   col1  col2  col3  col4 range
  <int> <int> <int> <int> <int>
1     1     6    11    16    15
2     2     7    12    17    15
3     3     8    13    18    15
4     4     9    14    19    15
5     5    10    15    20    15
ThomasIsCoding
  • 96,636
  • 9
  • 24
  • 81