{ "cells": [ { "cell_type": "markdown", "id": "492d2fec-0538-4e9c-a30f-aa431615d05a", "metadata": {}, "source": [ "###
San Jose State University
Department of Applied Data Science

**DATA 200
Computational Programming for Data Analytics**

Spring 2024
Instructor: Ron Mak
" ] }, { "cell_type": "markdown", "id": "84463419-7c1d-44fb-a65f-357cad40ab86", "metadata": {}, "source": [ "# Anatomy of a `matplotlib` figure\n", "#### See [Anatomy of a figure](https://matplotlib.org/stable/gallery/showcase/anatomy.html)\n", "#### Module `matplotlib` contains Python's the most popular graphing library. It emulates the graphing capabilities of MATLAB, a commercial package of scientific and mathematical software.\n", "#### A `matplotlib` plot is an object that contains multiple objects (the object-oriented *has-a* relationship) nested in a tree structure. \n", "#### The top visualization container is a `Figure` object. It can contain multiple `Axes` objects, which are individual plots inside the top-level container.\n", "- #### `Figure`: The outermost container which serves as the canvas upon which we can draw multiple plots.\n", "- #### `Axes`: An actual plot or subplot, depending on whether we draw a single plot or multiple plots. An `Axes` object itself contains multiple subobjects, including ones that control axes, tick marks, legends, title, textboxes, grid, and other objects.\n", "#### All the objects are customizable." ] }, { "cell_type": "code", "execution_count": null, "id": "6e846eaf-8a33-435a-a909-b157f0a42ff0", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "from matplotlib.patches import Circle\n", "from matplotlib.patheffects import withStroke\n", "from matplotlib.ticker import AutoMinorLocator, MultipleLocator\n", "\n", "%matplotlib inline" ] }, { "cell_type": "code", "execution_count": null, "id": "0a90555c-702b-4f17-8f68-7a76484ebf29", "metadata": {}, "outputs": [], "source": [ "CIRCLE_COLOR = [0, 20/256, 82/256]" ] }, { "cell_type": "markdown", "id": "56ce5f73-3244-450d-80f1-2452898de66e", "metadata": {}, "source": [ "## Generate random data for plotting" ] }, { "cell_type": "code", "execution_count": null, "id": "27832b0f-0528-4075-ab88-86ea473d46ec", "metadata": {}, "outputs": [], "source": [ "def generate_data():\n", " \"\"\"\n", " For evenly distributed X values, generate \n", " three sets of random values Y1, Y2, and Y3.\n", " Return X, Y1, Y2, and Y3 as numpy arrays.\n", " \"\"\"\n", " np.random.seed(19680801)\n", "\n", " X = np.linspace(0.5, 3.5, 100)\n", " Y1 = 3 + np.cos(X)\n", " Y2 = 1 + np.cos(1 + X/0.75)/2\n", " Y3 = np.random.uniform(Y1, Y2, len(X))\n", " \n", " return X, Y1, Y2, Y3" ] }, { "cell_type": "code", "execution_count": null, "id": "3afec4f9-e156-402e-bf1e-bb069510b466", "metadata": {}, "outputs": [], "source": [ "X, Y1, Y2, Y3 = generate_data()\n", "\n", "print(f'{X = }')\n", "print(f'{Y1 = }')\n", "print(f'{Y2 = }')\n", "print(f'{Y3 = }')" ] }, { "cell_type": "markdown", "id": "bf8bcc68-a584-46f4-9f6f-e4a6e9a351ab", "metadata": {}, "source": [ "## Make the figure" ] }, { "cell_type": "code", "execution_count": null, "id": "218415e9-9a35-445d-af50-56fbd9b94294", "metadata": {}, "outputs": [], "source": [ "def make_figure(X, Y1, Y2, Y3):\n", " \"\"\"\n", " Generate a figure that plots random values Y1, Y2, and Y3\n", " over X. Plot Y1 and Y2 as blue and orange line plots,\n", " respectively, and Y3 as a purple scatter plot.\n", " Return the figure and the axis.\n", " \"\"\"\n", " fig = plt.figure(figsize=(7.5, 7.5)) # width and height in inches\n", " ax = fig.add_axes([0.2, 0.17, 0.68, 0.7]) # [left, bottom, width, height]\n", "\n", " # Configure the x axis.\n", " ax.set_xlim(0, 4)\n", " ax.xaxis.set_major_locator(MultipleLocator(1.000))\n", " ax.xaxis.set_minor_locator(AutoMinorLocator(4))\n", " ax.xaxis.set_minor_formatter(\"{x:.2f}\")\n", "\n", " # Configure the y axis.\n", " ax.set_ylim(0, 4)\n", " ax.yaxis.set_major_locator(MultipleLocator(1.000))\n", " ax.yaxis.set_minor_locator(AutoMinorLocator(4))\n", "\n", " # Configure the tick labels.\n", " ax.tick_params(which='major', width=1.0, length=10, labelsize=14)\n", " ax.tick_params(which='minor', width=1.0, length=5, labelsize=10,\n", " labelcolor='0.25')\n", "\n", " # Configure the background grid.\n", " ax.grid(linestyle=\"--\", linewidth=0.5, color='.25', zorder=-10)\n", "\n", " # Draw the line plots of the Y1 and Y2 values.\n", " ax.plot(X, Y1, c='C0', lw=2.5, label='Y1 values', zorder=10)\n", " ax.plot(X, Y2, c='C1', lw=2.5, label='Y2 values')\n", " \n", " # Draw the scatter plot of the Y3 values.\n", " ax.plot(X[::3], Y3[::3], linewidth=0, markersize=9,\n", " marker='s', markerfacecolor='none', markeredgecolor='C4',\n", " markeredgewidth=2.5, label='Y3 values')\n", "\n", " # Write the figure title and x and y axis labels.\n", " ax.set_title('Anatomy of a figure', fontsize=20, \n", " verticalalignment='bottom')\n", " ax.set_xlabel('x Axis label', fontsize=14)\n", " ax.set_ylabel('y Axis label', fontsize=14)\n", " \n", " # Place the legend.\n", " ax.legend(loc='upper right', fontsize=14)\n", " \n", " return fig, ax" ] }, { "cell_type": "markdown", "id": "9c0c6562-69d1-45c7-8c44-1ca97705f1c4", "metadata": {}, "source": [ "## Draw the figure" ] }, { "cell_type": "code", "execution_count": null, "id": "dac2603d-14f7-432f-8f17-c2f237a0096c", "metadata": {}, "outputs": [], "source": [ "X, Y1, Y2, Y3 = generate_data()\n", "make_figure(X, Y1, Y2, Y3)\n", "\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "55c875f8-1775-49ae-a239-55fae0d4e765", "metadata": {}, "source": [ "## Make an annotation" ] }, { "cell_type": "code", "execution_count": null, "id": "8c66291b-4da6-4eb9-99e0-97dfc6e3181e", "metadata": {}, "outputs": [], "source": [ "def make_annotatation(ax, x, y, text, code):\n", " \"\"\"\n", " Use the Axes object ax to place an annotation at\n", " the x, y location. Each annotation consists of a \n", " circle marker around the component with a text label\n", " and the code reference to the component.\n", " \"\"\"\n", " # Draw the circle marker.\n", " c = Circle((x, y), radius=0.15, clip_on=False, \n", " zorder=10, linewidth=2.5,\n", " edgecolor=CIRCLE_COLOR + [0.6], facecolor='none',\n", " path_effects=[\n", " withStroke(linewidth=7, foreground='white')\n", " ])\n", " ax.add_artist(c)\n", "\n", " # Use path_effects as a background for the texts.\n", " # Draw the path_effects and the colored text separately \n", " # so that the path_effects cannot clip other texts.\n", " for path_effects in [[withStroke(linewidth=7, foreground='white')], \n", " []]:\n", " color = 'white' if path_effects else CIRCLE_COLOR\n", " ax.text(x, y-0.2, text, zorder=100,\n", " ha='center', va='top', weight='bold', color=color,\n", " style='italic', fontfamily='Courier New',\n", " path_effects=path_effects)\n", "\n", " color = 'white' if path_effects else 'black'\n", " ax.text(x, y-0.33, code, zorder=100,\n", " ha='center', va='top', weight='normal', color=color,\n", " fontfamily='monospace', fontsize='medium',\n", " path_effects=path_effects)" ] }, { "cell_type": "markdown", "id": "cb84df6e-13e7-41ae-b68c-25f68e4e70aa", "metadata": {}, "source": [ "## Annotate the figure" ] }, { "cell_type": "code", "execution_count": null, "id": "6ea11c0a-0d3a-43b0-a098-bf916dbfad83", "metadata": {}, "outputs": [], "source": [ "def annotate_figure(ax):\n", " \"\"\"\n", " Use Axes object ax to place the annotations on the figure.\n", " \"\"\"\n", " make_annotatation(ax, 3.5, -0.13, \n", " 'Minor tick label', 'ax.xaxis.set_minor_formatter')\n", " make_annotatation(ax, -0.03, 1.0, \n", " 'Major tick', 'ax.yaxis.set_major_locator')\n", " make_annotatation(ax, 0.00, 3.75,\n", " 'Minor tick', 'ax.yaxis.set_minor_locator')\n", " make_annotatation(ax, -0.15, 3.00, \n", " 'Major tick label', 'ax.yaxis.set_major_formatter')\n", " \n", " make_annotatation(ax, 1.68, -0.39, 'xlabel', 'ax.set_xlabel')\n", " make_annotatation(ax, -0.38, 1.67, 'ylabel', 'ax.set_ylabel')\n", " make_annotatation(ax, 1.52, 4.15, 'Title', 'ax.set_title')\n", " make_annotatation(ax, 1.75, 2.80, 'Line', 'ax.plot')\n", " make_annotatation(ax, 2.25, 1.54, 'Markers', 'ax.scatter')\n", " make_annotatation(ax, 3.00, 3.00, 'Grid', 'ax.grid')\n", " make_annotatation(ax, 3.60, 3.58, 'Legend', 'ax.legend')\n", " make_annotatation(ax, 2.5, 0.55, 'Axes', 'fig.subplots')\n", " make_annotatation(ax, 4, 4.5, 'Figure', 'plt.figure')\n", " make_annotatation(ax, 0.65, 0.01, 'x Axis', 'ax.xaxis')\n", " make_annotatation(ax, 0, 0.36, 'y Axis', 'ax.yaxis')\n", " make_annotatation(ax, 4.0, 0.7, 'Spine', 'ax.spines')" ] }, { "cell_type": "markdown", "id": "25f705f2-e7aa-4770-8915-0e8beedc863d", "metadata": {}, "source": [ "## Draw the figure with annotations and a frame" ] }, { "cell_type": "code", "execution_count": null, "id": "a85fd933-7165-4898-94d4-e36f3b6437ee", "metadata": {}, "outputs": [], "source": [ "fig, ax = make_figure(X, Y1, Y2, Y3)\n", "annotate_figure(ax)\n", "\n", "# Frame around figure.\n", "fig.patch.set(linewidth=4, edgecolor='0.5')\n", "\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": null, "id": "20eb2df5-7f60-4ecd-b261-67a0d4d98a9a", "metadata": {}, "outputs": [], "source": [ "# Free the memory used by the figure.\n", "plt.close()" ] }, { "cell_type": "code", "execution_count": null, "id": "95fc9d90-4d7e-49aa-b245-ca153c444f7c", "metadata": {}, "outputs": [], "source": [ "# Additional material (c) 2024 by Ronald Mak" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.5" } }, "nbformat": 4, "nbformat_minor": 5 }