--- title: "Loop-the-Loops" author: "JJB + Course" date: "06/26/2018" output: html_document --- {r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE)  # Iteration ## Sequences ### Example: Sequence Generation {r my_seq} ex_vec = c(5, 3, -2, 42) # Generate a sequence with colon 1:length(ex_vec) # Generate a sequence using seq seq(1, length(ex_vec)) # Generate a sequence with a vector seq_along(ex_vec) # Generate a sequence for a length seq_len(length(ex_vec))  ## while ### Example: Counting up to Three {r while-up-to-three} counter = 0 while( counter < 3) { message("start of loop counter:", counter) counter = counter + 1 message("end of loop counter:", counter) } counter  ### Example: Gambler's Ruin {r gamblers-ruin} money = 5 # Controls the random number generation # This will net me 11$ dollars # set.seed(7778) #set.seed(4) while ( money <= 10) { message("I have ", money , "$! Let's gamble!") coin_flip_result = rbinom(1, 1, 0.4) if (coin_flip_result == 1) { money = money + 1 } else { money = money - 1 } } message("I walked away with ", money , "$!!")  ### Exercise: Counting down from 10 {r ex-countdown, eval = TRUE} counter = 10 while ( counter > 0 ) { message("Countdown is at: ", counter) counter = counter - 1 } message("Blast off!")  ### Example: Loop to Infinity Please note, the example code chunks within this section are purposefully set to eval = FALSE as they will _never_ end. Please do not change this option. If you opt to run this code, please press the "Stop" icon that appears in the upper-right corner of the **console** to exit. {r, eval = FALSE} money = 5 while (money =< 10) { message("I have ", money , "$! Let's gamble!") # Gambler with bad luck always loses… money = money - 1 } message("I walked away with ", money , "$!!")  {r, eval = FALSE} # The loop failed because of a never-ending condition… while (TRUE) { message("I have ", money , "$! Let's gamble!") # Gambler with bad luck always loses… money = money - 1 }  ### Example: Sequence Convergence {r} # Values for Geometric Summation a = 2 r = 0.5 # Compute formula a/(1 - r)  Let's see what happens when we individual compute the terms. {r} # Computing terms individually round(a*r^(0:20), 4)  Notice, each of these terms is significantly different from the next up until a specific point. {r} # Value after successively computing and adding terms. round(cumsum(a*r^(0:20)), 4)  In essence, we are hitting a precision threshold between consecutive terms given by {r} .Machine$double.eps  This gives us the ability to compare up to 1e-15 places accurately. {r} sprintf("%.15f", 1 + c(-1, 1)*.Machine$double.eps)  So, this leads to the following issue with comparing numeric (e.g. floating point numbers): {r} # Not equivalent (0.10 + 0.05) == 0.15 # Using an epsilon neighborhood allows us to see the difference all.equal(0.10 + 0.05, 0.15)  We can see convergence to the analytical solution get reached via the graph. {r} # Make a graph! series_data = data.frame(index_of_summation = 0:20, summed_values = round(cumsum(a*r^(0:20)), 4) ) library("ggplot2") ggplot(series_data, aes(index_of_summation, summed_values)) + geom_point() + geom_hline(yintercept=4, color="red") + annotate("text", label = "True Value", x = 9.25, y = 4.15, size = 5) + labs(x = "Index of Summation (k)", y = "Summed Value") + theme_bw() #ggsave("summed_plot.png")  Thus, we can use a while loop to obtain the solution up to a specific number's precision. {r} # Notice, this computation is done # for the first two parts of the series eps = 0.001 # Set a discrepancy counter = 1 # Keep track of number of iterations x1 = a; x2 = a * r # Compute first two terms separately summed = x1 + x2 # Sum the terms message("x1 = ", x1, ", x2 = ", x2, ", summed = ", summed) while( abs(x1 - x2) >= eps) { # Any difference between terms? message("start of loop: x1 = ", x1, ", x2 = ", x2, ", summed = ", summed) counter = counter + 1 # Count loop iteration x1 = x2 # Set last computed term to x1 x2 = a*r^counter # Compute new term summed = summed + x2 # Add new term to summation message("end of loop: x1 = ", x1, ", x2 = ", x2, ", summed = ", summed) } summed; counter # Display summation and n iterations  ### Example: Positional Access with while {r} values = c(-4, 2, 5, 9, 0) # Values to add together summed = 0 # Summed value counter = 1 # Keep track of number of iterations while( counter < length(values)) { # Any elements left? summed = summed + values[counter] # Add new term to summation counter = counter + 1 # Count loop iteration } summed # Display summation and n iterations sum(values) # Check against built-in function  ## for loops ### Example: Summations $$\sum\limits_{i = 1}^n {{x_i}} = {x_1} + {x_2} + \cdots + {x_n}$$ {r ex-for-loop} # Define vector to sum x = c(5, 3, -2, 42) # Define a variable to hold the result of values added together summed = 0 # Sum values together for(index in seq_along(x)) { summed = summed + x[index] } # End result of summation summed # This is equivalent to sum() sum(x)  ### Exercise: Adding vectors together {r create-element-wise-add, eval = FALSE} x = c(8L, -55L, 42L, 0L) y = c(3L, -9L, 65L, 2L) # Code required using a for loop n_obs = length(x) z = numeric(n_obs) for (i in seq_along(x)) { z[i] = y[i] + x[i] } z # Vectorized approach x + y  {r, eval = FALSE} x = c(8L,-55L, 42L, 0L) y = c(3L,-9L, 65L) # Code required using a for loop n_obs = length(x) z = numeric(n_obs) if (length(x) == length(y)) { for (i in seq_along(x)) { z[i] = y[i] + x[i] } z } else { stop("Must have the same length to add element-wise values.") }  {r} a = numeric() value = 0 for(i in 1:length(a)) { message("This loop ran in the colon operator") value = value + i } # All the same statements! 1:length(a) 1:0 seq(1, 0) c(1, 0) # This gives us the 1 value for( i in seq_along(a)) { message("This loop ran in seq_along") value = value - i } value  ### Example: Adding only positive numbers {r add-pos} x = c(5, 3, -2, 42) summed = 0 for(index in seq_along(x)) { if(x[index] < 0) { next } summed = summed + x[index] }  ### Exercise: Adding non-na values {r add-non-na, eval = FALSE} a = c(-1L, -24L, NA, 11L, 0L, NA) summed = 0 for( i in seq_along(a) ) { if( is.na(a[i]) ) { next } summed = summed + a[i] } summed # Step 1: # Found non-missing values in a !is.na(a) # Step 2: # Subset a to retrieve only the values that are non-missing a[!is.na(a)] # Step 3: # Add together all the non-missing values sum(a[!is.na(a)])  ### Example: Classifying Data {r iterative-classification} ## Construct BP data bp_data = data.frame( Subject_ID = c("S005", "S130", "S023", "S098", "S035", "S007", "S104"), Sex = c("Male", "Female", "Male", "Male", "Male", "Female", "Female"), Systolic = c(110, 141, 125, 168, 115, 122, 135) ) # Count number of observations n_obs = nrow(bp_data) # Create an empty character vector (e.g., "") classify_vector = character(n_obs) # Iterate through a vector containing positional indexes for (i in seq_len(n_obs)) { # Retrieve ith value being tested to avoid multiple subsets in condition test_obs_value = bp_data$Systolic[i] # Classify bp and save it into the vector at position i classify_vector[i] = if (test_obs_value < 120) { "Normal" } else if (test_obs_value < 129) { "Elevated" } else if (test_obs_value < 139) { "Stage 1" } else { "Stage 2" } } # View classifications classify_vector # Save classifications into the data.frame bp_data\$BP_Type = classify_vector # Output data.frame into markdown table knitr::kable(bp_data)  ### Example: Matrix Multiplication with Built-in Binary Operator {r matrix-multiplication-iteration} A = matrix(seq(1, 6), nrow = 2, ncol = 3) A B = matrix(seq(1, 6), nrow = 3, ncol = 2) B C = A %*% B C  ### Example: Nested Iteration for Matrix Multiplication {r nested-matrix-multiplication} A = matrix(seq(1, 6), nrow = 2, ncol = 3) B = matrix(seq(1, 6), nrow = 3, ncol = 2) C = matrix(0, nrow = nrow(A), ncol = ncol(B)) if (ncol(A) == nrow(B)) { for (i in seq_len(nrow(A))) { for (j in seq_len(ncol(B))) { for (k in seq_len(ncol(A))) { C[i, j] = C[i, j] + A[i, k] * B[k, j] } } } } else { stop("matrices A and B dimensions are improper") } # %*% is the binary operator for matrix multiplication all.equal(C, A %*% B) C  ### Exercise: Fill a matrix with a constant To design two nested for loops that populate a matrix with a constant my_val. {r ex-fill-matrix}  ## repeat ### Example: Counting up to Four {r repeat-count-to-four} counter = 0 repeat { counter = counter + 1 if(counter > 3) { break } } counter  ### Example: Who is the fairest of them all? {r repeat-force-answer} repeat { input = readline("Who is the fairest of them all? ") if (input == "JJB" || input == "Balamuta") { message("Correct! You're future looks brighter.") break } else { message("I'm sorry Human, I'm afraid that's incorrect.") } }  ### Exercise: Adding numbers until first NA {r repeat-add-numbs, eval = FALSE} x = c(9L, 88L, -2L, NA, 0L, NA) summed = 0 counter = 1 repeat { message("On iteration: ", counter, ", x[counter] = ", x[counter]) if ( is.na(x[counter]) ) { break } summed = summed + x[counter] counter = counter + 1 } summed  {r} x = "Nick" students = c("Bill", "Jenny", "Pete", "Nick") # Search for x in the set of students x %in% students # Search for students in the set of x students %in% x