For every fruit there is a loop ... i.e., more on control flow

This week’s problem set and extra-credit opportunity can be found here:

The big new concept or construct discussed this week was the for-loop. Similar to the if and else constructs, the for-loop allows us to control the flow of operations in R. Whereas the if and else construct allows us to get the computer to do one thing if some condition is TRUE and another thing if the condition is FALSE, the for-loop construct allows us to instruct the computer to repeat a task or calculation as set number of times. This works like so:

# define object.o as a sequence of integers

object.o <- 1:10

for (element.i in object.o) {	
	# do something like say
	print("Hello") # or
	print(element.i) # or
	Sys.sleep(time = 2) # go to sleep for 2 seconds
}

The above code will instruct the computer to repeat the tasks of printing "Hello", printing an element.i from the object.o, and sleeping for 2 seconds, 10 times each.

We can use these for-loop constructs for all sorts of tasks that need to be repeated a certain number of times. Say, I need 10 random numbers. We could do the following:

rnorm(n = 10, mean = 0, sd = 1) # returns 10 random numbers 
			 	# as one vector.

Or we could instruct R to give us 1 random number 10 times in a row.

for (i in 1:10) {
	x <- rnorm(n = 1, mean = 0, sd = 1)
	print(x)
}			 	

If we ask R to do something once for every element i in a sequence of integers say 1:10, we can use the fact that the i’s are integers to index vectors and store output in specific locations of some vector. This might look something like this.

x <- rep(NA, times = 10) # initiate an empty vector

for (i in 1:10) {
	x[i] <- sample(x = letters, size = 1, replace = TRUE)
}

In the above code we asked R to pick one random letter from the alphabet (the object letters) and store the random letter in the ith location of the empty NA vector x. At the end of the loop the vector x will contain 10 random letters.

Another use for loops might be the creation of counters (in combination with if and else constructs).

Suppose we wanted to count the number of even numbers in some vector of integers. A for-loop can help solve this problem.

random.integers <- sample(x = 1:1000, size = 100, replace = TRUE)

# initiate two counters and set them to zero

even.counter <- 0
odd.counter <- 0

for (i in 1:100) {

	if (random.integers[i] %% 2 == 0) {

		even.counter <- even.counter + 1

	} else {

		odd.counter <- odd.counter + 1

	}

}

even.counter
odd.counter

During class on Tuesday we also reviewed the creation of functions by way of revisiting Problem Set 4. A set of possible responses is reproduced below.

Consider two examples:

The min

my_min <- function(input) {

	output <- sort(x = input, decreasing = FALSE)
	return(output)

}

# test my_min() and compare to min() 

x <- 10:1
y <- c(0, 1, -1)
z <- rnorm(n = 10, mean = 0, sd = 1)

min(x= x)
min(x= y)
min(x= z)

my_min(input = x)
my_min(input = y)
my_min(input = z)

The mean

my_mean <- function(input) {

	ouput <- sum(x = input)/length(x = input)
	return(output)

}

Even vs. odd

even <- function(input) {

	output <- ifelse(test = input %% 2 == 0, 
			  yes = "even",
			   no = "odd")
	return(output)

}

Absolute value (Version 1)

my_abs <- function(input) {

	output <- ifelse(test = input < 0,
		 	  yes = input * -1, 
		 	   no = input)
	return(output)

}

Absolute value (Version 2)

my_abs <- function(input) {

	output <- sqrt(x = input^2)
	return(output)

}

Median

# this version depends on the even() function above

my_median <- function(input) {

	n <- length(x = input)
	x <- sort(x = input)

	if (even(input = n) == "even") {

		return(mean(x = x[n/2, n/2 + 1]))

	} else {

		return(x[ceiling(x = n/2)])

	}

}

# test my_median() on even and odd length inputs 
# and compare to median() 

x <- rnorm(n = 100)
y <- rnorm(n = 101)

median(x = x)
my_median(input = x)

median(x = y)
my_median(input = y)

On Tuesday, we also covered a number other control flow operators (repeat and while-loops) and a few odd functions. See examples below:

all( ), any( ), and is.element( )

x <- -5:5

# Query if all elements of the vector x contain values smaller than 0

all(x < 0)

# Query if any elements of the vector x contain values smaller than 0

any(x < 0)

# Eveluates if elements of the first vector (A and B) and elements of
# the second vector (A ... Z). They of course are and the function 
# will return TRUE, TRUE as both A and B are part of the vector of
# all letters in the alphabet

is.element(c("A", "B"), LETTERS)

# The is.element function is equivalent to the %in% operator
# The below will evaluate if the values in the sequence from 
# 0 to 3 can be found in the sequence from 1:10. The command
# will obviously return FALSE, TRUE, TRUE, and TRUE. 

0:3 %in% 1:10

counters

In the code below a counter is initiated at zero and then updated in the for-loop if an element of x is even.

x <- c(2, 5, 3, 9, 8, 11, 6)

count <- 0

for (i in 1:length(x)) {

	if(x[i] %% 2 == 0)  {
		count <- count + 1
	}

}

count

repeat-loop with the ‘break’ control flow operator

A repeat-loop will continue forever unless you specify a break.

x <- 1

repeat {
	print(x)
	x <- x + 1
	if (x == 6) {
		break
	}
}

for-loop with ‘next’ control flow operator

I forgot to mention this operator in class. Its usage and purpose is fairly straightforward. The next operator basically instructs R to skip back to the beginning of the loop i.e., it is a temporary break.

for (i in 1:5) {
	if (i == 3) {
		next
	}
	print(i)
}

while-loop with counter

The while loop repeats commands while some condition is met. As long as the condition is true the loop will continue. Make sure that the condition can be broken else the while-loop will continue, forever-ever.

count <- 0

while (count < 6) {
	print(count)
	count <- count + 1
}