# In the document context to handle the pre-rendered chunks library(mosaic) library(mosaicCalc)
# Note the context="setup". That's so the packages # are available both at compile time and run time library(mosaic) library(mosaicCalc) library(ggformula) library(math141Z) library(etude) library(learnr) library(gradethis) library(submitr) library(basket) library(math141Zexercises) etude::show_answers(FALSE) learnr::tutorial_options(exercise.timelimit = 60, exercise.checker = gradethis::grade_learnr)
submitr::login_controls()
options(tutorial.storage = "none") vfun <- basket::check_valid #submitr::make_basic_validator(NULL, "hello") storage_actions <- submitr::record_gs4("1w3fEld2rZlR_6FuzvkA-viOLBA5JdqD3xHl-LuLX3-Y", "statprep.annie@gmail.com", vfun) # submitr::record_local("./minimal_submissions.csv") submitr::shiny_logic(input, output, session, vfun, storage_actions)
This tutorial covers the R commands used in Chapter 1 of Kilty and McAllister's Mathematical Modeling and Applied Calculus. You can ignore the "Working in RStudio" sections in the book itself.
In this tutorial, you will learn
The new R functions you will see are:
makeFun()
-- turns a tilde-expression into a functionslice_plot()
-- graphs a function with one inputdomain()
-- sets the extent of the $x$ axis (the "domain")c()
-- collects its argument together into a vector.contour_plot()
-- graphs a function with two inputsfindZeros()
-- finds solutions numericallygf_refine()
-- adds selected refinements to plotscoord_flip()
-- flips the $x$ and $y$ axes in a plot. exp()
, log()
, sin()
, cos()
-- the R implementation of the eponymous mathematical functions.Make sure to review the R Syntax tutorial [IN DRAFT: GIVE LINK] before starting this tutorial.
A formula is a mathematical expression that describes an arithmetic calculation. For example, $m c^2$ is a formula introduced by Albert Einstein to describe the energy of a particle as a function of mass and the speed of light.
Math textbooks love to write functions using $x$ and $y$, for instance,
$$y = 5 + 2 x . $$
In this traditional notation, the formula is on the right-hand side of =
. The $y$ on the left-hand side of =
is more or less meaningless. There is nothing explicit in the notation to say what the input(s) are to the function.
In the MMAC textbook, they use this traditional notation often. In our own notes, we will use a better mathematical notation:
$$f(x) \equiv 5 + 2 x .$$ You may wonder what makes this notation better. Clearly, it is more verbose. But that provides some advantages:
Consider Einstein's energy formula. The function based on this formula could be written:
$$E(m) \equiv m c^2 .$$ Notice that $E$ is simply a function of $m$. That's because the speed of light is constant, roughly 300,000km per second.
Let's implement $E(m)$ as an R function. We'll do this with a tilde expression that specifies not only the formula ($m c^2$) but which of the symbols in the formula are variables and which not. The tilde expression for Einstein's energy function is
m * c^2 ~ m
To transform the tilde expression into an R function, use makeFun()
, like this:
E <- makeFun(m * c^2 ~ m)
You might wonder how the function E()
knows the value of c
. That's a topic for Chapter 2.
Finally, a small reminder. Traditional mathematical notation uses very extensively one-letter names, like $E$ or $x$ or $y$. With computer notation, you can often be more effective by selecting names that remind you of what the thing you're creating is for. For instance, perhaps better to have created a function mass_energy()
rather than E()
, like this:
mass_energy <- makeFun(mass * speed_of_light^2 ~ mass)
r etude::include_etude("Exercises/Computing/child-leave-coat.Rmd", "**Practice 1**: ", package = "math141Zexercises")
r etude::include_etude("Exercises/Computing/doe-stand-magnet.Rmd", "**MMAC 1.1.81**: ", package = "math141Zexercises")
[This section replaces the "Working in RStudio" box on p. 14 of the MMAC book.]
The slice_plot()
function makes graphs of mathematical functions with a single input. As you know, it's conventional to draw the graph with the function input on the horizontal axis and the function output on the vertical axis.
When making a graph, you need to choose what region of the number line the horizontal axis should cover. This region is called the domain of the graphic. Many mathematical functions have function domains that cover the whole number line (that is, $-\infty$ to $+\infty$) or the positive half of the number line (that is, 0 to $+\infty$). But even though you can legitimately evaluate the mathematical function at any point in its function domain, it's not possible to draw a graph that runs to $-\infty$ or $+\infty$. (Imagine how big the computer screen would have to be! Even a screen the size of the galaxy wouldn't do it.) So, you have to make choices of the part of the function domain that is to be included in the graphic domain. Typically this choice is based on context: the region of interest.
The domain()
function is used within slice_plot()
to set the graphic domain. Here's an example:
slice_plot(tanh(x) ~ x, domain(x = c(-2,4)))
Just FYI, the mathematical $\tanh()$ is called the "hyperbolic tangent" or, for short, pronounced "tanch" which rhymes with "branch." It is an example of a "sigmoidal function," so called because it has a double bend similar to the one in the letter "S."
Let's take apart the how slice_plot()
is used.
slice_plot()
takes two arguments. The first is a tilde expression of exactly the same sort as used in makeFun()
. The second is the domain, as produced by the domain()
function.
domain()
itself takes arguments. Here, since there is only one input variable in the function, there is a single argument: a named argument with the same name as used on the right-hand side of the tilde expression. The value of that named argument is a pair of numbers: one specifying the left side of the graphic domain, the other specifying the right side. In our example, the argument is x = c(-2, 4)
, setting the graphic domain to be $-2 \leq x \leq 4$. (Domains are often written in a different notation. In that notation, the domain would be written $x \in [-2, 4]$.)
c()
does something simple but essential. It collects the two numbers--here $-2$ and $4$--into a single package. That two-number package becomes the argument x
, allowing you to specify both ends of the graphic domain at once. Some people prefer to use the range()
function, which is another way of producing a package of two numbers. Either will work fine.
r etude::include_etude("Exercises/Computing/owl-choose-roof.Rmd", "**MMAC Question 1.1.8**: ", package = "math141Zexercises")
You can use makeFun()
to turn mathematical formulas with two or more inputs into functions. It's all a matter of the tilde expression. On the right side of the tilde, list the names of all the inputs, separating them by +
. Like this example, from Example 1.2.10 of the MMAC textbook for a function
$$f(x, y, z) \equiv x^2 + 7y - z .$$
f <- makeFun(x^2 + 7*y - z ~ x + y + z)
Remember that on the right side of the tilde, x + y + z
here, the +
symbol means simply "and" or "as well as," not mathematical addition. Some people prefer to use &
rather than +
to avoid giving the wrong impression. That's fine.
On the left side of the tilde, x^2 + 7*y - z
here, the symbols +
, ^
, *
, and -
do have exactly their ordinary arithmetic meaning.
Plotting a function of two variables can be done in several ways. In these note (and in the MMAC book), we prefer to use a contour-plot format. Contour plots are produced by contour_plot()
which takes two arguments:
x + y
or capital + labor
.domain()
used with two arguments, one for each of the inputs listed in the tilde expression.For instance, consider the function
$$g(x, y) \equiv 5 - x - y/2$$ and suppose we want to plot it over the domain $-5 \leq x \leq 5$ and $-7 \leq y \leq 7$. You can accomplish this with
contour_plot( 5 - x - y/2 ~ x + y, domain(x = c(-5,5), y = c(-7, 7)) )
Since the tilde expression and the domain()
specification are typographically long, for clarity I've put each of the two arguments to contour_plot()
on it's own line. Notice that the two arguments are separated by a comma in the usual fashion. Notice as well that the closing function-application parenthesis for contour_plot()
is on its own line. Such formatting is helpful for the human reader and works well with R.
The assignment of variables to the horizontal and vertical axes is established by the order of the named arguments to domain()
.
Recall that in a contour plot, each individual contour shows the set of inputs at which the output of the function is a certain level that's labeled on the contour. The levels displayed in the plot are, by default, selected by contour_plot()
to give a fair display of the range of function outputs over the graphic domain of the plot. To keep the plot from becoming overburdened with labels, contour_plot()
will omit some of the labels. So in the example above, the contours are for function outputs of -2, 0, 2, 4, 6, 8, 10, and 12. But there are labels only for -2, 2, 6, and 10.
Sometimes you will want to choose for yourself the output levels at which a contour will be drawn and label every contour rather than skipping. You can accomplish this by giving additional named arguments to contour_plot()
, for instance:
contour_plot( 5 - x - y/2 ~ x + y, domain(x = c(-5,5), y = c(-7, 7)), contours_at = c(0, 3, 6, 9, 12), skip = 0 )
By default, contour_plot()
uses two modes to display the function: the contours as well as a color fill where the color indicates the output value at each point. This works pretty well on a computer display, but occasionally you may want to turn off the fill. To accomplish this, use the filled = FALSE
named argument.
contour_plot( 5 - x - y/2 ~ x + y, domain(x = c(-5,5), y = c(-7, 7)), contours_at = c(0, 3, 6, 9, 12), skip = 0, filled = FALSE )
r etude::include_etude("Exercises/Computing/giraffe-sing-magnet.Rmd", "**Practice 1**: ", package = "math141Zexercises")
Occasionally, it's helpful to display a function of two variables as if it were a three-dimensional object with the two inputs constituting two of the dimensions and the output represented by the third dimension. When the function is smooth, the output will a surface suspended above the input axes, analogous to an awning suspended over the two dimensions of a backyard patio.
Surface plots can be cool looking, but they can be hard to interpret quantitatively and it's practically impossible to compare two or more functions in surface plots. That's why we mostly use contour plots in displaying functions of two variables.
To make a surface plot, use the interactive_plot()
function, which takes a tilde expression and a graphic domain as arguments, just like contour_plot()
.
set.seed(102) tmp <- mosaic::rfun( ~ latitude + longitude, n = 3) terrain <- makeFun(tmp(latitude - 38, longitude + 104) ~ latitude + longitude)
interactive_plot( terrain(latitude, longitude) ~ latitude + longitude, domain(longitude = c(-99, -106 ), latitude = c(33, 43)) )
Surface plots are much easier to interpret if you can rotate them. Use your mouse or trackpad to do this.
r etude::include_etude("Exercises/Computing/doe-bid-vase.Rmd", "**MMAC Question 1.2.6**: ", package = "math141Zexercises")
Many physical, economic, and social science phenomena involve a balance between two or more factors. For example, micro-economics is famously about the balance of supply and demand.
Both the supply of a good (the quantity brought to market) and the demand (the quantity actually purchased) are functions of price. The lower the price, the higher the demand; people prefer things that are less expensive. But at a low price, sellers have little incentive to bring the good to market. The graph displays this schematically.
supply <- makeFun(1000 + 1000 * tanh(price/20 - 3) ~ price) demand <- makeFun(750 + 750 * tanh(2 -price/30) ~ price) slice_plot(supply(price) ~ price, domain(price=c(0,100)), color = "blue", label_text = "Supply", label_x = .8) %>% slice_plot(demand(price) ~ price, color = "green", label_text = "Demand", label_x = 0.2) %>% gf_labs(y = "Quantity", x = "Price ($)")
As you can see, there is one price that leads to demand being exactly in balance with supply. The mathematical process of finding such a price is called "solving simultaneously for price the supply function and the demand function." Or, stated more brusquely, find the price where the graphs of the two functions intersect.
Later, when we study differential equations, we'll see a mechanism by which a market price changes to balance supply and demand.
With slice_plot()
you can draw a graph of a function of one variable. You can "chain" multiple slice_plot()
commands together to put multiple graphs on the same axis. The syntax is this:
slice_plot(demand(price) ~ price, domain(price = c(0,100)), color = "green" ) %>% slice_plot(supply(price) ~ price, color = "blue")
Comment on the %>%
symbol. slice_plot()
is arranged to accept an optional input: a graph that's already been made. When such an input is given, slice_plot()
adds the new graph to the graph provided as input. You can add as many layers of such graphics as you like. slice_plot()
provides a label_text=
argument that puts a label you provide on the plot, positioning it at the height of the function. For instance, in the example that started this section, slice_plot()
was given the argument label_text = "Supply"
. The quotation marks around the label are essential.
An alternative to plotting two graphs on top of one another and looking for the crossing points (if any) is to construct a single function that is the difference between the two functions. For instance:
excess_demand <- makeFun(demand(price) - supply(price) ~ price) slice_plot(excess_demand(price) ~ price, domain(price = c(0, 100)))
According to the graph, for prices below about \$50, demand exceeds supply, while for prices above about \$70 there is excess supply compared to demand. Where the graph crosses an output of zero, demand and supply are equal.
Looking for the zero-crossing of a difference function is a perfect sensible way to see whether the two functions produce the same output for a given input. But sometimes you won't want the trouble of reading the x-axis to find a precise value of the input where the zero-crossing happens. An alternative, not involving graphics, is to use findZeros()
.
For instance, to find the price where supply and demand are equal, apply findZeros()
to the difference function:
findZeros(excess_demand(price) ~ price)
Evaluating supply and demand at that price shows that they are indeed equal:
demand(56.64) supply(56.64)
r etude::include_etude("Exercises/Computing/camel-lie-gloves.Rmd", "**MMAC Question 1.3.9**: ", package = "math141Zexercises")
Exercise: find the zeros of the sin()
function.
Suppose you have a function of two variables and want to find values for the inputs that produce a given output value. For instance, let's look for inputs to the terrain()
function (from previous sections) which produce an output of 1500 ft. This is easy using contour_plot()
. Since terrain()
gives an output with units thousands of feet, we just need to draw the contour at 1.5.
contour_plot( terrain(latitude, longitude) ~ latitude + longitude, domain(longitude = c(-99, -106 ), latitude = c(33, 43)), contours_at = 1.5 )
There's not much to say about the exponential, sine, cosine, and log functions. In R they correspond to exp()
, sin()
, cos()
, and log()
respectively.
sin()
and cos()
(and other trigonometric functions) is always given in radians rather than degrees. If you have an input specified in degrees, you'll have to convert it to radians. For fun, we'll write the conversion function here:degrees_to_radians <- makeFun( degrees * pi / 180 ~ degrees )
The inverse of a function $f()$ is another function $g()$ (sometimes $g()$ is named $f^{-1}()$) such that applying $g()$ to the output of $f()$ produces whatever the original input to $f()$ was.
To illustrate, exp()
and log()
are inverse functions. For example, using 3 as the input:
log(exp(3)) exp(log(3))
In contrast, sin()
and cos()
are not inverses of each other, as demonstrated this way:
sin(cos(3)) cos(sin(3))
Given a function $f()$, it's easy to plot the inverse. Let's look at this function:
f <- rfun( ~ x, n = 3, seed= 101) slice_plot(f(x) ~ x, domain(x = c(-5, 5)), label_text = "f(x)")
To plot the inverse of $f()$ you just need to flip the horizontal and vertical axes, like this:
slice_plot(f(x) ~ x, domain(x = c(-5, 5))) %>% gf_refine(coord_flip())
To evaluate the inverse, choose the point you want on the horizontal axis and read up to the value of the function according to the vertical axis.
A mathematician would point out that this flipped graph shows that $f()$ does not have an inverse; the flipped graph fails the "vertical line" test. The mathematical definition of a function requires that the function have only one possible output for any value of the input.
Computing functions, however, don't necessarily respect that definition. Certainly, function like exp()
or sin()
which implement mathematical function will always produce the same output for any given input. You can confirm this by running the expressions in the code block over and over again. But the functions runif()
and date()
don't have this unique-output feature.
exp(2) sin(3) runif(1) date()
r etude::include_etude("Exercises/Computing/octopus-know-bottle.Rmd", "**Practice 1**: ", package = "math141Zexercises")
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.