--- jupytext: text_representation: extension: .md format_name: myst kernelspec: display_name: Python 3 language: python name: python3 --- (matplotlib)= ```{raw} html
QuantEcon
``` # {index}`Matplotlib ` ```{index} single: Python; Matplotlib ``` ```{contents} Contents :depth: 2 ``` ## Overview We've already generated quite a few figures in these lectures using [Matplotlib](http://matplotlib.org/). Matplotlib is an outstanding graphics library, designed for scientific computing, with * high-quality 2D and 3D plots * output in all the usual formats (PDF, PNG, etc.) * LaTeX integration * fine-grained control over all aspects of presentation * animation, etc. ### Matplotlib's Split Personality Matplotlib is unusual in that it offers two different interfaces to plotting. One is a simple MATLAB-style API (Application Programming Interface) that was written to help MATLAB refugees find a ready home. The other is a more "Pythonic" object-oriented API. For reasons described below, we recommend that you use the second API. But first, let's discuss the difference. ## The APIs ```{index} single: Matplotlib; Simple API ``` ### The MATLAB-style API Here's the kind of easy example you might find in introductory treatments ```{code-cell} ipython import matplotlib.pyplot as plt %matplotlib inline import numpy as np x = np.linspace(0, 10, 200) y = np.sin(x) plt.plot(x, y, 'b-', linewidth=2) plt.show() ``` This is simple and convenient, but also somewhat limited and un-Pythonic. For example, in the function calls, a lot of objects get created and passed around without making themselves known to the programmer. Python programmers tend to prefer a more explicit style of programming (run `import this` in a code block and look at the second line). This leads us to the alternative, object-oriented Matplotlib API. ### The Object-Oriented API Here's the code corresponding to the preceding figure using the object-oriented API ```{code-cell} python3 fig, ax = plt.subplots() ax.plot(x, y, 'b-', linewidth=2) plt.show() ``` Here the call `fig, ax = plt.subplots()` returns a pair, where * `fig` is a `Figure` instance---like a blank canvas. * `ax` is an `AxesSubplot` instance---think of a frame for plotting in. The `plot()` function is actually a method of `ax`. While there's a bit more typing, the more explicit use of objects gives us better control. This will become more clear as we go along. ### Tweaks Here we've changed the line to red and added a legend ```{code-cell} python3 fig, ax = plt.subplots() ax.plot(x, y, 'r-', linewidth=2, label='sine function', alpha=0.6) ax.legend() plt.show() ``` We've also used `alpha` to make the line slightly transparent---which makes it look smoother. The location of the legend can be changed by replacing `ax.legend()` with `ax.legend(loc='upper center')`. ```{code-cell} python3 fig, ax = plt.subplots() ax.plot(x, y, 'r-', linewidth=2, label='sine function', alpha=0.6) ax.legend(loc='upper center') plt.show() ``` If everything is properly configured, then adding LaTeX is trivial ```{code-cell} python3 fig, ax = plt.subplots() ax.plot(x, y, 'r-', linewidth=2, label='$y=\sin(x)$', alpha=0.6) ax.legend(loc='upper center') plt.show() ``` Controlling the ticks, adding titles and so on is also straightforward ```{code-cell} python3 fig, ax = plt.subplots() ax.plot(x, y, 'r-', linewidth=2, label='$y=\sin(x)$', alpha=0.6) ax.legend(loc='upper center') ax.set_yticks([-1, 0, 1]) ax.set_title('Test plot') plt.show() ``` ## More Features Matplotlib has a huge array of functions and features, which you can discover over time as you have need for them. We mention just a few. ### Multiple Plots on One Axis ```{index} single: Matplotlib; Multiple Plots on One Axis ``` It's straightforward to generate multiple plots on the same axes. Here's an example that randomly generates three normal densities and adds a label with their mean ```{code-cell} python3 from scipy.stats import norm from random import uniform fig, ax = plt.subplots() x = np.linspace(-4, 4, 150) for i in range(3): m, s = uniform(-1, 1), uniform(1, 2) y = norm.pdf(x, loc=m, scale=s) current_label = f'$\mu = {m:.2}$' ax.plot(x, y, linewidth=2, alpha=0.6, label=current_label) ax.legend() plt.show() ``` ### Multiple Subplots ```{index} single: Matplotlib; Subplots ``` Sometimes we want multiple subplots in one figure. Here's an example that generates 6 histograms ```{code-cell} python3 num_rows, num_cols = 3, 2 fig, axes = plt.subplots(num_rows, num_cols, figsize=(10, 12)) for i in range(num_rows): for j in range(num_cols): m, s = uniform(-1, 1), uniform(1, 2) x = norm.rvs(loc=m, scale=s, size=100) axes[i, j].hist(x, alpha=0.6, bins=20) t = f'$\mu = {m:.2}, \quad \sigma = {s:.2}$' axes[i, j].set(title=t, xticks=[-4, 0, 4], yticks=[]) plt.show() ``` ### 3D Plots ```{index} single: Matplotlib; 3D Plots ``` Matplotlib does a nice job of 3D plots --- here is one example ```{code-cell} python3 from mpl_toolkits.mplot3d.axes3d import Axes3D from matplotlib import cm def f(x, y): return np.cos(x**2 + y**2) / (1 + x**2 + y**2) xgrid = np.linspace(-3, 3, 50) ygrid = xgrid x, y = np.meshgrid(xgrid, ygrid) fig = plt.figure(figsize=(8, 6)) ax = fig.add_subplot(111, projection='3d') ax.plot_surface(x, y, f(x, y), rstride=2, cstride=2, cmap=cm.jet, alpha=0.7, linewidth=0.25) ax.set_zlim(-0.5, 1.0) plt.show() ``` ### A Customizing Function Perhaps you will find a set of customizations that you regularly use. Suppose we usually prefer our axes to go through the origin, and to have a grid. Here's a nice example from [Matthew Doty](https://github.com/xcthulhu) of how the object-oriented API can be used to build a custom `subplots` function that implements these changes. Read carefully through the code and see if you can follow what's going on ```{code-cell} python3 def subplots(): "Custom subplots with axes through the origin" fig, ax = plt.subplots() # Set the axes through the origin for spine in ['left', 'bottom']: ax.spines[spine].set_position('zero') for spine in ['right', 'top']: ax.spines[spine].set_color('none') ax.grid() return fig, ax fig, ax = subplots() # Call the local version, not plt.subplots() x = np.linspace(-2, 10, 200) y = np.sin(x) ax.plot(x, y, 'r-', linewidth=2, label='sine function', alpha=0.6) ax.legend(loc='lower right') plt.show() ``` The custom `subplots` function 1. calls the standard `plt.subplots` function internally to generate the `fig, ax` pair, 1. makes the desired customizations to `ax`, and 1. passes the `fig, ax` pair back to the calling code. ## Further Reading * The [Matplotlib gallery](http://matplotlib.org/gallery.html) provides many examples. * A nice [Matplotlib tutorial](http://scipy-lectures.org/intro/matplotlib/index.html) by Nicolas Rougier, Mike Muller and Gael Varoquaux. * [mpltools](http://tonysyu.github.io/mpltools/index.html) allows easy switching between plot styles. * [Seaborn](https://github.com/mwaskom/seaborn) facilitates common statistics plots in Matplotlib. ## Exercises ### Exercise 1 Plot the function $$ f(x) = \cos(\pi \theta x) \exp(-x) $$ over the interval $[0, 5]$ for each $\theta$ in `np.linspace(0, 2, 10)`. Place all the curves in the same figure. The output should look like this ```{figure} /_static/lecture_specific/matplotlib/matplotlib_ex1.png ``` ## Solutions ### Exercise 1 Here's one solution ```{code-cell} ipython3 def f(x, θ): return np.cos(np.pi * θ * x ) * np.exp(- x) θ_vals = np.linspace(0, 2, 10) x = np.linspace(0, 5, 200) fig, ax = plt.subplots() for θ in θ_vals: ax.plot(x, f(x, θ)) plt.show() ```