In the last post, I described how I use TikZ to create a graph that might otherwise be created freehand:
In this post, I will describe one method for using TikZ to to plot a function defined by a formula, such as
Plotting from a formula
Simple example
There are a number of ways to achieve this, and PGF actually includes the functionality to perform calculations in TeX. (For example, I have used the PGF pseudorandom number generator in graphics. Someone even made TikZ code to generate a random city skyline.) However, for efficiency sake, I prefer to have the calculations done outside TeX, in a more efficient computer language.
The trick is to use the gnuplot
table import format. You simply have to generate a text file with a list of coordinates in the following form:
-1.250000 11.779907
-1.240000 11.456050
-1.230000 11.138839
This file contains the coordinates
There are a number of ways to generate such a file. Since I like Python, I used the following (simple) Python script:
#! /usr/bin/env python3
import numpy as np
f = lambda x: (2*x**2 - x - 1)*np.exp(-x)
xvals = np.arange(-1.25, 10.25, 0.01)
with open("plot1.table", "wt") as outfile:
for x in xvals:
outfile.write("{:f} {:f}\n".format(x, f(x)))
Now, this curve can be imported into your TikZ illustration using \draw
[. . .] plot
; in this case:
\draw[thick, blue, <->] plot[smooth] file {plot1.table};
Here is a complete example (with coordinate axes and everything):
\documentclass[border=0.25cm]{standalone}
\usepackage{tikz}
\usepackage{fouriernc}
\begin{document}
\begin{tikzpicture}[yscale=0.5]
\draw[gray] (-1.25, -1.25) grid (10.25, 12.25);
% x-axis
\draw[thick, black, ->] (-1.25, 0) -- (10.25, 0)
node[anchor=south west] {$x$};
% x-axis tick marks
\foreach \x in {-1, 10}
\draw[thick] (\x, 0.2) -- (\x, -0.2)
node[anchor=north] {\(\x\)};
% y-axis
\draw[thick, black, ->] (0, -1.25) -- (0, 12.25)
node[anchor=south west] {$y$};
% y-axis tick marks
\foreach \y in {-1, 12}
\draw[thick] (-0.1, \y) -- (0.1, \y)
node[anchor=west] {\(\y\)};
% graph of function
\draw[thick, blue, <->] plot[smooth] file {plot1.table};
\end{tikzpicture}
\end{document}
And here is the output:
In my Python script above, I carefully chose the set of inputs np.arange(-1.25, 10.25, 0.01)
, i.e., every real number of the form
Example involving asymptotes
For example, suppose I want to plot
Factoring the numerator and denominator, we have:
As you can see, we will have vertical asymptotes at
To handle the asymptotes, we plot the function
, , and
(Actually, for each of the above intervals
Here is the Python script:
#! /usr/bin/env python3
import numpy as np
r = lambda x: (2*x - 3)*(3*x + 12)/((x + 2)*(3*x - 6))
xvals = [
np.arange(-15.5, -2, 0.01),
np.arange(-2.01, 2, 0.01),
np.arange(2.01, 15.6, 0.01)
]
for idx, interval in enumerate(xvals):
with open("plot2_{}.table".format(idx), "wt") as outfile:
for x in interval:
if abs(r(x)) <= 16:
outfile.write("{:f} {:f}\n".format(x, r(x)))
Here is the corresponding LaTeX code:
\documentclass[border=0.25cm]{standalone}
\usepackage{tikz}
\usepackage{fouriernc}
\begin{document}
\begin{tikzpicture}[scale=0.5] % Scale keeps the graphic small
\draw[gray] (-15.5, -15.5) grid (15.5, 15.5);
% x-axis
\draw[thick, black, ->] (-15.5, 0) -- (15.5, 0)
node[anchor=south west] {$x$};
% x-axis tick marks
\foreach \x in {-15, -10, -5, 5, 10, 15}
\draw[thick] (\x, 0.5) -- (\x, -0.5)
node[anchor=north] {\(\x\)};
% y-axis
\draw[thick, black, ->] (0, -15.5) -- (0, 15.5)
node[anchor=south west] {$y$};
% y-axis tick marks
\foreach \y in {-15, -10, -5, 5, 10, 15}
\draw[thick] (-0.5, \y) -- (0.5, \y)
node[anchor=west] {\(\y\)};
% graph of function
\draw[thick, blue, <->] plot[smooth] file {plot2_0.table};
\draw[thick, blue, <->] plot[smooth] file {plot2_1.table};
\draw[thick, blue, <->] plot[smooth] file {plot2_2.table};
% hole at (-1, 5)
\draw[thick, blue, fill=white] (-1, 5) circle (2.5mm);
% vertical asymptotes
\draw[thick, dashed, red] (-2, -15.5) -- (-2, 15.5);
\draw[thick, dashed, red] (2, -15.5) -- (2, 15.5);
% horizontal asymptote
\draw[thick, dashed, red] (-15.5, 2) -- (15.5, 2);
\end{tikzpicture}
\end{document}
And here is the result:
Plotting functions that go through certain points: two ways
Suppose I want to plot a function that go through certain points, say:
, , , and .
I want the function to be differentiable.
Method one: Bézier curves
Recall from the last post that a (cubic) Bézier curve is specified by four points:
- A start point (
in the diagram below) - Two control points (
and in the diagram below) - An end point (
in the diagram below)
The syntax is: \draw (
) .. controls (
) and (
) .. (
);
with start, control, and end points as described
Now, we can combine multiple Bézier curves into a single \draw
. For example, \draw (
) .. controls (
) and (
) .. (
) .. controls (
) and (
) .. (
);
will produce:
In general, this curve will not necessarily be differentiable. We can get differentiability by ensuring that
their connecting ends
Keeping these principles in mind, we can plot a function going through the desired points with:
\documentclass[border=0.25cm]{standalone}
\usepackage{tikz}
\usepackage{fouriernc}
\begin{document}
\begin{tikzpicture}[scale=0.5] % Scale keeps the graphic small
\draw[gray] (-5.25, 0) grid (5.25, 8.25);
% x-axis
\draw[thick, black, ->] (-5.25, 0) -- (5.25, 0)
node[anchor=south west] {$x$};
% x-axis tick marks
\foreach \x in {-5, 5}
\draw[thick] (\x, 0.25) -- (\x, -0.25)
node[anchor=north] {\(\x\)};
% y-axis
\draw[thick, black, ->] (0, 0) -- (0, 8.25)
node[anchor=south west] {$y$};
% y-axis tick marks
\foreach \y in {4, 8}
\draw[thick] (-0.25, \y) -- (0.25, \y)
node[anchor=west] {\(\y\)};
% graph of function
\draw[thick, blue, <->] (-5.25, -0.5)
.. controls (-4, 0) and (-5, 0) .. (-4, 2)
.. controls (-3.5, 3) and (-3, 6) .. (-1, 6)
.. controls (1, 6) and (1.75, 4) .. (2, 3)
.. controls (2.25, 2) and (3, 2) .. (4, 5)
.. controls (4.5, 6.5) and (4.5, 7) .. (5.25, 8);
\end{tikzpicture}
\end{document}
Here is the output:
Method two: Polynomial interpolation
SciPy's interpolation functionality can find a smooth function that passes through an arbitrary list of points. Here is an example of a Python script to produce such a function and create a table in the necessary format:
#! /usr/bin/env python3
import numpy as np
import scipy.interpolate
coords = [(-10, 8), (-4, 2), (-1, 6), (2, 3), (4, 5), (10, 1)]
# Since our goal is to plot from x = -5.25 to x = 5.25,
# we need to specify some points beyond that interval on both sides.
xcoords, ycoords = zip(*coords)
# This produces a seperate list of x-coordinates and y-coordinates
f = scipy.interpolate.interp1d(xcoords, ycoords, kind="cubic")
xvals = np.arange(-5.25, 5.25, 0.01)
yvals = f(xvals)
outcoords = zip(xvals, yvals)
# This produces a list of coordinate pairs
with open("plot3.table", "wt") as outfile:
for pair in outcoords:
outfile.write("{:f} {:f}\n".format(pair[0], pair[1]))
Note that if we had set coords = [(-4, 2), (-1, 6), (2, 3), (4, 5)]
, then the resulting function would not have had a defined value outside of the interval
To plot, we use the following tikzpicture
:
\documentclass[border=0.25cm]{standalone}
\usepackage{tikz}
\usepackage{fouriernc}
\begin{document}
\begin{tikzpicture}[scale=0.5] % Scale keeps the graphic small
\draw[gray] (-5.25, 0) grid (5.25, 8.25);
% x-axis
\draw[thick, black, ->] (-5.25, 0) -- (5.25, 0)
node[anchor=south west] {$x$};
% x-axis tick marks
\foreach \x in {-5, 5}
\draw[thick] (\x, 0.25) -- (\x, -0.25)
node[anchor=north] {\(\x\)};
% y-axis
\draw[thick, black, ->] (0, 0) -- (0, 8.25)
node[anchor=south west] {$y$};
% y-axis tick marks
\foreach \y in {4, 8}
\draw[thick] (-0.25, \y) -- (0.25, \y)
node[anchor=west] {\(\y\)};
% graph of function
\draw[thick, blue, <->] plot[smooth] file {plot3.table};
\end{tikzpicture}
\end{document}
Here is the result:
As we have seen, creating plots with TikZ allows you a deep amount of control over the final result, and can be especially powerful when combined with a programming language such as Python.