From 66308a9afe8fd9f9d236d4f739a634a01dd43d1b Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 3 Jun 2018 21:32:12 -0500 Subject: [PATCH 1/8] DOC: edits to Part 1 - changes to the backend discussion - formatting changes - add color mapping to scatter up front --- ...b-Part1-Figures_Subplots_and_layouts.ipynb | 158 +++++++----------- 1 file changed, 65 insertions(+), 93 deletions(-) diff --git a/AnatomyOfMatplotlib-Part1-Figures_Subplots_and_layouts.ipynb b/AnatomyOfMatplotlib-Part1-Figures_Subplots_and_layouts.ipynb index 94e0cc2..f06de9e 100644 --- a/AnatomyOfMatplotlib-Part1-Figures_Subplots_and_layouts.ipynb +++ b/AnatomyOfMatplotlib-Part1-Figures_Subplots_and_layouts.ipynb @@ -1,20 +1,5 @@ { "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": false, - "slideshow": { - "slide_type": "-" - } - }, - "outputs": [], - "source": [ - "# Let printing work the same in Python 2 and 3\n", - "from __future__ import print_function" - ] - }, { "cell_type": "markdown", "metadata": { @@ -25,7 +10,13 @@ "source": [ "# Matplotlib\n", "## Introduction\n", - "Matplotlib is a library for producing publication-quality figures. mpl (for short) was designed from the beginning to serve two purposes. First, allow for interactive, cross-platform control of figures and plots, and second, to make it very easy to produce static raster or vector graphics files without the need for any GUIs. Furthermore, mpl -- much like Python itself -- gives the developer complete control over the appearance of their plots, while still being very usable through a powerful defaults system." + "\n", + "Matplotlib is a library for producing publication-quality figures. mpl (for short) was designed from the beginning to serve two purposes: \n", + "\n", + " 1. allow for interactive, cross-platform control of figures and plots\n", + " 2. make it easy to produce static raster or vector graphics files without the need for any GUIs. \n", + " \n", + "Furthermore, mpl -- much like Python itself -- gives the developer complete control over the appearance of their plots, while still being very usable through a powerful defaults system." ] }, { @@ -33,17 +24,19 @@ "metadata": {}, "source": [ "## Online Documentation\n", - "The [matplotlib.org](http://matplotlib.org) project website is the primary online resource for the library's documentation. It contains [examples](http://matplotlib.org/examples/index.html), [FAQs](http://matplotlib.org/faq/index.html), [API documentation](http://matplotlib.org/api/index.html), and, most importantly, the [gallery](http://matplotlib.org/gallery.html).\n", + "The [matplotlib.org](http://matplotlib.org) project website is the primary online resource for the library's documentation. It contains the [example galleries](https://matplotlib.org/gallery/index.html), [FAQs](http://matplotlib.org/faq/index.html), [API documentation](http://matplotlib.org/api/index.html), and [tutorials](https://matplotlib.org/tutorials/index.html).\n", "\n", "## Gallery\n", - "Many users of Matplotlib are often faced with the question, \"I want to make a figure that has X with Y in the same figure, but it needs to look like Z\". Good luck getting an answer from a web search with that query! This is why the [gallery](http://matplotlib.org/gallery.html) is so useful, because it showcases the variety of ways one can make figures. Browse through the gallery, click on any figure that has pieces of what you want to see and the code that generated it. Soon enough, you will be like a chef, mixing and matching components to produce your masterpiece!\n", + "Many users of Matplotlib are often faced with the question, \"I want to make a figure that has X with Y in the same figure, but it needs to look like Z\". Good luck getting an answer from a web search with that query! This is why the [gallery](https://matplotlib.org/gallery/index.html) is so useful, because it showcases the variety of ways one can make figures. Browse through the gallery, click on any figure that has pieces of what you want to see and the code that generated it. Soon enough, you will be like a chef, mixing and matching components to produce your masterpiece!\n", "\n", "As always, if you have a new and interesting plot that demonstrates a feature of Matplotlib, feel free to submit a concise, well-commented version of the code for inclusion in the gallery.\n", "\n", - "## Mailing Lists and StackOverflow\n", - "When you are just simply stuck, and cannot figure out how to get something to work, or just need some hints on how to get started, you will find much of the community at the matplotlib-users [mailing list](https://mail.python.org/mailman/listinfo/matplotlib-users). This mailing list is an excellent resource of information with many friendly members who just love to help out newcomers. The number one rule to remember with this list is to be persistant. While many questions do get answered fairly quickly, some do fall through the cracks, or the one person who knows the answer isn't available. Therefore, try again with your questions rephrased, or with a plot showing your attempts so far. We love plots, so an image showing what is wrong often gets the quickest responses.\n", + "## Mailing Lists, StackOverflow, and gitter\n", + "When you are just simply stuck, and cannot figure out how to get something to work, or just need some hints on how to get started, you will find much of the community at the matplotlib-users [mailing list](https://mail.python.org/mailman/listinfo/matplotlib-users). This mailing list is an excellent resource of information with many friendly members who just love to help out newcomers. We love plots, so an image showing what is wrong often gets the quickest responses.\n", + "\n", + "Another community resource is [StackOverflow](http://stackoverflow.com/questions/tagged/matplotlib), so if you need to build up karma points, submit your questions here, and help others out too! \n", "\n", - "Another community resource is [StackOverflow](http://stackoverflow.com/questions/tagged/matplotlib), so if you need to build up karma points, submit your questions here, and help others out too! We are also on [Gitter](https://gitter.im/matplotlib/matplotlib).\n", + "We are also on [Gitter](https://gitter.im/matplotlib/matplotlib).\n", "\n", "## Github repository\n", "### Location\n", @@ -58,15 +51,16 @@ "metadata": {}, "source": [ "# Quick note on \"backends\" and Jupyter notebooks\n", - "Matplotlib has multiple backends. The backends allow mpl to be used on a variety of platforms with a variety of GUI toolkits (GTK, Qt, Wx, etc.), all of them written so that most of the time, you will not need to care which backend you are using. " + "\n", + "Matplotlib has multiple \"backends\" that handle converting Matplotlib's in-memory representation of your plot into the colorful output you can look at. This is either by writing files (ex png, svg, pdf) thaht ou can use an external tool to look at or by embedding into your GUI toolkit of choice (Qt, Tk, Wx, etc).\n", + "\n", + "To check what backend Matplotlib is currently using:" ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import matplotlib\n", @@ -78,32 +72,32 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Normally we wouldn't need to think about this too much, but IPython/Jupyter notebooks behave a touch differently than \"normal\" python.\n", - "\n", - "Inside of IPython, it's often easiest to use the Jupyter ``nbagg`` or ``notebook`` backend. This allows plots to be displayed and interacted with in the browser in a Jupyter notebook. Otherwise, figures will pop up in a separate GUI window.\n", - "\n", - "We can do this in two ways:\n", - "\n", - "1. The IPython ``%matplotlib backend_name`` \"magic\" command (or ``plt.ion()``, which behaves similarly)\n", - " - Figures will be shown automatically by IPython, even if you don't call ``plt.show()``.\n", - " \n", - "2. ``matplotlib.use(\"backend_name\")``\n", - " - Figures will only be shown when you call ``plt.show()``.\n", - "\n", - "Here, we'll use the second method for one simple reason: it allows our code to behave the same way regardless of whether we run it inside of an Jupyter notebook or from a python script at the command line. Feel free to use the ``%matplotlib`` magic command if you'd prefer.\n", + "If you are working interactively at an (I)python prompt, the GUI framework is not critical (mostly aesthetic) however when working in Jupyter we need to pick a backend that integrates with Jupyter (javascript) framework.\n", "\n", - "One final note: You will always need to do this before you ``import matplotlib.pyplot as plt``." + "To select the backend use ``matplotlib.use(\"backend_name\")``, in this case we want ``'nbagg'``\n" ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ - "matplotlib.use('nbagg')" + "matplotlib.use('nbagg')\n", + "print(matplotlib.get_backend())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "which must be done *before* you `import matplotlib.pyplot as plt`.\n", + "\n", + "You can also set the backend via a 'magic ``%matplotlib backend_name``. In addition to setting the backend, the magic also calls `plt.ion()`, which puts Matplotlib in 'interacitve mode' (the inverse is `plt.ioff()`). In 'interactive mode' figures are shown (injected into the web page in the notebook) as soon as they are created. Otherwise, figures are not shown until you explicitly call `plt.show()`.\n", + "\n", + "\n", + "In these tutorials we will mostly work in non-interactive mode for better control of when\n", + "figures are shown in the notebooks." ] }, { @@ -138,9 +132,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", @@ -159,12 +151,10 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ - "fig = plt.figure()" + "fig = plt.figure(facecolor=(1, 0, 0, .1)) # red background to see where the figure is" ] }, { @@ -179,9 +169,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.show()" @@ -193,6 +181,7 @@ "source": [ "Great, a blank figure! Not terribly useful yet.\n", "\n", + "\n", "However, while we're on the topic, you can control the size of the figure through the ``figsize`` argument, which expects a tuple of ``(width, height)`` in inches. \n", "\n", "A really useful utility function is [`figaspect`](http://matplotlib.org/api/figure_api.html?highlight=figaspect#matplotlib.figure.figaspect)" @@ -201,13 +190,11 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Twice as tall as it is wide:\n", - "fig = plt.figure(figsize=plt.figaspect(2.0))\n", + "fig = plt.figure(figsize=plt.figaspect(2.0), facecolor=(1, 0, 0, .1))\n", "plt.show()" ] }, @@ -227,9 +214,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig = plt.figure()\n", @@ -254,16 +239,15 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "ax.set_xlim([0.5, 4.5])\n", "ax.set_ylim([-2, 8])\n", - "ax.set_title('An Example Axes')\n", - "ax.set_ylabel('Y-Axis')\n", - "ax.set_xlabel('X-Axis')" + "ax.set_title('An Diferent Example Axes Tile')\n", + "ax.set_ylabel('Y-Axis (changed)')\n", + "ax.set_xlabel('X-Axis (changed)')\n", + "fig.canvas.draw_idle()" ] }, { @@ -295,15 +279,13 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig = plt.figure()\n", "ax = fig.add_subplot(111)\n", "ax.plot([1, 2, 3, 4], [10, 20, 25, 30], color='lightblue', linewidth=3)\n", - "ax.scatter([0.3, 3.8, 1.2, 2.5], [11, 25, 9, 26], color='darkgreen', marker='^')\n", + "ax.scatter([0.3, 3.8, 1.2, 2.5], [11, 25, 9, 26], c=[1 ,2, 3, 5], marker='^')\n", "ax.set_xlim(0.5, 4.5)\n", "plt.show()" ] @@ -320,13 +302,11 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.plot([1, 2, 3, 4], [10, 20, 25, 30], color='lightblue', linewidth=3)\n", - "plt.scatter([0.3, 3.8, 1.2, 2.5], [11, 25, 9, 26], color='darkgreen', marker='^')\n", + "plt.scatter([0.3, 3.8, 1.2, 2.5], [11, 25, 9, 26], c=[1, 2, 3, 5], marker='^')\n", "plt.xlim(0.5, 4.5)\n", "plt.show()" ] @@ -335,7 +315,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Much cleaner, and much clearer! So, why will most of my examples not follow the pyplot approach? Because [PEP20](http://www.python.org/dev/peps/pep-0020/) \"The Zen of Python\" says:\n", + "That is a bit terser and has fewer local varialbes, so, why will most of my examples not follow the pyplot approach? Because [PEP20](http://www.python.org/dev/peps/pep-0020/) \"The Zen of Python\" says:\n", "\n", "\"Explicit is better than implicit\"\n", "\n", @@ -358,9 +338,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig, axes = plt.subplots(nrows=2, ncols=2)\n", @@ -381,9 +359,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig, axes = plt.subplots(nrows=2, ncols=2)\n", @@ -441,9 +417,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "%load exercises/1.1-subplots_and_basic_plotting.py" @@ -452,9 +426,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", @@ -473,23 +445,23 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 2", + "display_name": "Python 3", "language": "python", - "name": "python2" + "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.12" + "pygments_lexer": "ipython3", + "version": "3.6.5" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 1 } From dba20c6357cf470b2e8c83601d18f0b65f9c12c1 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 3 Jun 2018 22:17:48 -0500 Subject: [PATCH 2/8] ENH: switch problem 2.2 away from classic style --- .../2.2-vmin_vmax_imshow_and_colorbars.py | 2 -- ...ise_2.2-vmin_vmax_imshow_and_colorbars.png | Bin 13142 -> 53155 bytes .../2.2-vmin_vmax_imshow_and_colorbars.py | 1 - 3 files changed, 3 deletions(-) diff --git a/exercises/2.2-vmin_vmax_imshow_and_colorbars.py b/exercises/2.2-vmin_vmax_imshow_and_colorbars.py index 0b504f2..4922fb3 100644 --- a/exercises/2.2-vmin_vmax_imshow_and_colorbars.py +++ b/exercises/2.2-vmin_vmax_imshow_and_colorbars.py @@ -2,8 +2,6 @@ import matplotlib.pyplot as plt np.random.seed(1) -plt.style.use('classic') - # Generate random data with different ranges... data1 = np.random.random((10, 10)) data2 = 2 * np.random.random((10, 10)) diff --git a/images/exercise_2.2-vmin_vmax_imshow_and_colorbars.png b/images/exercise_2.2-vmin_vmax_imshow_and_colorbars.png index 66aaa3e81c511ba8d34181bb792fea87b70df5a9..45db4d946dd07594e8cabf4d74ccf51a64230ee6 100644 GIT binary patch literal 53155 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�r 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 literal 13142 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 T+FLmM2gA-;oF$yOa_7GRGuw)! diff --git a/solutions/2.2-vmin_vmax_imshow_and_colorbars.py b/solutions/2.2-vmin_vmax_imshow_and_colorbars.py index da67e7b..2bc76a8 100644 --- a/solutions/2.2-vmin_vmax_imshow_and_colorbars.py +++ b/solutions/2.2-vmin_vmax_imshow_and_colorbars.py @@ -2,7 +2,6 @@ import matplotlib.pyplot as plt np.random.seed(1) -plt.style.use('classic') # Generate random data with different ranges... data1 = np.random.random((10, 10)) From dd049413394af0f2884e66ae015edea2c041028b Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 3 Jun 2018 22:36:46 -0500 Subject: [PATCH 3/8] DOC: minor edits do data kwarg discussion + code style + links --- ...tlib-Part2-Plotting_Methods_Overview.ipynb | 80 +++++++------------ 1 file changed, 28 insertions(+), 52 deletions(-) diff --git a/AnatomyOfMatplotlib-Part2-Plotting_Methods_Overview.ipynb b/AnatomyOfMatplotlib-Part2-Plotting_Methods_Overview.ipynb index b354ba0..6ab58e8 100644 --- a/AnatomyOfMatplotlib-Part2-Plotting_Methods_Overview.ipynb +++ b/AnatomyOfMatplotlib-Part2-Plotting_Methods_Overview.ipynb @@ -47,9 +47,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Let's get our standard imports out of the way\n", @@ -77,9 +75,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "np.random.seed(1)\n", @@ -111,9 +107,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", @@ -147,9 +141,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "np.random.seed(1)\n", @@ -171,9 +163,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "x = np.linspace(0, 10, 200)\n", @@ -197,7 +187,8 @@ "metadata": {}, "source": [ "## `data` keyword argument\n", - "When using specialized data structures such as Pandas DataFrame and XArray, the input data to be plotted are accessed like dictionary elements. This can get very repetitive and tedious as one types out a plotting command accessing those elements. So, the `data` keyword argument was added to many of the plotting functions in v1.5. With this feature, one can pass in a single dictionary-like object as `data`, and use the string key names in the place of the usual input data arguments.\n", + "\n", + "When using nested data structures such as h5py objects, Pandas DataFrames, or XArrays, the data can be accessed via `[]` like dictionary elements. This can get very repetitive and tedious as one types out a plotting command accessing those elements. So, the `data` keyword argument was added to almost all of the plotting functions in v1.5. With this feature, one can pass in a single dictionary-like object as `data`, and use the string key names in the place of the usual input data arguments.\n", "\n", "Let's revisit the above example:" ] @@ -205,9 +196,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "x = np.linspace(0, 10, 200)\n", @@ -242,9 +231,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "%load exercises/2.1-bar_and_fill_between.py" @@ -253,9 +240,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", @@ -316,9 +301,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "from matplotlib.cbook import get_sample_data\n", @@ -342,9 +325,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", @@ -377,22 +358,22 @@ " \n", "`vmin` and `vmax` are particularly useful. Quite often, you'll want the colors to be mapped to a set range of data values, which aren't the min/max of your input data. For example, you might want a symmetric ranges of values around 0.\n", "\n", - "As an example of that, let's use a divergent colormap with the data we showed earlier. We'll also use `interpolation=\"nearest\"` to \"turn off\" interpolation of the cells in the input dataset:" + "See the documentation for longer discussions of [colormaps](https://matplotlib.org/tutorials/colors/colormaps.html) and [norms](https://matplotlib.org/tutorials/colors/colormapnorms.html).\n", + "\n", + "As an example of that, let's use a divergent colormap with the data we showed earlier. " ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "from matplotlib.cbook import get_sample_data\n", "data = np.load(get_sample_data('axes_grid/bivariate_normal.npy'))\n", "\n", "fig, ax = plt.subplots()\n", - "im = ax.imshow(data, cmap='seismic', interpolation='nearest')\n", + "im = ax.imshow(data, cmap='seismic')\n", "fig.colorbar(im)\n", "plt.show()" ] @@ -407,13 +388,12 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots()\n", - "im = ax.imshow(data, cmap='seismic', interpolation='nearest',\n", + "im = ax.imshow(data, \n", + " cmap='seismic',\n", " vmin=-2, vmax=2)\n", "fig.colorbar(im)\n", "plt.show()" @@ -434,9 +414,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "%load exercises/2.2-vmin_vmax_imshow_and_colorbars.py" @@ -445,9 +423,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", @@ -472,23 +448,23 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 2", + "display_name": "Python 3", "language": "python", - "name": "python2" + "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.12" + "pygments_lexer": "ipython3", + "version": "3.6.5" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 1 } From 7ced77a0a6654a8c474f363813eb9c50a4da2d2e Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Sun, 3 Jun 2018 23:11:22 -0500 Subject: [PATCH 4/8] DOC: tweak comments about color --- AnatomyOfMatplotlib-Part3-HowToSpeakMPL.ipynb | 105 ++++++------------ 1 file changed, 36 insertions(+), 69 deletions(-) diff --git a/AnatomyOfMatplotlib-Part3-HowToSpeakMPL.ipynb b/AnatomyOfMatplotlib-Part3-HowToSpeakMPL.ipynb index 8afa98f..5c572bf 100644 --- a/AnatomyOfMatplotlib-Part3-HowToSpeakMPL.ipynb +++ b/AnatomyOfMatplotlib-Part3-HowToSpeakMPL.ipynb @@ -3,14 +3,12 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# Let printing work the same in Python 2 and 3\n", "from __future__ import print_function\n", - "# Turning on inline plots -- just for use in jupyter notebooks.\n", + "# Turning on notebook plots -- just for use in jupyter notebooks.\n", "import matplotlib\n", "matplotlib.use('nbagg')\n", "import numpy as np\n", @@ -45,23 +43,26 @@ "- k: black\n", "- w: white\n", "\n", - "Other colornames that are allowed are the HTML/CSS colornames such as \"burlywood\" and \"chartreuse\". See the [full list](http://www.w3schools.com/html/html_colornames.asp) of the 147 colornames. For the British speaking and poor spellers among us (I am not implying that British speakers are poor spellers!), we allow \"grey\" where-ever \"gray\" appears in that list of colornames. All of these colornames are case-insensitive.\n", + "Other colornames that are allowed are the HTML/CSS colornames such as \"burlywood\" and \"chartreuse\". See the [full list](http://www.w3schools.com/html/html_colornames.asp) of the 147 colornames. \n", + "\n", + "Matplotlib supports the [xkcd color names](https://xkcd.com/color/rgb/) prefxed by `'xkcd:'`.\n", + "\n", + "Matplotlib also understands `{'tab:blue', 'tab:orange', 'tab:green', 'tab:red', 'tab:purple', 'tab:brown', 'tab:pink', 'tab:gray', 'tab:olive', 'tab:cyan'}` which are the Tableau Colors from the ‘T10’ categorical palette (and the default color cycle);\n", "\n", "### Hex values\n", "Colors can also be specified by supplying a HTML/CSS hex string, such as `'#0000FF'` for blue. Support for an optional alpha channel was added for v2.0.\n", "\n", - "### 256 Shades of Gray\n", - "A gray level can be given instead of a color by passing a string representation of a number between 0 and 1, inclusive. `'0.0'` is black, while `'1.0'` is white. `'0.75'` would be a light shade of gray.\n", + "### Cycle references\n", + "With the advent of fancier color cycles coming from the many available styles, users needed a way to reference those colors in the style without explicitly knowing what they are. So, in v2.0, the ability to reference the first 10 iterations of the color cycle was added. Whereever one could specify a color, you can supply a 2 character string of 'C#'. So, 'C0' would be the first color, 'C1' would be the second, and so on and so forth up to 'C9'.\n", + "\n", "\n", "### RGB[A] tuples\n", - "You may come upon instances where the previous ways of specifying colors do not work. This can sometimes happen in some of the deeper, stranger levels of the library. When all else fails, the universal language of colors for matplotlib is the RGB[A] tuple. This is the \"Red\", \"Green\", \"Blue\", and sometimes \"Alpha\" tuple of floats in the range of [0, 1]. One means full saturation of that channel, so a red RGBA tuple would be `(1.0, 0.0, 0.0, 1.0)`, whereas a partly transparent green RGBA tuple would be `(0.0, 1.0, 0.0, 0.75)`. The documentation will usually specify whether it accepts RGB or RGBA tuples. Sometimes, a list of tuples would be required for multiple colors, and you can even supply a Nx3 or Nx4 numpy array in such cases.\n", + "You may come upon instances where the previous ways of specifying colors do not work. When all else fails, the universal language of colors for matplotlib is the RGB[A] tuple. This is the \"Red\", \"Green\", \"Blue\", and sometimes \"Alpha\" tuple of floats in the range of [0, 1]. One means full saturation of that channel, so a red RGBA tuple would be `(1.0, 0.0, 0.0, 1.0)`, whereas a partly transparent green RGBA tuple would be `(0.0, 1.0, 0.0, 0.75)`. The documentation will usually specify whether it accepts RGB or RGBA tuples. Sometimes, a list of tuples would be required for multiple colors, and you can even supply a Nx3 or Nx4 numpy array in such cases.\n", "\n", "In functions such as `plot()` and `scatter()`, while it may appear that they can take a color specification, what they really need is a \"format specification\", which includes color as part of the format. Unfortunately, such specifications are string only and so RGB[A] tuples are not supported for such arguments (but you can still pass an RGB[A] tuple for a \"color\" argument).\n", "\n", "Oftentimes there is a separate argument for \"alpha\" where-ever you can specify a color. The value for \"alpha\" will usually take precedence over the alpha value in the RGBA tuple. There is no easy way around this inconsistency.\n", - "\n", - "### Cycle references\n", - "With the advent of fancier color cycles coming from the many available styles, users needed a way to reference those colors in the style without explicitly knowing what they are. So, in v2.0, the ability to reference the first 10 iterations of the color cycle was added. Whereever one could specify a color, you can supply a 2 character string of 'C#'. So, 'C0' would be the first color, 'C1' would be the second, and so on and so forth up to 'C9'." + "\n" ] }, { @@ -75,20 +76,16 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ - "%load exercises/3.1-colors.py" + "%load exercises/3.1-colors.py\n" ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "t = np.arange(0.0, 5.0, 0.2)\n", @@ -119,9 +116,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "xs, ys = np.mgrid[:4, 9:0:-1]\n", @@ -151,9 +146,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "%load exercises/3.2-markers.py" @@ -162,9 +155,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "t = np.arange(0.0, 5.0, 0.2)\n", @@ -195,9 +186,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "t = np.arange(0.0, 5.0, 0.2)\n", @@ -215,9 +204,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig, ax = plt.subplots(1, 1)\n", @@ -236,9 +223,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "t = np.arange(0., 5., 0.2)\n", @@ -284,9 +269,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "%load exercises/3.3-properties.py" @@ -295,9 +278,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "t = np.arange(0.0, 5.0, 0.1)\n", @@ -323,9 +304,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "# %load http://matplotlib.org/mpl_examples/color/colormaps_reference.py\n", @@ -434,9 +413,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "fig, (ax1, ax2) = plt.subplots(1, 2)\n", @@ -457,9 +434,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "plt.scatter([1, 2, 3, 4], [4, 3, 2, 1])\n", @@ -505,9 +480,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import matplotlib as mpl\n", @@ -532,9 +505,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "mpl.rc('axes', prop_cycle=cycler('color', ['r', 'orange', 'c', 'y']) +\n", @@ -575,9 +546,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import matplotlib\n", @@ -594,9 +563,7 @@ { "cell_type": "code", "execution_count": null, - "metadata": { - "collapsed": false - }, + "metadata": {}, "outputs": [], "source": [ "import matplotlib as mpl\n", @@ -624,23 +591,23 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 2", + "display_name": "Python 3", "language": "python", - "name": "python2" + "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", - "version": 2 + "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", - "pygments_lexer": "ipython2", - "version": "2.7.12" + "pygments_lexer": "ipython3", + "version": "3.6.5" } }, "nbformat": 4, - "nbformat_minor": 0 + "nbformat_minor": 1 } From 206a25bd64a76e21238082c8115cca8d25d04c2a Mon Sep 17 00:00:00 2001 From: Benjamin Root Date: Wed, 13 Jun 2018 20:55:56 -0400 Subject: [PATCH 5/8] Adding notes on testing the tutorial install --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ee93428..9493e3f 100644 --- a/README.md +++ b/README.md @@ -26,4 +26,5 @@ git clone https://github.com/WeatherGod/AnatomyOfMatplotlib.git cd AnatomyOfMatplotlib jupyter notebook ``` +A browser window should appear and you can verify that everything works as expected by clicking on the `Test Install.ipynb` notebook. There, you will see a "code cell" that you can execute. Run it, and you should see a very simple line plot, indicating that all is well. From 7128e50033c9ca47d8506deb8f5e3a5cc31b6cf9 Mon Sep 17 00:00:00 2001 From: Benjamin Root Date: Wed, 13 Jun 2018 20:58:55 -0400 Subject: [PATCH 6/8] Update links in readme.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9493e3f..3cb647b 100644 --- a/README.md +++ b/README.md @@ -8,13 +8,13 @@ Please fork and contribute back improvements! Feel free to use this tutorial for conferences and other opportunities for training. The tutorial can be viewed on [nbviewer](http://nbviewer.jupyter.org): -* [Part 0: Introduction To NumPy](http://nbviewer.jupyter.org/github/WeatherGod/AnatomyOfMatplotlib/blob/master/AnatomyOfMatplotlib-Part0-Intro2NumPy.ipynb) -* [Part 1: Overview of Matplotlib](http://nbviewer.jupyter.org/github/WeatherGod/AnatomyOfMatplotlib/blob/master/AnatomyOfMatplotlib-Part1-Figures_Subplots_and_layouts.ipynb) -* [Part 2: Plotting Methods](http://nbviewer.jupyter.org/github/WeatherGod/AnatomyOfMatplotlib/blob/master/AnatomyOfMatplotlib-Part2-Plotting_Methods_Overview.ipynb) -* [Part 3: How To Speak MPL](http://nbviewer.jupyter.org/github/WeatherGod/AnatomyOfMatplotlib/blob/master/AnatomyOfMatplotlib-Part3-HowToSpeakMPL.ipynb) -* [Part 4: Limits, Legends, and Layouts](http://nbviewer.jupyter.org/github/WeatherGod/AnatomyOfMatplotlib/blob/master/AnatomyOfMatplotlib-Part4-Limits_Legends_and_Layouts.ipynb) -* [Part 5: Artists](http://nbviewer.jupyter.org/github/WeatherGod/AnatomyOfMatplotlib/blob/master/AnatomyOfMatplotlib-Part5-Artists.ipynb) -* [Part 6: mpl_toolkits](http://nbviewer.jupyter.org/github/WeatherGod/AnatomyOfMatplotlib/blob/master/AnatomyOfMatplotlib-Part6-mpl_toolkits.ipynb) +* [Part 0: Introduction To NumPy](http://nbviewer.jupyter.org/github/matplotlib/AnatomyOfMatplotlib/blob/master/AnatomyOfMatplotlib-Part0-Intro2NumPy.ipynb) +* [Part 1: Overview of Matplotlib](http://nbviewer.jupyter.org/github/matplotlib/AnatomyOfMatplotlib/blob/master/AnatomyOfMatplotlib-Part1-Figures_Subplots_and_layouts.ipynb) +* [Part 2: Plotting Methods](http://nbviewer.jupyter.org/github/matplotlib/AnatomyOfMatplotlib/blob/master/AnatomyOfMatplotlib-Part2-Plotting_Methods_Overview.ipynb) +* [Part 3: How To Speak MPL](http://nbviewer.jupyter.org/github/matplotlib/AnatomyOfMatplotlib/blob/master/AnatomyOfMatplotlib-Part3-HowToSpeakMPL.ipynb) +* [Part 4: Limits, Legends, and Layouts](http://nbviewer.jupyter.org/github/matplotlib/AnatomyOfMatplotlib/blob/master/AnatomyOfMatplotlib-Part4-Limits_Legends_and_Layouts.ipynb) +* [Part 5: Artists](http://nbviewer.jupyter.org/github/matplotlib/AnatomyOfMatplotlib/blob/master/AnatomyOfMatplotlib-Part5-Artists.ipynb) +* [Part 6: mpl_toolkits](http://nbviewer.jupyter.org/github/matplotlib/AnatomyOfMatplotlib/blob/master/AnatomyOfMatplotlib-Part6-mpl_toolkits.ipynb) # Installation All you need is matplotlib (v1.5 or greater) and jupyter installed. From 8c0fa6f65c50271300c317bb9fa9981bb5b20da5 Mon Sep 17 00:00:00 2001 From: Benjamin Root Date: Wed, 13 Jun 2018 21:01:29 -0400 Subject: [PATCH 7/8] Update README.md I swear, this is the last remnant of the WeatherGod repo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3cb647b..726412c 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ You can use your favorite Python package installer for this: ```bash conda install matplotlib jupyter -git clone https://github.com/WeatherGod/AnatomyOfMatplotlib.git +git clone https://github.com/matplotlib/AnatomyOfMatplotlib.git cd AnatomyOfMatplotlib jupyter notebook ``` From 5d4d1ee485572c650f30ac40c1929ccf74e2c87e Mon Sep 17 00:00:00 2001 From: Benjamin Root Date: Wed, 27 Jun 2018 21:06:17 -0400 Subject: [PATCH 8/8] Some copy-editing, and putting backa from __future__ that was taken out --- ...b-Part1-Figures_Subplots_and_layouts.ipynb | 28 +++++++++++++++---- AnatomyOfMatplotlib-Part3-HowToSpeakMPL.ipynb | 4 ++- exercises/1.1-subplots_and_basic_plotting.py | 2 -- solutions/1.1-subplots_and_basic_plotting.py | 2 -- 4 files changed, 25 insertions(+), 11 deletions(-) diff --git a/AnatomyOfMatplotlib-Part1-Figures_Subplots_and_layouts.ipynb b/AnatomyOfMatplotlib-Part1-Figures_Subplots_and_layouts.ipynb index f06de9e..efa6b8a 100644 --- a/AnatomyOfMatplotlib-Part1-Figures_Subplots_and_layouts.ipynb +++ b/AnatomyOfMatplotlib-Part1-Figures_Subplots_and_layouts.ipynb @@ -1,5 +1,20 @@ { "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": false, + "slideshow": { + "slide_type": "-" + } + }, + "outputs": [], + "source": [ + "# Let printing work the same in Python 2 and 3\n", + "from __future__ import print_function" + ] + }, { "cell_type": "markdown", "metadata": { @@ -52,7 +67,7 @@ "source": [ "# Quick note on \"backends\" and Jupyter notebooks\n", "\n", - "Matplotlib has multiple \"backends\" that handle converting Matplotlib's in-memory representation of your plot into the colorful output you can look at. This is either by writing files (ex png, svg, pdf) thaht ou can use an external tool to look at or by embedding into your GUI toolkit of choice (Qt, Tk, Wx, etc).\n", + "Matplotlib has multiple \"backends\" that handle converting Matplotlib's in-memory representation of your plot into the colorful output you can look at. This is done either by writing files (e.g., png, svg, pdf) that you can use an external tool to look at or by embedding into your GUI toolkit of choice (Qt, Tk, Wx, etc).\n", "\n", "To check what backend Matplotlib is currently using:" ] @@ -93,11 +108,12 @@ "source": [ "which must be done *before* you `import matplotlib.pyplot as plt`.\n", "\n", - "You can also set the backend via a 'magic ``%matplotlib backend_name``. In addition to setting the backend, the magic also calls `plt.ion()`, which puts Matplotlib in 'interacitve mode' (the inverse is `plt.ioff()`). In 'interactive mode' figures are shown (injected into the web page in the notebook) as soon as they are created. Otherwise, figures are not shown until you explicitly call `plt.show()`.\n", + "You can also set the backend via an 'ipython magic' ``%matplotlib backend_name``. In addition to setting the backend, the magic also calls `plt.ion()`, which puts Matplotlib in 'interacitve mode' (the inverse is `plt.ioff()`). In 'interactive mode' figures are shown (injected into the web page in the notebook) as soon as they are created. Otherwise, figures are not shown until you explicitly call `plt.show()`.\n", "\n", "\n", "In these tutorials we will mostly work in non-interactive mode for better control of when\n", - "figures are shown in the notebooks." + "figures are shown in the notebooks.\n", + "This also better mimics the behavior you can expect in regular python scripts.\n" ] }, { @@ -244,10 +260,10 @@ "source": [ "ax.set_xlim([0.5, 4.5])\n", "ax.set_ylim([-2, 8])\n", - "ax.set_title('An Diferent Example Axes Tile')\n", + "ax.set_title('A Different Example Axes Title')\n", "ax.set_ylabel('Y-Axis (changed)')\n", "ax.set_xlabel('X-Axis (changed)')\n", - "fig.canvas.draw_idle()" + "plt.show()" ] }, { @@ -285,7 +301,7 @@ "fig = plt.figure()\n", "ax = fig.add_subplot(111)\n", "ax.plot([1, 2, 3, 4], [10, 20, 25, 30], color='lightblue', linewidth=3)\n", - "ax.scatter([0.3, 3.8, 1.2, 2.5], [11, 25, 9, 26], c=[1 ,2, 3, 5], marker='^')\n", + "ax.scatter([0.3, 3.8, 1.2, 2.5], [11, 25, 9, 26], c=[1, 2, 3, 5], marker='^')\n", "ax.set_xlim(0.5, 4.5)\n", "plt.show()" ] diff --git a/AnatomyOfMatplotlib-Part3-HowToSpeakMPL.ipynb b/AnatomyOfMatplotlib-Part3-HowToSpeakMPL.ipynb index 5c572bf..5f7c0e7 100644 --- a/AnatomyOfMatplotlib-Part3-HowToSpeakMPL.ipynb +++ b/AnatomyOfMatplotlib-Part3-HowToSpeakMPL.ipynb @@ -52,10 +52,12 @@ "### Hex values\n", "Colors can also be specified by supplying a HTML/CSS hex string, such as `'#0000FF'` for blue. Support for an optional alpha channel was added for v2.0.\n", "\n", + "### 256 Shades of Gray\n", + "A gray level can be given instead of a color by passing a string representation of a number between 0 and 1, inclusive. `'0.0'` is black, while `'1.0'` is white. `'0.75'` would be a light shade of gray.\n", + "\n", "### Cycle references\n", "With the advent of fancier color cycles coming from the many available styles, users needed a way to reference those colors in the style without explicitly knowing what they are. So, in v2.0, the ability to reference the first 10 iterations of the color cycle was added. Whereever one could specify a color, you can supply a 2 character string of 'C#'. So, 'C0' would be the first color, 'C1' would be the second, and so on and so forth up to 'C9'.\n", "\n", - "\n", "### RGB[A] tuples\n", "You may come upon instances where the previous ways of specifying colors do not work. When all else fails, the universal language of colors for matplotlib is the RGB[A] tuple. This is the \"Red\", \"Green\", \"Blue\", and sometimes \"Alpha\" tuple of floats in the range of [0, 1]. One means full saturation of that channel, so a red RGBA tuple would be `(1.0, 0.0, 0.0, 1.0)`, whereas a partly transparent green RGBA tuple would be `(0.0, 1.0, 0.0, 0.75)`. The documentation will usually specify whether it accepts RGB or RGBA tuples. Sometimes, a list of tuples would be required for multiple colors, and you can even supply a Nx3 or Nx4 numpy array in such cases.\n", "\n", diff --git a/exercises/1.1-subplots_and_basic_plotting.py b/exercises/1.1-subplots_and_basic_plotting.py index 1e4df45..1ff79ca 100644 --- a/exercises/1.1-subplots_and_basic_plotting.py +++ b/exercises/1.1-subplots_and_basic_plotting.py @@ -1,8 +1,6 @@ import numpy as np import matplotlib.pyplot as plt -plt.style.use('classic') - # Try to reproduce the figure shown in images/exercise_1-1.png # Our data... diff --git a/solutions/1.1-subplots_and_basic_plotting.py b/solutions/1.1-subplots_and_basic_plotting.py index cd23241..4a641d6 100644 --- a/solutions/1.1-subplots_and_basic_plotting.py +++ b/solutions/1.1-subplots_and_basic_plotting.py @@ -1,8 +1,6 @@ import numpy as np import matplotlib.pyplot as plt -plt.style.use('classic') - x = np.linspace(0, 10, 100) y1, y2, y3 = np.cos(x), np.cos(x + 1), np.cos(x + 2) names = ['Signal 1', 'Signal 2', 'Signal 3']