edits
This commit is contained in:
@@ -9,7 +9,7 @@ This section will use the following packages:
|
||||
```{julia}
|
||||
using CalculusWithJulia
|
||||
using Plots
|
||||
plotly()
|
||||
plotly();
|
||||
```
|
||||
|
||||
```{julia}
|
||||
@@ -18,6 +18,7 @@ plotly()
|
||||
using Roots
|
||||
using SymPy
|
||||
using DataFrames
|
||||
using Latexify
|
||||
|
||||
nothing
|
||||
```
|
||||
@@ -83,7 +84,7 @@ plotly()
|
||||
|
||||
(Certain graphics are produced with the `gr()` backend.)
|
||||
|
||||
With `Plots` loaded, it is straightforward to graph a function.
|
||||
With `Plots` loaded and a backend chosen, it is straightforward to graph a function.
|
||||
|
||||
|
||||
For example, to graph $f(x) = 1 - x^2/2$ over the interval $[-3,3]$ we have:
|
||||
@@ -97,8 +98,9 @@ plot(f, -3, 3)
|
||||
The `plot` command does the hard work behind the scenes. It needs $2$ pieces of information declared:
|
||||
|
||||
|
||||
* **What** to plot. With this invocation, this detail is expressed by passing a function object to `plot`
|
||||
* **Where** to plot; the `xmin` and `xmax` values. As with a sketch, it is impossible in this case to render a graph with all possible $x$ values in the domain of $f$, so we need to pick some viewing window. In the example this is $[-3,3]$ which is expressed by passing the two endpoints as the second and third arguments.
|
||||
* **What** to plot. With this invocation, this detail is expressed by passing a function object to `plot`
|
||||
|
||||
* **Where** to plot; the `xmin` and `xmax` values. As with a sketch, it is impossible in this case to render a graph with all possible $x$ values in the domain of $f$, so we need to pick some viewing window. In the example this has $x$ limits of $[-3,3]$; expressed by passing the two endpoints as the second and third arguments.
|
||||
|
||||
|
||||
Plotting a function is then this simple: `plot(f, xmin, xmax)`.
|
||||
@@ -198,9 +200,9 @@ This choices does *not* show the $x-y$ axes. As such, we might layer on the axes
|
||||
To emphasize concepts, we may stylize a function graph, rather than display the basic graphic. For example, in this graphic highlighting the amount the function goes up as it moves from $1$ to $x$:
|
||||
|
||||
```{julia}
|
||||
gr()
|
||||
#| echo: false
|
||||
let
|
||||
plt = let
|
||||
gr()
|
||||
f(x) = x^2
|
||||
|
||||
empty_style = (xaxis=([], false),
|
||||
@@ -239,6 +241,7 @@ let
|
||||
])
|
||||
current()
|
||||
end
|
||||
plt
|
||||
```
|
||||
|
||||
```{julia}
|
||||
@@ -315,6 +318,7 @@ plot(xs, ys)
|
||||
This plots the points as pairs and then connects them in order using straight lines. Basically, it creates a dot-to-dot graph. The above graph looks primitive, as it doesn't utilize enough points.
|
||||
|
||||
|
||||
|
||||
##### Example: Reflections
|
||||
|
||||
|
||||
@@ -396,17 +400,16 @@ The graph is that of the "inverse function" for $\sin(x), x \text{ in } [-\pi/2,
|
||||
When plotting a univariate function there are three basic patterns that can be employed. We have examples above of:
|
||||
|
||||
|
||||
* `plot(f, xmin, xmax)` uses an adaptive algorithm to identify values for $x$ in the interval `[xmin, xmas]`,
|
||||
* `plot(xs, f.(xs))` to manually choose the values of $x$ to plot points for, and
|
||||
* `plot(f, xmin, xmax)` uses a recipe implementing an adaptive algorithm to identify values for $x$ in the interval `[xmin, xmas]`,
|
||||
|
||||
* `plot(xs, f.(xs))` to manually choose the values of $x$ to plot points for, and
|
||||
|
||||
Finally, there is a merging of the first two following the pattern:
|
||||
|
||||
* `plot(xs, f)`
|
||||
|
||||
|
||||
Finally there is a merging of these following either of these patterns:
|
||||
|
||||
|
||||
* `plot(f, xs)` *or* `plot(xs, f)`
|
||||
|
||||
|
||||
Both require a manual choice of the values of the $x$-values to plot, but the broadcasting is carried out in the `plot` command. This style is convenient, for example, to down sample the $x$ range to see the plotting mechanics, such as:
|
||||
All require a manual choice of the values of the $x$-values to plot, but the broadcasting is carried out in the `plot` command. This style is convenient, for example, to down sample the $x$ range to see the plotting mechanics, such as:
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -416,10 +419,10 @@ plot(0:pi/4:2pi, sin)
|
||||
#### NaN values
|
||||
|
||||
|
||||
At times it is not desirable to draw lines between each successive point. For example, if there is a discontinuity in the function or if there were a vertical asymptote, such as what happens at $0$ with $f(x) = 1/x$.
|
||||
At times it is not desirable to draw lines between each successive point. For example, if there is a discontinuity in the function or if there were a vertical asymptote.
|
||||
|
||||
|
||||
The most straightforward plot is dominated by the vertical asymptote at $x=0$:
|
||||
For example,what happens at $0$ with $f(x) = 1/x$. The most straightforward plot is dominated by the vertical asymptote at $x=0$:
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -437,10 +440,10 @@ As we see, even with this adjustment, the spurious line connecting the points wi
|
||||
plot(q, -1, 1, ylims=(-10,10))
|
||||
```
|
||||
|
||||
The dot-to-dot algorithm, at some level, assumes the underlying function is continuous; here $q(x)=1/x$ is not.
|
||||
The dot-to-dot algorithm, at some level, assumes the underlying function is *continuous*; here $q(x)=1/x$ is not.
|
||||
|
||||
|
||||
There is a convention for most plotting programs that **if** the $y$ value for a point is `NaN` that no lines will connect to that point, `(x,NaN)`. `NaN` conveniently appears in many cases where a plot may have an issue, though not with $1/x$ as `1/0` is `Inf` and not `NaN`. (Unlike, say, `0/0` which is NaN.)
|
||||
There is a convention for most plotting programs that **if** the $y$ value for a point is `NaN` then no lines will connect to that point, `(x,NaN)`. `NaN` conveniently appears in many cases where a plot may have an issue, though not with $1/x$ as `1/0` is `Inf` and not `NaN`. (Unlike, say, `0/0` which is NaN.)
|
||||
|
||||
|
||||
Here is one way to plot $q(x) = 1/x$ over $[-1,1]$ taking advantage of this convention:
|
||||
@@ -457,7 +460,7 @@ plot(xs, ys)
|
||||
By using an odd number of points, we should have that $0.0$ is amongst the `xs`. The next to last line replaces the $y$ value that would be infinite with `NaN`.
|
||||
|
||||
|
||||
As a recommended alternative, we might modify the function so that if it is too large, the values are replaced by `NaN`. Here is one such function consuming a function and returning a modified function put to use to make this graph:
|
||||
The above is fussy. As a recommended alternative, we might modify the function so that if it is too large, the values are replaced by `NaN`. Here is one such function consuming a function and returning a modified function put to use to make this graph:
|
||||
|
||||
|
||||
```{julia}
|
||||
@@ -471,7 +474,7 @@ plot(rangeclamp(x -> 1/x), -1, 1)
|
||||
## Layers
|
||||
|
||||
|
||||
Graphing more than one function over the same viewing window is often desirable. Though this is easily done in `Plots` by specifying a vector of functions as the first argument to `plot` instead of a single function object, we instead focus on building the graph layer by layer.
|
||||
Graphing more than one function over the same viewing window is often desirable. Though this is easily done all at once in `Plots` by specifying a vector of functions as the first argument to `plot` instead of a single function object, we instead focus on building the graph layer by layer.^[The style of `Plots` is to combine multiple *series* to plot into one object and let `Plots` sort out which (every column is treated as a separate series). This can be very efficient from a programming perspective, but we leave it for power users. The use of layers, seems much easier to understand.]
|
||||
|
||||
|
||||
For example, to see that a polynomial and the cosine function are "close" near $0$, we can plot *both* $\cos(x)$ and the function $f(x) = 1 - x^2/2$ over $[-\pi/2,\pi/2]$:
|
||||
@@ -505,8 +508,8 @@ For another example, suppose we wish to plot the function $f(x)=x\cdot(x-1)$ ove
|
||||
|
||||
```{julia}
|
||||
#| hold: true
|
||||
f(x) = x*(x-1)
|
||||
plot(f, -1, 2, legend=false) # turn off legend
|
||||
f(x) = x * (x-1)
|
||||
plot(f, -1, 2; legend=false) # turn off legend
|
||||
plot!(zero)
|
||||
scatter!([0,1], [0,0])
|
||||
```
|
||||
@@ -514,43 +517,53 @@ scatter!([0,1], [0,0])
|
||||
The $3$ main functions used in these notes for adding layers are:
|
||||
|
||||
|
||||
* `plot!(f, a, b)` to add the graph of the function `f`; also `plot!(xs, ys)`
|
||||
* `scatter!(xs, ys)` to add points $(x_1, y_1), (x_2, y_2), \dots$.
|
||||
* `annotate!((x,y, label))` to add a label at $(x,y)$
|
||||
* `plot!(f, a, b)` to add the graph of the function `f`; also `plot!(xs, ys)`
|
||||
|
||||
* `scatter!(xs, ys)` to add points $(x_1, y_1), (x_2, y_2), \dots$.
|
||||
|
||||
* `annotate!((x,y, label))` to add a label at $(x,y)$
|
||||
|
||||
|
||||
:::{.callout-warning}
|
||||
## Warning
|
||||
## Trailing ! convention
|
||||
Julia has a convention to use functions named with a `!` suffix to indicate that they mutate some object. In this case, the object is the current graph, though it is implicit. Both `plot!`, `scatter!`, and `annotate!` (others too) do this by adding a layer.
|
||||
|
||||
:::
|
||||
|
||||
## Additional arguments
|
||||
|
||||
|
||||
The `Plots` package provides many arguments for adjusting a graphic, here we mention just a few of the [attributes](https://docs.juliaplots.org/latest/attributes/):
|
||||
The `Plots` package uses positional arguments for input data and keyword arguments for [attributes](https://docs.juliaplots.org/latest/attributes/).
|
||||
The `Plots` package provides many such arguments for adjusting a graphic, here we mention just a few:
|
||||
|
||||
|
||||
* `plot(..., title="main title", xlab="x axis label", ylab="y axis label")`: add title and label information to a graphic
|
||||
* `plot(..., color="green")`: this argument can be used to adjust the color of the drawn figure (color can be a string,`"green"`, or a symbol, `:green`, among other specifications)
|
||||
* `plot(..., linewidth=5)`: this argument can be used to adjust the width of drawn lines
|
||||
* `plot(..., xlims=(a,b), ylims=(c,d))`: either or both `xlims` and `ylims` can be used to control the viewing window
|
||||
* `plot(..., linestyle=:dash)`: will change the line style of the plotted lines to dashed lines. Also `:dot`, ...
|
||||
* `plot(..., aspect_ratio=:equal)`: will keep $x$ and $y$ axis on same scale so that squares look square.
|
||||
* `plot(..., legend=false)`: by default, different layers will be indicated with a legend, this will turn off this feature
|
||||
* `plot(..., label="a label")` the `label` attribute will show up when a legend is present. Using an empty string, `""`, will suppress add the layer to the legend.
|
||||
* `plot(...; title="main title", xlab="x axis label", ylab="y axis label")`: add title and label information to a graphic
|
||||
* `plot(...; color="green")`: this argument can be used to adjust the color of the drawn figure (color can be a string,`"green"`, or a symbol, `:green`, among other specifications)
|
||||
* `plot(...; linewidth=5)`: this argument can be used to adjust the width of drawn lines
|
||||
* `plot(...; linestyle=:dash)`: will change the line style of the plotted lines to dashed lines. Also `:dot`, ...
|
||||
* `plot(...; label="a label")` the `label` attribute will show up when a legend is present. Using an empty string, `""`, will suppress add the layer to the legend.
|
||||
* `plot(...; legend=false)`: by default, different layers will be indicated with a legend, this will turn off this feature
|
||||
* `plot(...; xlims=(a,b), ylims=(c,d))`: either or both `xlims` and `ylims` can be used to control the viewing window
|
||||
* `plot(...; xticks=[xs..], yticks=[ys...]: either or both `xticks` and `yticks` can be used to specify where the tick marks are to be drawn
|
||||
* `plot(...; aspect_ratio=:equal)`: will keep $x$ and $y$ axis on same scale so that squares look square.
|
||||
* `plot(...; framestyle=:origin)`: The default `framestyle` places $x$-$y$ guides on the edges; this specification places them on the $x-y$ plane.
|
||||
|
||||
|
||||
For plotting points with `scatter`, or `scatter!` the markers can be adjusted via
|
||||
|
||||
|
||||
* `scatter(..., markersize=5)`: increase marker size
|
||||
* `scatter(..., marker=:square)`: change the marker (uses a symbol, not a string to specify)
|
||||
* `scatter(...; markersize=5)`: increase marker size
|
||||
* `scatter(...; marker=:square)`: change the marker (uses a symbol, not a string to specify)
|
||||
|
||||
|
||||
Of course, zero, one, or more of these can be used on any given call to `plot`, `plot!`, `scatter`, or `scatter!`.
|
||||
|
||||
There are also several *shorthands* in `Plots` that allows several related attributes to be specified to a single argument that is disambiguated using the type of the value. (Eg. `line=(5, 0.25, "blue")` will specify the line have width `5`, color `blue`, and alpha-transparency `0.25`.)
|
||||
### Shorthands
|
||||
|
||||
There are also several *shorthands* in `Plots` that allows several related attributes to be specified to a single argument that is disambiguated using the type of the value. A few used herein are:
|
||||
|
||||
* `line`. For example, `line=(5, 0.25, "blue")` will specify `linewidth=5` (integer), `linecolor="blue"` (string or symbol), `linealpha=0.25` (floating point)
|
||||
* `marker`. For example `marker=(:star, 5)` will specify `markerstyle=:star` (symbol) and `markersize=5` (integer).
|
||||
* `fill`. For example `fill=(:blue, 0.25)` will specify `fillcolor=:blue` (string or symbol) and `fillalpha=0.25` (floating point).
|
||||
|
||||
#### Example: Bresenham's algorithm
|
||||
|
||||
@@ -607,9 +620,9 @@ p = plot(f, x₀, x₁; legend=false, aspect_ratio=:equal,
|
||||
col = RGBA(.64,.64,.64, 0.25)
|
||||
for xy ∈ xs
|
||||
x, y = xy
|
||||
scatter!([x], [y]; markersize=5)
|
||||
scatter!([x+1], [y - 1/2], markersize=5, markershape=:star7)
|
||||
plot!(Shape(x .+ [0, 1, 1, 0], y .+ [0, 0, -1, -1]), color=col)
|
||||
scatter!([x], [y]; marker=(5,))
|
||||
scatter!([x+1], [y - 1/2]; marker=(5,:star))
|
||||
plot!(Shape(x .+ [0, 1, 1, 0], y .+ [0, 0, -1, -1]); color=col)
|
||||
end
|
||||
p
|
||||
```
|
||||
@@ -618,14 +631,46 @@ We see a number of additional arguments used: different marker sizes and shapes
|
||||
|
||||
Of course, generalizations for positive slope and slope with magnitude greater than $1$ are needed. As well, this basic algorithm could be optimized, especially if it is part of a lower-level drawing primitive. But this illustrates the considerations involved.
|
||||
|
||||
## Points, lines, polygons
|
||||
|
||||
Two basic objects to graph are points and lines. Add to these polygons.
|
||||
|
||||
A point in two-dimensional space has two coordinates, often denoted by $(x,y)$. In `Julia`, the same notation produces a `tuple`. Using square brackets, as in `[x,y]`, produces a vector. Vectors are are more commonly used in these notes, as we have seen there are algebraic operations defined for them. However, tuples have other advantages and are how `Plots` designates a point.
|
||||
|
||||
The plot command `plot(xs, ys)` plots the points $(x_1,y_1), \dots, (x_n, y_n)$ and then connects adjacent points with with lines. The command `scatter(xs, ys)` just plots the points.
|
||||
|
||||
However, the points might be more naturally specified as coordinate pairs. If tuples are used to pair them off, then `Plots` will plot a vector of tuples as a sequence of points through `plot([(x1,y1), (x2, y2), ..., (xn, yn)])`:
|
||||
|
||||
```{julia}
|
||||
pts = [( 1, 0), ( 1/4, 1/4), (0, 1), (-1/4, 1/4),
|
||||
(-1, 0), (-1/4, -1/4), (0, -1), ( 1/4, -1/4)]
|
||||
scatter(pts; legend=false)
|
||||
```
|
||||
|
||||
A line segment simply connects two points. While these can be specified as vectors of $x$ and $y$ values, again it may be more convenient to use coordinate pairs to specify the points. Continuing the above, we can connect adjacent points with line segments:
|
||||
|
||||
```{julia}
|
||||
plot!(pts; line=(:gray, 0.5, :dash))
|
||||
```
|
||||
|
||||
This uses the shorthand notation of `Plots` to specify `linecolor=:gray, linealpha=0.5, linestyle=:dash`. To plot just a line segment, just specifying two points suffices.
|
||||
|
||||
The four-pointed star is not closed off, as there isn't a value from the last point to the first point. A polygon closes itself off. The `Shape` function can take a vector of points or a pair of `xs` and `ys` to specify a polygon. When these are plotted, the arguments to `fill` describe the interior of the polygon, the arguments to `line` the boundary:
|
||||
|
||||
|
||||
```{julia}
|
||||
plot(Shape(pts); fill=(:gray, 0.25), line=(:black, 2), legend=false)
|
||||
scatter!(pts)
|
||||
```
|
||||
|
||||
|
||||
## Graphs of parametric equations
|
||||
|
||||
|
||||
If we have two functions $f(x)$ and $g(x)$ there are a few ways to investigate their joint behavior. As just mentioned, we can graph both $f$ and $g$ over the same interval using layers. Such a graph allows an easy comparison of the shape of the two functions and can be useful in solving $f(x) = g(x)$. For the latter, the graph of $h(x) = f(x) - g(x)$ is also of value: solutions to $f(x)=g(x)$ appear as crossing points on the graphs of `f` and `g`, whereas they appear as zeros (crossings of the $x$-axis) when `h` is plotted.
|
||||
If we have two functions $f(x)$ and $g(x)$ there are a few ways to investigate their joint behavior. As mentioned, we can graph both $f$ and $g$ over the same interval using layers. Such a graph allows an easy comparison of the shape of the two functions and can be useful in solving $f(x) = g(x)$, as the $x$ solutions are where the two curves intersect.
|
||||
|
||||
|
||||
A different graph can be made to compare the two functions side-by-side. This is a parametric plot. Rather than plotting points $(x,f(x))$ and $(x,g(x))$ with two separate graphs, the graph consists of points $(f(x), g(x))$. We illustrate with some examples below:
|
||||
A different graph can be made to compare the two functions side-by-side. This is a parametric plot. Rather than plotting points $(x,f(x))$ and $(x,g(x))$ with two separate graphs, the graph consists of points $(f(x), g(x))$ over a range of $x$ values. We illustrate with some examples below:
|
||||
|
||||
|
||||
##### Example
|
||||
@@ -643,7 +688,7 @@ plot(f.(ts), g.(ts), aspect_ratio=:equal) # make equal axes
|
||||
Any point $(a,b)$ on this graph is represented by $(\cos(t), \sin(t))$ for some value of $t$, and in fact multiple values of $t$, since $t + 2k\pi$ will produce the same $(a,b)$ value as $t$ will.
|
||||
|
||||
|
||||
Making the parametric plot is similar to creating a plot using lower level commands. There a sequence of values is generated to approximate the $x$ values in the graph (`xs`), a set of commands to create the corresponding function values (e.g., `f.(xs)`), and some instruction on how to represent the values, in this case with lines connecting the points (the default for `plot` for two sets of numbers).
|
||||
Making the parametric plot is similar to creating a plot using lower level commands. There a sequence of values is generated to approximate the $x$ values in the graph (`xs`), a set of commands to create the corresponding function values (e.g., `f.(xs)`), and some instruction on how to represent the values, in this case with lines connecting the points (the default for `plot` for two vectors of numbers).
|
||||
|
||||
|
||||
In this next plot, the angle values are chosen to be the familiar ones, so the mechanics of the graph can be emphasized. Only the upper half is plotted:
|
||||
@@ -653,7 +698,7 @@ In this next plot, the angle values are chosen to be the familiar ones, so the m
|
||||
#| hold: true
|
||||
#| echo: false
|
||||
θs =[0, PI/6, PI/4, PI/3, PI/2, 2PI/3, 3PI/4,5PI/6, PI]
|
||||
DataFrame(θ=θs, x=cos.(θs), y=sin.(θs))
|
||||
latexify(DataFrame(θ=θs, x=cos.(θs), y=sin.(θs)))
|
||||
```
|
||||
|
||||
```{julia}
|
||||
@@ -718,7 +763,7 @@ This graph is *nearly* a straight line. At the point $(0,0)=(f(0), g(0))$, we se
|
||||
##### Example: Etch A Sketch
|
||||
|
||||
|
||||
[Etch A sketch](http://en.wikipedia.org/wiki/Etch_A_Sketch) is a drawing toy where two knobs control the motion of a pointer, one knob controlling the $x$ motion, the other the $y$ motion. The trace of the movement of the pointer is recorded until the display is cleared by shaking. Shake to clear is now a motion incorporated by some smart-phone apps.
|
||||
[Etch A Sketch](http://en.wikipedia.org/wiki/Etch_A_Sketch) is a drawing toy where two knobs control the motion of a pointer, one knob controlling the $x$ motion, the other the $y$ motion. The trace of the movement of the pointer is recorded until the display is cleared by shaking. Shake to clear is now a motion incorporated by some smart-phone apps.
|
||||
|
||||
|
||||
Playing with the toy makes a few things become clear:
|
||||
@@ -757,38 +802,6 @@ plot(f, g, 0, max((R-r)/r, r/(R-r))*2pi)
|
||||
In the above, one can fix $R=1$. Then different values for `r` and `rho` will produce different graphs. These graphs will be periodic if $(R-r)/r$ is a rational. (Nothing about these equations requires $\rho < r$.)
|
||||
|
||||
|
||||
## Points, lines, polygons
|
||||
|
||||
Two basic objects to graph are points and lines.
|
||||
|
||||
A point in two-dimensional space has two coordinates, often denoted by $(x,y)$. In `Julia`, the same notation produces a `tuple`. Using square brackets, as in `[x,y]`, produces a vector. Vectors are usually used, as we have seen there are algebraic operations defined for them. However, tuples have other advantages and are how `Plots` designates a point.
|
||||
|
||||
The plot command `plot(xs, ys)` plots the points $(x_1,y_1), \dots, (x_n, y_n)$ and then connects adjacent points with with lines. The command `scatter(xs, ys)` just plots the points.
|
||||
|
||||
However, the points might be more naturally specified as coordinate pairs. If tuples are used to pair them off, then `Plots` will plot a vector of tuples as a sequence of points:
|
||||
|
||||
```{julia}
|
||||
pts = [(1, 0), (1/4, 1/4), (0, 1), (-1/4, 1/4), (-1, 0),
|
||||
(-1/4, -1/4), (0, -1), (1/4, -1/4)]
|
||||
scatter(pts; legend=false)
|
||||
```
|
||||
|
||||
A line segment simply connects two points. While these can be specified as vectors of $x$ and $y$ values, again it may be more convenient to use coordinate pairs to specify the points. Continuing the above, we can connect adjacent points with line segments:
|
||||
|
||||
```{julia}
|
||||
plot!(pts; line=(:gray, 0.5, :dash))
|
||||
```
|
||||
|
||||
This uses the shorthand notation of `Plots` to specify `linecolor=:gray, linealpha=0.5, linestyle=:dash`. To plot just a line segment, just specifying two points suffices.
|
||||
|
||||
The four-pointed star is not closed off, as there isn't a value from the last point to the first point. A polygon closes itself off. The `Shape` function can take a vector of points or a pair of `xs` and `ys` to specify a polygon. When these are plotted, the arguments to `fill` describe the interior of the polygon, the arguments to `line` the boundary:
|
||||
|
||||
|
||||
```{julia}
|
||||
plot(Shape(pts); fill=(:gray, 0.25), line=(:black, 2), legend=false)
|
||||
scatter!(pts)
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Questions
|
||||
|
||||
Reference in New Issue
Block a user