--- title: 'Lecture 7: More On Functions' font-family: Garamond date: "30 September 2015" output: beamer_presentation transition: none --- ## How We Extend Functions - Multiple functions: Doing different things to the same object - Sub-functions: Breaking up big jobs into small ones Reading for Friday: 1.3, 7.3--7.5, 7.11, 7.13 of Matloff (skipping "extended examples") ## In our last episode ... Functions tie together related commands: ``` my.clever.function <- function(an.argument,another.argument) { # many lines of clever calculations return(important.result) } ``` Inputs/arguments and outputs/return values define the interface A user only cares about turning inputs into outputs correctly ## Why You Have to Write More Than One Function Meta-problems: - You've got more than one problem - Your problem is too hard to solve in one step - You keep solving the same problems Meta-solutions: - Write multiple functions, which rely on each other - Split your problem, and write functions for the pieces - Solve the recurring problems once, and re-use the solutions ## Writing Multiple Related Functions Statisticians want to do lots of things with their models: estimate, predict, visualize, test, compare, simulate, uncertainty, ... Write multiple functions to do these things Make the model one object; assume it has certain components ## Consistent Interfaces - Functions for the same kind of object should use the same arguments, and presume the same structure - Functions for the same kind of task should use the same arguments, and return the same sort of value (to the extent possible) ## Keep related things together - Put all the related functions in a single file - Source them together - Use comments to note ***dependencies*** ## Power-Law Scaling for Urban Economies (cont'd.) Remember the model: $$Y = y_0 N^{a} + \mathrm{noise}$$ $$(\mathrm{output}\ \mathrm{per}\ \mathrm{person}) = $$ $$ (\mathrm{baseline}) (\mathrm{population})^{\mathrm{scaling}\ \mathrm{exponent}} + \mathrm{noise}$$ Estimated parameters $a$, $y_0$ by minimizing the mean squared error Exercise: Modify the estimation code from last time so it returns a list, with components `a` and `y0` ## Example: Predicting from a Fitted Model Predict values from the power-law model: ``` # Predict response values from a power-law scaling model # Inputs: fitted power-law model (object), vector of values at which to make # predictions at (newdata) # Outputs: vector of predicted response values predict.plm <- function(object, newdata) { # Check that object has the right components stopifnot("a" %in% names(object), "y0" %in% names(object)) a <- object$a y0 <- object$y0 # Sanity check the inputs stopifnot(is.numeric(a),length(a)==1) stopifnot(is.numeric(y0),length(y0)==1) stopifnot(is.numeric(newdata)) return(y0*newdata^a) # Actual calculation and return } ``` ## Example: Predicting from a Fitted Model ``` # Plot fitted curve from power law model over specified range # Inputs: list containing parameters (plm), start and end of range (from, to) # Outputs: TRUE, silently, if successful # Side-effect: Makes the plot plot.plm.1 <- function(plm,from,to) { # Take sanity-checking of parameters as read y0 <- plm$y0 # Extract parameters a <- plm$a f <- function(x) { return(y0*x^a) } curve(f(x),from=from,to=to) # Return with no visible value on the terminal invisible(TRUE) } ``` ## Example: Predicting from a Fitted Model When one function calls another, use \texttt{...} as a meta-argument, to pass along unspecified inputs to the called function: ``` plot.plm.2 <- function(plm,...) { y0 <- plm$y0 a <- plm$a f <- function(x) { return(y0*x^a) } # from and to are possible arguments to curve() curve(f(x), ...) invisible(TRUE) } ``` ## Sub-Functions Solve big problems by dividing them into a few sub-problems - Easier to understand: get the big picture at a glance - Easier to fix, improve and modify: tinker with sub-problems at leisure - Easier to design: for future lecture - Easier to re-use solutions to recurring sub-problems Rule of thumb: A function longer than a page is probably too long ## Sub-Functions or Separate Functions? Defining a function inside another function - Pros: Simpler code, access to local variables, doesn't clutter workspace - Cons: Gets re-declared each time, can't access in global environment (or in other functions) - Alternative: Declare the function in the same file, source them together Rule of thumb: If you find yourself writing the same code in multiple places, make it a separate function ## Example: Plotting a Power-Law Model Our old plotting function calculated the fitted values But so does our prediction function ``` plot.plm.3 <- function(plm,from,to,n=101,...) { x <- seq(from=from,to=to,length.out=n) y <- predict.plm(object=plm,newdata=x) plot(x,y,...) invisible(TRUE) } ``` ## Summary - ***Multiple functions*** let us do multiple related jobs, either on the same object or on similar ones - ***Sub-functions*** let us break big problems into smaller ones, and re-use the solutions to the smaller ones Next time: Designing functions from the top down