If you specify a value for either or both of slope and intercept, they will be used. If you don’t, values 5 for slope and/or 3 for intercept will be used instead:
linear3(10, 2, -1) # 10 * 2 - 1
[1] 19
linear3(10, intercept =12) # 10 * 5 + 12
[1] 62
linear3(10) # 10 * 5 + 3
[1] 53
but…
the value of x is not optional:
linear3(slope =1, intercept =4)
Error in `linear3()`:
! argument "x" is missing, with no default
Using R’s built-ins
When you write a function, you can use anything built-in to R, or even any functions that you defined before.
For example: you have data x and y, and you want to obtain a predicted value of y for an input value x_pred based on the regression of y on x.
Steps:
run the regression
pull out the intercept and slope
use function linear3 to use x_pred, the slope, and the intercept to work out the prediction.
Without a function
In worksheet 1, you met a dataframe mtcars which contained data on some cars, including gas mileage mpg and weight wt. What would be the predicted gas mileage of a car that weighs 4 tons?
Regression, but not obvious how to get slope and intercept:
mtcars.1<-lm(mpg ~ wt, data = mtcars)summary(mtcars.1)
Call:
lm(formula = mpg ~ wt, data = mtcars)
Residuals:
Min 1Q Median 3Q Max
-4.5432 -2.3647 -0.1252 1.4096 6.8727
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 37.2851 1.8776 19.858 < 2e-16 ***
wt -5.3445 0.5591 -9.559 1.29e-10 ***
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
Residual standard error: 3.046 on 30 degrees of freedom
Multiple R-squared: 0.7528, Adjusted R-squared: 0.7446
F-statistic: 91.38 on 1 and 30 DF, p-value: 1.294e-10
Without a function, cont’d
Use tidy from broom:
m <-tidy(mtcars.1)m
Slope is second number in estimate column and intercept is first:
slope <- m$estimate[2]intercept <- m$estimate[1]
Without a function, cont’d
and now use linear3:
x_pred <-4linear3(x_pred, slope, intercept)
[1] 15.90724
Making a function of this
Use “generic” names for things:
make_prediction <-function(x, y, x_pred) { reg.1<-lm(y ~ x) m <-tidy(reg.1) my_slope <- m$estimate[2] my_intercept <- m$estimate[1]linear3(x_pred, my_slope, my_intercept)}
Test function on mtcars data:
make_prediction(mtcars$wt, mtcars$mpg, 4)
[1] 15.90724
The answer is correctly what we got before.
Passing things on
lm has a lot of options, with defaults, that we might want to change.
Two of these are formula, the y ~ x thing, and data, the dataframe.
Instead of supplying x and y, we can make lm do the heavy lifting for us, thus:
The ... in the header line means “accept any other input”, and the ... in the lm line means “pass anything other than x_pred straight on to lm”.
Using ...
To use make_prediction2, instead of supplying an x and y, we supply a model formula and a dataframe, so that using this function looks a lot more like using lm:
make_prediction2(x_pred =4, formula = mpg ~ wt, data = mtcars)
[1] 15.90724
and the answer is the same as before, but more aesthetically obtained, and the function will work with any variables in any dataframe.
Running a function for each of several inputs
We might want to do several predictions of mpg, for different values of wt, maybe these:
wts <-c(2, 3, 4)wts
[1] 2 3 4
you might think of using a for loop for this
but R has a “functional programming” method called map, in this case map_dbl because our function make_predictions2 returns a decimal number (dbl). It goes like this:
If the “for each” part is simple, go ahead and use map_-whatever.
If not, write a function to do the complicated thing first.
Example: “half or triple plus one”: if the input is an even number, halve it; if it is an odd number, multiply it by three and add one.
This is hard to do as a one-liner: first we have to figure out whether the input is odd or even, and then we have to do the right thing with it.
Odd or even?
Odd or even? Work out the remainder when dividing by 2:
6%%2
[1] 0
5%%2
[1] 1
5 has remainder 1 so it is odd.
Write the function
First test for integerness, then test for odd or even, and then do the appropriate calculation:
hotpo <-function(x) {stopifnot(round(x) == x) # passes if input an integer remainder <- x %%2if (remainder ==1) { # odd number ans <-3* x +1 }else { # even number ans <- x %/%2# integer division } ans}