{ "cells": [ { "cell_type": "markdown", "id": "995987d3", "metadata": {}, "source": [ "\n", "\n", "
\n", " \n", " \"QuantEcon\"\n", " \n", "
" ] }, { "cell_type": "markdown", "id": "4a6d526b", "metadata": {}, "source": [ "# Matplotlib\n", "\n", "\n", "" ] }, { "cell_type": "markdown", "id": "a7269a6f", "metadata": {}, "source": [ "## Contents\n", "\n", "- [Matplotlib](#Matplotlib) \n", " - [Overview](#Overview) \n", " - [The APIs](#The-APIs) \n", " - [More Features](#More-Features) \n", " - [Further Reading](#Further-Reading) \n", " - [Exercises](#Exercises) " ] }, { "cell_type": "markdown", "id": "487597ca", "metadata": {}, "source": [ "## Overview\n", "\n", "We’ve already generated quite a few figures in these lectures using [Matplotlib](http://matplotlib.org/).\n", "\n", "Matplotlib is an outstanding graphics library, designed for scientific computing, with\n", "\n", "- high-quality 2D and 3D plots \n", "- output in all the usual formats (PDF, PNG, etc.) \n", "- LaTeX integration \n", "- fine-grained control over all aspects of presentation \n", "- animation, etc. " ] }, { "cell_type": "markdown", "id": "67105118", "metadata": {}, "source": [ "### Matplotlib’s Split Personality\n", "\n", "Matplotlib is unusual in that it offers two different interfaces to plotting.\n", "\n", "One is a simple MATLAB-style API (Application Programming Interface) that was written to help MATLAB refugees find a ready home.\n", "\n", "The other is a more “Pythonic” object-oriented API.\n", "\n", "For reasons described below, we recommend that you use the second API.\n", "\n", "But first, let’s discuss the difference." ] }, { "cell_type": "markdown", "id": "fe69df5c", "metadata": {}, "source": [ "## The APIs\n", "\n", "\n", "" ] }, { "cell_type": "markdown", "id": "fa0684d9", "metadata": {}, "source": [ "### The MATLAB-style API\n", "\n", "Here’s the kind of easy example you might find in introductory treatments" ] }, { "cell_type": "code", "execution_count": null, "id": "96a8f4b0", "metadata": { "hide-output": false }, "outputs": [], "source": [ "%matplotlib inline\n", "import matplotlib.pyplot as plt\n", "plt.rcParams[\"figure.figsize\"] = (10, 6) #set default figure size\n", "import numpy as np\n", "\n", "x = np.linspace(0, 10, 200)\n", "y = np.sin(x)\n", "\n", "plt.plot(x, y, 'b-', linewidth=2)\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "ca401c5c", "metadata": {}, "source": [ "This is simple and convenient, but also somewhat limited and un-Pythonic.\n", "\n", "For example, in the function calls, a lot of objects get created and passed around without making themselves known to the programmer.\n", "\n", "Python programmers tend to prefer a more explicit style of programming (run `import this` in a code block and look at the second line).\n", "\n", "This leads us to the alternative, object-oriented Matplotlib API." ] }, { "cell_type": "markdown", "id": "ac84dcd8", "metadata": {}, "source": [ "### The Object-Oriented API\n", "\n", "Here’s the code corresponding to the preceding figure using the object-oriented API" ] }, { "cell_type": "code", "execution_count": null, "id": "cc3c0fec", "metadata": { "hide-output": false }, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "ax.plot(x, y, 'b-', linewidth=2)\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "02fc876f", "metadata": {}, "source": [ "Here the call `fig, ax = plt.subplots()` returns a pair, where\n", "\n", "- `fig` is a `Figure` instance—like a blank canvas. \n", "- `ax` is an `AxesSubplot` instance—think of a frame for plotting in. \n", "\n", "\n", "The `plot()` function is actually a method of `ax`.\n", "\n", "While there’s a bit more typing, the more explicit use of objects gives us better control.\n", "\n", "This will become more clear as we go along." ] }, { "cell_type": "markdown", "id": "91164d2d", "metadata": {}, "source": [ "### Tweaks\n", "\n", "Here we’ve changed the line to red and added a legend" ] }, { "cell_type": "code", "execution_count": null, "id": "7d8206f0", "metadata": { "hide-output": false }, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "ax.plot(x, y, 'r-', linewidth=2, label='sine function', alpha=0.6)\n", "ax.legend()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "b012cf3f", "metadata": {}, "source": [ "We’ve also used `alpha` to make the line slightly transparent—which makes it look smoother.\n", "\n", "The location of the legend can be changed by replacing `ax.legend()` with `ax.legend(loc='upper center')`." ] }, { "cell_type": "code", "execution_count": null, "id": "aa6edd6c", "metadata": { "hide-output": false }, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "ax.plot(x, y, 'r-', linewidth=2, label='sine function', alpha=0.6)\n", "ax.legend(loc='upper center')\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "b9a90f09", "metadata": {}, "source": [ "If everything is properly configured, then adding LaTeX is trivial" ] }, { "cell_type": "code", "execution_count": null, "id": "229136d7", "metadata": { "hide-output": false }, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "ax.plot(x, y, 'r-', linewidth=2, label='$y=\\sin(x)$', alpha=0.6)\n", "ax.legend(loc='upper center')\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "c29e1f5b", "metadata": {}, "source": [ "Controlling the ticks, adding titles and so on is also straightforward" ] }, { "cell_type": "code", "execution_count": null, "id": "b35bf0c6", "metadata": { "hide-output": false }, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "ax.plot(x, y, 'r-', linewidth=2, label='$y=\\sin(x)$', alpha=0.6)\n", "ax.legend(loc='upper center')\n", "ax.set_yticks([-1, 0, 1])\n", "ax.set_title('Test plot')\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "e1e32079", "metadata": {}, "source": [ "## More Features\n", "\n", "Matplotlib has a huge array of functions and features, which you can discover\n", "over time as you have need for them.\n", "\n", "We mention just a few." ] }, { "cell_type": "markdown", "id": "feb24655", "metadata": {}, "source": [ "### Multiple Plots on One Axis\n", "\n", "\n", "\n", "It’s straightforward to generate multiple plots on the same axes.\n", "\n", "Here’s an example that randomly generates three normal densities and adds a label with their mean" ] }, { "cell_type": "code", "execution_count": null, "id": "bc1c05d4", "metadata": { "hide-output": false }, "outputs": [], "source": [ "from scipy.stats import norm\n", "from random import uniform\n", "\n", "fig, ax = plt.subplots()\n", "x = np.linspace(-4, 4, 150)\n", "for i in range(3):\n", " m, s = uniform(-1, 1), uniform(1, 2)\n", " y = norm.pdf(x, loc=m, scale=s)\n", " current_label = f'$\\mu = {m:.2}$'\n", " ax.plot(x, y, linewidth=2, alpha=0.6, label=current_label)\n", "ax.legend()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "1ffde44b", "metadata": {}, "source": [ "### Multiple Subplots\n", "\n", "\n", "\n", "Sometimes we want multiple subplots in one figure.\n", "\n", "Here’s an example that generates 6 histograms" ] }, { "cell_type": "code", "execution_count": null, "id": "5567afc8", "metadata": { "hide-output": false }, "outputs": [], "source": [ "num_rows, num_cols = 3, 2\n", "fig, axes = plt.subplots(num_rows, num_cols, figsize=(10, 12))\n", "for i in range(num_rows):\n", " for j in range(num_cols):\n", " m, s = uniform(-1, 1), uniform(1, 2)\n", " x = norm.rvs(loc=m, scale=s, size=100)\n", " axes[i, j].hist(x, alpha=0.6, bins=20)\n", " t = f'$\\mu = {m:.2}, \\quad \\sigma = {s:.2}$'\n", " axes[i, j].set(title=t, xticks=[-4, 0, 4], yticks=[])\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "b7a4f9b6", "metadata": {}, "source": [ "### 3D Plots\n", "\n", "\n", "\n", "Matplotlib does a nice job of 3D plots — here is one example" ] }, { "cell_type": "code", "execution_count": null, "id": "37ebd4e2", "metadata": { "hide-output": false }, "outputs": [], "source": [ "from mpl_toolkits.mplot3d.axes3d import Axes3D\n", "from matplotlib import cm\n", "\n", "\n", "def f(x, y):\n", " return np.cos(x**2 + y**2) / (1 + x**2 + y**2)\n", "\n", "xgrid = np.linspace(-3, 3, 50)\n", "ygrid = xgrid\n", "x, y = np.meshgrid(xgrid, ygrid)\n", "\n", "fig = plt.figure(figsize=(10, 6))\n", "ax = fig.add_subplot(111, projection='3d')\n", "ax.plot_surface(x,\n", " y,\n", " f(x, y),\n", " rstride=2, cstride=2,\n", " cmap=cm.jet,\n", " alpha=0.7,\n", " linewidth=0.25)\n", "ax.set_zlim(-0.5, 1.0)\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "20f29872", "metadata": {}, "source": [ "### A Customizing Function\n", "\n", "Perhaps you will find a set of customizations that you regularly use.\n", "\n", "Suppose we usually prefer our axes to go through the origin, and to have a grid.\n", "\n", "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.\n", "\n", "Read carefully through the code and see if you can follow what’s going on" ] }, { "cell_type": "code", "execution_count": null, "id": "2d8409de", "metadata": { "hide-output": false }, "outputs": [], "source": [ "def subplots():\n", " \"Custom subplots with axes through the origin\"\n", " fig, ax = plt.subplots()\n", "\n", " # Set the axes through the origin\n", " for spine in ['left', 'bottom']:\n", " ax.spines[spine].set_position('zero')\n", " for spine in ['right', 'top']:\n", " ax.spines[spine].set_color('none')\n", "\n", " ax.grid()\n", " return fig, ax\n", "\n", "\n", "fig, ax = subplots() # Call the local version, not plt.subplots()\n", "x = np.linspace(-2, 10, 200)\n", "y = np.sin(x)\n", "ax.plot(x, y, 'r-', linewidth=2, label='sine function', alpha=0.6)\n", "ax.legend(loc='lower right')\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "af3c3b74", "metadata": {}, "source": [ "The custom `subplots` function\n", "\n", "1. calls the standard `plt.subplots` function internally to generate the `fig, ax` pair, \n", "1. makes the desired customizations to `ax`, and \n", "1. passes the `fig, ax` pair back to the calling code. " ] }, { "cell_type": "markdown", "id": "df018533", "metadata": {}, "source": [ "### Style Sheets\n", "\n", "Another useful feature in Matplotlib is [style sheets](https://matplotlib.org/stable/gallery/style_sheets/style_sheets_reference.html).\n", "\n", "We can use style sheets to create plots with uniform styles.\n", "\n", "We can find a list of available styles by printing the attribute `plt.style.available`" ] }, { "cell_type": "code", "execution_count": null, "id": "03751515", "metadata": { "hide-output": false }, "outputs": [], "source": [ "print(plt.style.available)" ] }, { "cell_type": "markdown", "id": "c87edf3f", "metadata": {}, "source": [ "We can now use the `plt.style.use()` method to set the style sheet.\n", "\n", "Let’s write a function that takes the name of a style sheet and draws different plots with the style" ] }, { "cell_type": "code", "execution_count": null, "id": "da32de94", "metadata": { "hide-output": false }, "outputs": [], "source": [ "def draw_graphs(style='default'):\n", "\n", " # Setting a style sheet\n", " plt.style.use(style)\n", "\n", " fig, axes = plt.subplots(nrows=1, ncols=4, figsize=(10, 3))\n", " x = np.linspace(-13, 13, 150)\n", "\n", " # Set seed values to replicate results of random draws\n", " np.random.seed(9)\n", "\n", " for i in range(3):\n", "\n", " # Draw mean and standard deviation from uniform distributions\n", " m, s = np.random.uniform(-8, 8), np.random.uniform(2, 2.5)\n", "\n", " # Generate a normal density plot\n", " y = norm.pdf(x, loc=m, scale=s)\n", " axes[0].plot(x, y, linewidth=3, alpha=0.7)\n", "\n", " # Create a scatter plot with random X and Y values \n", " # from normal distributions\n", " rnormX = norm.rvs(loc=m, scale=s, size=150)\n", " rnormY = norm.rvs(loc=m, scale=s, size=150)\n", " axes[1].plot(rnormX, rnormY, ls='none', marker='o', alpha=0.7)\n", "\n", " # Create a histogram with random X values\n", " axes[2].hist(rnormX, alpha=0.7)\n", "\n", " # and a line graph with random Y values\n", " axes[3].plot(x, rnormY, linewidth=2, alpha=0.7)\n", "\n", " style_name = style.split('-')[0]\n", " plt.suptitle(f'Style: {style_name}', fontsize=13)\n", " plt.show()" ] }, { "cell_type": "markdown", "id": "1daeae39", "metadata": {}, "source": [ "Let’s see what some of the styles look like.\n", "\n", "First, we draw graphs with the style sheet `seaborn`" ] }, { "cell_type": "code", "execution_count": null, "id": "f5f219cf", "metadata": { "hide-output": false }, "outputs": [], "source": [ "draw_graphs(style='seaborn-v0_8')" ] }, { "cell_type": "markdown", "id": "a6ea0477", "metadata": {}, "source": [ "We can use `grayscale` to remove colors in plots" ] }, { "cell_type": "code", "execution_count": null, "id": "a37cb30f", "metadata": { "hide-output": false }, "outputs": [], "source": [ "draw_graphs(style='grayscale')" ] }, { "cell_type": "markdown", "id": "e250cc77", "metadata": {}, "source": [ "Here is what `ggplot` looks like" ] }, { "cell_type": "code", "execution_count": null, "id": "5bc3f24c", "metadata": { "hide-output": false }, "outputs": [], "source": [ "draw_graphs(style='ggplot')" ] }, { "cell_type": "markdown", "id": "c04d13f2", "metadata": {}, "source": [ "We can also use the style `dark_background`" ] }, { "cell_type": "code", "execution_count": null, "id": "67a2eb4c", "metadata": { "hide-output": false }, "outputs": [], "source": [ "draw_graphs(style='dark_background')" ] }, { "cell_type": "markdown", "id": "ca5729f6", "metadata": {}, "source": [ "You can use the function to experiment with other styles in the list.\n", "\n", "If you are interested, you can even create your own style sheets.\n", "\n", "Parameters for your style sheets are stored in a dictionary-like variable `plt.rcParams`" ] }, { "cell_type": "code", "execution_count": null, "id": "0581abf0", "metadata": { "hide-output": false }, "outputs": [], "source": [ "print(plt.rcParams.keys())" ] }, { "cell_type": "markdown", "id": "bf5674a6", "metadata": {}, "source": [ "There are many parameters you could set for your style sheets.\n", "\n", "Set parameters for your style sheet by:\n", "\n", "1. creating your own [`matplotlibrc` file](https://matplotlib.org/stable/users/explain/customizing.html), or \n", "1. updating values stored in the dictionary-like variable `plt.rcParams` \n", "\n", "\n", "Let’s change the style of our overlaid density lines using the second method" ] }, { "cell_type": "code", "execution_count": null, "id": "5ca251c7", "metadata": { "hide-output": false }, "outputs": [], "source": [ "from cycler import cycler\n", "\n", "# set to the default style sheet\n", "plt.style.use('default')\n", "\n", "# You can update single values using keys:\n", "\n", "# Set the font style to italic\n", "plt.rcParams['font.style'] = 'italic'\n", "\n", "# Update linewidth\n", "plt.rcParams['lines.linewidth'] = 2\n", "\n", "\n", "# You can also update many values at once using the update() method:\n", "\n", "parameters = {\n", "\n", " # Change default figure size\n", " 'figure.figsize': (5, 4),\n", "\n", " # Add horizontal grid lines\n", " 'axes.grid': True,\n", " 'axes.grid.axis': 'y',\n", "\n", " # Update colors for density lines\n", " 'axes.prop_cycle': cycler('color', \n", " ['dimgray', 'slategrey', 'darkgray'])\n", "}\n", "\n", "plt.rcParams.update(parameters)" ] }, { "cell_type": "markdown", "id": "556fd92f", "metadata": {}, "source": [ ">**Note**\n", ">\n", ">These settings are `global`.\n", "\n", "Any plot generated after changing parameters in `.rcParams` will be affected by the setting." ] }, { "cell_type": "code", "execution_count": null, "id": "00308159", "metadata": { "hide-output": false }, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", "x = np.linspace(-4, 4, 150)\n", "for i in range(3):\n", " m, s = uniform(-1, 1), uniform(1, 2)\n", " y = norm.pdf(x, loc=m, scale=s)\n", " current_label = f'$\\mu = {m:.2}$'\n", " ax.plot(x, y, linewidth=2, alpha=0.6, label=current_label)\n", "ax.legend()\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "30482820", "metadata": {}, "source": [ "Apply the `default` style sheet again to change your style back to default" ] }, { "cell_type": "code", "execution_count": null, "id": "34e892b0", "metadata": { "hide-output": false }, "outputs": [], "source": [ "plt.style.use('default')\n", "\n", "# Reset default figure size\n", "plt.rcParams['figure.figsize'] = (10, 6)" ] }, { "cell_type": "markdown", "id": "61fe2ff3", "metadata": {}, "source": [ "Here are [more examples](https://www.datafantic.com/the-magic-of-matplotlib-stylesheets/) on how to change these parameters." ] }, { "cell_type": "markdown", "id": "63309b24", "metadata": {}, "source": [ "## Further Reading\n", "\n", "- The [Matplotlib gallery](http://matplotlib.org/gallery.html) provides many examples. \n", "- A nice [Matplotlib tutorial](http://scipy-lectures.org/intro/matplotlib/index.html) by Nicolas Rougier, Mike Muller and Gael Varoquaux. \n", "- [mpltools](http://tonysyu.github.io/mpltools/index.html) allows easy\n", " switching between plot styles. \n", "- [Seaborn](https://github.com/mwaskom/seaborn) facilitates common statistics plots in Matplotlib. " ] }, { "cell_type": "markdown", "id": "fedf7266", "metadata": {}, "source": [ "## Exercises" ] }, { "cell_type": "markdown", "id": "18739372", "metadata": {}, "source": [ "## Exercise 12.1\n", "\n", "Plot the function\n", "\n", "$$\n", "f(x) = \\cos(\\pi \\theta x) \\exp(-x)\n", "$$\n", "\n", "over the interval $ [0, 5] $ for each $ \\theta $ in `np.linspace(0, 2, 10)`.\n", "\n", "Place all the curves in the same figure.\n", "\n", "The output should look like this\n", "\n", "![https://python-programming.quantecon.org/_static/lecture_specific/matplotlib/matplotlib_ex1.png](https://python-programming.quantecon.org/_static/lecture_specific/matplotlib/matplotlib_ex1.png)" ] }, { "cell_type": "markdown", "id": "800e8c4a", "metadata": {}, "source": [ "## Solution to[ Exercise 12.1](https://python-programming.quantecon.org/#mpl_ex1)\n", "\n", "Here’s one solution" ] }, { "cell_type": "code", "execution_count": null, "id": "d52652e6", "metadata": { "hide-output": false }, "outputs": [], "source": [ "def f(x, θ):\n", " return np.cos(np.pi * θ * x ) * np.exp(- x)\n", "\n", "θ_vals = np.linspace(0, 2, 10)\n", "x = np.linspace(0, 5, 200)\n", "fig, ax = plt.subplots()\n", "\n", "for θ in θ_vals:\n", " ax.plot(x, f(x, θ))\n", "\n", "plt.show()" ] } ], "metadata": { "date": 1710455931.9540942, "filename": "matplotlib.md", "kernelspec": { "display_name": "Python", "language": "python3", "name": "python3" }, "title": "Matplotlib" }, "nbformat": 4, "nbformat_minor": 5 }