Name:
Andrew ID:
Collaborated with:
This lab is to be completed in class. You can collaborate with your classmates, but you must identify their names above, and you must submit your own lab as an Rmd file on Blackboard, by 11:59pm on the day of the lab.
There are Homework 8 questions dispersed throughout. These must be written up in a separate Rmd document, together with all Homework 8 questions from other labs. Your homework writeup must start as this one: by listing your name, Andrew ID, and who you collaborated with. You must submit your own homework as a knit HTML file on Blackboard, by 11:59pm on Tuesday November 1. This document contains 15 of the 45 total points for Homework 8.
log()
will throw an error when the input is not a numeric variable. It will return NaN when the input is negative. See the example below, and note what happens when an error is encountered in the for()
loop: the execution halts and the loop stops. Write a function robust.log()
, that takes a single input x
, and first checks that x
is numeric. If this is not the case, then the function should throw a warning (with an informative error message, about requiring numeric inputs) and return NA; otherwise, the function should compute the logarithm of the input as usual. After you have written this, replace log()
below in the for()
loop example with robust.log()
. Now what do you notice about the behavior when an input is encountered that is not numeric?input.list = list(0, 1, 2, -1, "Jessica", exp(1), exp(pi))
for (i in 1:length(input.list)) {
cat(paste("We are at iteration", i, "with x =", input.list[[i]],
"and log(x) =", log(input.list[[i]]), "\n"))
}
## We are at iteration 1 with x = 0 and log(x) = -Inf
## We are at iteration 2 with x = 1 and log(x) = 0
## We are at iteration 3 with x = 2 and log(x) = 0.693147180559945
## Warning in log(input.list[[i]]): NaNs produced
## We are at iteration 4 with x = -1 and log(x) = NaN
## Error in log(input.list[[i]]): non-numeric argument to mathematical function
fibonacci()
function from Lab 8w, which is pasted below for your convenience. (This is the correct version, i.e., the debugged version, of the function.) The function computes the \(n\)th number in the Fibonacci sequence 1, 1, 2, 3, 5, 8, 13, 21, 34, …, which begins with 1, 1, and where every number after this is the sum of the previous two. But for negative numbers, or non-integer positive numbers, we get errors, as demonstrated in the examples below. Using an appropriate if()
statement and the warning()
function, implement the following behavior: if is the input is negative, or positive but not an integer, then the function throws a warning (with an informative warning message) and returns NA
. Check that you get the appropriate behavior on the examples below.fibonacci = function(n) {
if (n==1 || n==2) return(1)
my.fib = c(1,1)
for (i in 3:n) my.fib[i] = my.fib[i-1] + my.fib[i-2]
return(my.fib[n])
}
fibonacci(20)
## [1] 6765
fibonacci(-1)
## Error in my.fib[i] = my.fib[i - 1] + my.fib[i - 2]: replacement has length zero
fibonacci(1.5)
## Error in my.fib[i] = my.fib[i - 1] + my.fib[i - 2]: replacement has length zero
fibonacci()
, as demonstrated below. Further modify this function so that it throws a warning (with an informative message) and returns NA whenever the input is not a single numeric variable, i.e., a numeric variable of length 1. Check that you get the appropriate behavior on the examples below.fibonacci("hi")
## Warning in fibonacci("hi"): NAs introduced by coercion
## Error in 3:n: NA/NaN argument
fibonacci(matrix(1:4, 2, 2))
## [1] 1
fibonacci(list(x=0, y=1))
## Warning in 3:n: numerical expression has 2 elements: only the first used
## Error in 3:n: NA/NaN argument
Hw8 Q1 (6 points). Fill out the function skeleton quadratic.solver()
below that takes inputs a
, b
, c
, all numeric variables, representing the coefficients of a quadratic \(q(x)=ax^2 + bx + c\), and returns a numeric vector of length 2, which contains the roots of the quadratic, i.e., the solutions to \(q(x)=0\). Use error checking at the start to make sure that a
, b
, c
are all really single numeric variables—if not, then your function should throw a warning (with an informative message), and return NA. Demonstrate this behavior on the test inputs below. Also, when one or two of the roots are complex numbers, your function should throw a warning (with an informative message). (Hint: you’ll want to remind yourself of the well-known formula for the roots of a quadratic; there’s even a song, to the tune of “Jing Bells”!) Check that the outputs of your function match those described in comments, for the test inputs below.
quadratic.solver = function(a,b,c) {
return(0)
}
quadratic.solver("catch","this","bug") # Should throw a warning, and return NA
## [1] 0
quadratic.solver(1:5,6:10,1:15) # Should throw a warning, and return NA
## [1] 0
quadratic.solver(1,0,-1) # Should return c(1, -1)
## [1] 0
quadratic.solver(1,0,1) # Should return c(0+1i, 0-1i), with a warning
## [1] 0
get.wordtab()
, get.wordtabs()
from Lab 6m (and earlier labs and mini-lectures), pasted below for your convenience. The function get.wordtab()
uses readLines()
to read text from the website specified by the first input str.url
. The function get.wordtabs()
uses a for()
loop and calls get.wordtab()
for each element in the first input str.urls
. The function get.wordtab()
will crash when the specified website doesn’t exist. Note that this also causes problems for get.wordtabs()
, which will quit its for()
loop prematurely when get.wordtab()
encounters an error. See the examples below. Modify the get.wordtab()
function so that it uses a tryCatch()
statement around readLines()
. If an error is encountered, then this function should catch it, print a message to the screen indicating that an error has occurred, print the error message, and return NULL. Check that you get the appropriate behavior on the examples below. In particular, is get.wordtabs()
able to retrieve the rest of the word tables successfully, even after a failed attempt, when particular a website doesn’t exist?get.wordtab = function(str.url, split="[[:space:]]|[[:punct:]]",
tolower=TRUE, keep.numbers=FALSE) {
lines = readLines(str.url)
text = paste(lines, collapse=" ")
words = strsplit(text, split=split)[[1]]
words = words[words != ""]
# Convert to lower case, if we're asked to
if (tolower) words = tolower(words)
# Get rid of words with numbers, if we're asked to
if (!keep.numbers)
words = grep("[0-9]", words, inv=TRUE, val=TRUE)
table(words)
}
get.wordtabs = function(str.urls, split="[[:space:]]|[[:punct:]]",
tolower=TRUE, keep.numbers=FALSE) {
wordtabs = list()
for (i in 1:length(str.urls)) {
cat(paste("* Website:",str.urls[i],"\n"))
wordtabs[[i]] = get.wordtab(str.urls[i], split, tolower, keep.numbers)
}
return(wordtabs)
}
str.urls = c("http://stat.cmu.edu/~ryantibs/research.html",
"www.this.is.not.a.real.website.com",
"http://stat.cmu.edu/~ryantibs/teaching.html")
wordtab = get.wordtab(str.urls[2])
## Warning in file(con, "r"): cannot open file
## 'www.this.is.not.a.real.website.com': No such file or directory
## Error in file(con, "r"): cannot open the connection
wordtabs = get.wordtabs(str.urls)
## * Website: http://stat.cmu.edu/~ryantibs/research.html
## * Website: www.this.is.not.a.real.website.com
## Warning in file(con, "r"): cannot open file
## 'www.this.is.not.a.real.website.com': No such file or directory
## Error in file(con, "r"): cannot open the connection
Hw8 Q2 (9 points). The function evil.rbinom()
below is an evil binomial sampler written by your professor. Think about it as operating in three modes: “warning” mode, “error” mode, and “normal” mode. Each time evil.rbinom()
is called, it enters warning mode with probability \(p_{\rm warning}\), error mode with probability \(p_{\rm error}\), and normal mode otherwise. In warning mode, the function throws a warning, and returns an arbitrary number between 1 and 100. In error mode, the function throws an error. In normal mode, it samples a binomial random variable, with size 100, and success probability \(p_{\rm heads}\) (i.e., this is the number of coin tosses that came up heads out of 100 i.i.d. coin tosses, each with probability \(p_{\rm heads}\) of coming up heads.)
Your task is to estimate \(p_{\rm warning}\) (the probability of getting a warning), \(p_{\rm error}\) (the probability of getting an error), and \(p_{\rm heads}\) (the probability of single coin toss being heads, in normal mode), by calling evil.rbinom()
as many times as you wish. After we have loaded it with source()
below, this function is included in your R workspace, so you may just call evil.rbinom()
directly. (Hint: you’ll want to use a for()
loop to repeatedly call evil.rbinom()
many times. You’ll also want to use a tryCatch()
statement to catch both errors and warnings, that could appear each time you call evil.rbinom()
. For example, code of the form:
tryCatch({
# EXPRESSION GOES HERE
}, warning = function(w) {
return(-1)
}, error = function (e) {
return(-2)
})
will run some expression and then either call the warning function or the error function, depending on what gets encountered. When a warning is encountered, the above code block will evaluate to -1; when an error is encountered, the above will evaluate to -2; otherwise it evaluates to the value of the expression.) Your code must support your estimated probabilities, i.e., it must show clearly how you computed them. Simply stating numbers will not get you points.
source("http://www.stat.cmu.edu/~ryantibs/statcomp-F16/labs/evil.rbinom.R")