For iteratively performing computation steps, R has a special syntax for for loops. Here is an example of an (again, stupid, but illustrative) example of a for loop in R:
# fix a vector to transforminput_vector <-1:6# create output vector for memory allocationoutput_vector <-integer(length(input_vector))# iterate over length of inputfor (i in1:length(input_vector)) {# multiply by 10 if evenif (input_vector[i] %%2==0) { output_vector[i] <- input_vector[i] *10 }# otherwise leave unchangedelse { output_vector[i] <- input_vector[i] }}output_vector
## [1] 1 20 3 40 5 60
Exercise 2.12
Let’s practice for-loops and if/else statements!
Create a vector a with 10 random integers from range (1:50).
Create a second vector b that has the same length as vector a.
Then fill vector b such that the \(i\)th entry in b is the mean of a[(i-1):(i+1)].
Do that using a for-loop.
Note that missing values are equal to 0 (see example below).
Print out the result as a tibble whose columns are a and b.
Example: If a has the values [25, 39, 12, 33, 47, 3, 48, 14, 45, 8], then vector b should contain the values [21, 25, 28, 31, 28, 33, 22, 36, 22, 18] when rounded to whole integers. The value in the fourth position of b (value 31), is obtained with (a[3] + a[4] + a[5])/3. The value in the first position of b (value 21) is obtained with (0 + a[1] + a[2])/3 and similarly the last value with (a[9] + a[10] + 0)/3. (Hint: use conditional statements if, if else and else to deal specifically with the edge cases (first and last entry in the vectors).)
a <-c(sample((1:50), 10, replace = T))b <-c(integer(length(a)))for (i in1:length(a)){if (i ==1) { b[i] <- (sum(a[i:(i+1)])/3) } elseif (i ==length(a)) { b[i] <- (sum((a[(i-1):i]))/3) }else { b[i] <- (mean(a[(i-1):(i+1)])) }}tibble(a, b)
Base R provides functional iterators (e.g., apply), but we will use the functional iterators from the purrr package. The main functional operator from purrr is map which takes a vector and a function, applies the function to each element in the vector and returns a list with the outcome. There are also versions of map, written as map_dbl (double), map_lgl (logical) or map_df (data frame), which return a vector of doubles, Booleans or a data frame. The following code repeats the previous example which used a for-loop but now within a functional style using the functional iterator map_dbl:
The trailing ~ indicates that we define an anonymous function. It, therefore, replaces the usual function(...) call which indicates which arguments the anonymous function expects. To make up for this, after the ~ we can use .x for the first (and only) argument of our anonymous function.
To apply a function to more than one input vector, element per element, we can use pmap and its derivatives, like pmap_dbl etc. pmap takes a list of vectors and a function. In short-hand notation, we can define an anonymous function with ~ and integers like ..1, ..2 etc, for the first, second … argument. For example:
x <-1:3y <-4:6z <-7:9pmap_dbl(list(x, y, z),~ ..1- ..2+ ..3)
## [1] 4 5 6
Exercise 2.13
Use map_dbl and an anonymous function to take the following input vector and return a vector whose \(i\)th element is the cumulative product of input up to the \(i\)th position divided by the cumulative sum of input up to that position. (Hint: the cumulative product up to position \(i\) is produced by prod(input[1:i]); notice that you need to “loop over”, so to speak, the index \(i\), not the elements of the vector input.)