Commit fbd8ae16 authored by Michiel Cottaar's avatar Michiel Cottaar Committed by Michiel Cottaar
Browse files

Bug fixes/changes suggested by Mo

parent 8ecc39a4
......@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "markdown",
"id": "4ba14387",
"id": "dda775e0",
"metadata": {},
"source": [
"# Matplotlib tutorial\n",
......@@ -48,7 +48,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "3f5212f2",
"id": "5ccc9a73",
"metadata": {},
"outputs": [],
"source": [
......@@ -58,7 +58,7 @@
},
{
"cell_type": "markdown",
"id": "9b66b866",
"id": "ed561bcb",
"metadata": {},
"source": [
"<a class=\"anchor\" id=\"line\"></a>\n",
......@@ -69,7 +69,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "520e577b",
"id": "80e15ee1",
"metadata": {},
"outputs": [],
"source": [
......@@ -78,7 +78,7 @@
},
{
"cell_type": "markdown",
"id": "88a5db94",
"id": "c2923739",
"metadata": {},
"source": [
"To adjust how the line is plotted, check the documentation:"
......@@ -87,7 +87,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "ba7b1bf7",
"id": "0ce4a966",
"metadata": {},
"outputs": [],
"source": [
......@@ -96,7 +96,7 @@
},
{
"cell_type": "markdown",
"id": "1eb64212",
"id": "e7263d90",
"metadata": {},
"source": [
"As you can see there are a lot of options.\n",
......@@ -109,7 +109,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "b0571451",
"id": "dfbbb093",
"metadata": {},
"outputs": [],
"source": [
......@@ -123,7 +123,7 @@
},
{
"cell_type": "markdown",
"id": "85597103",
"id": "c5cf861d",
"metadata": {},
"source": [
"Because these keywords are so common, you can actually set one or more of them by passing in a string as the third argument."
......@@ -132,7 +132,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "e8c50bcf",
"id": "9446bd5b",
"metadata": {},
"outputs": [],
"source": [
......@@ -145,7 +145,7 @@
},
{
"cell_type": "markdown",
"id": "84e7d60e",
"id": "f17ba1d2",
"metadata": {},
"source": [
"<a class=\"anchor\" id=\"scatter\"></a>\n",
......@@ -156,7 +156,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "fff43424",
"id": "31c06d59",
"metadata": {},
"outputs": [],
"source": [
......@@ -168,7 +168,7 @@
},
{
"cell_type": "markdown",
"id": "ed3c393d",
"id": "777734ae",
"metadata": {},
"source": [
"The third argument is the variable determining the size, while the fourth argument is the variable setting the color.\n",
......@@ -180,7 +180,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "87e83be8",
"id": "1fd95cae",
"metadata": {},
"outputs": [],
"source": [
......@@ -190,7 +190,7 @@
},
{
"cell_type": "markdown",
"id": "78abc3b7",
"id": "72a015c6",
"metadata": {},
"source": [
"where it also returns the number of elements in each bin, as `n`, and\n",
......@@ -207,7 +207,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "b2945a9f",
"id": "0c410bcd",
"metadata": {},
"outputs": [],
"source": [
......@@ -222,7 +222,7 @@
},
{
"cell_type": "markdown",
"id": "09f070f4",
"id": "75c96456",
"metadata": {},
"source": [
"> If you want more advanced distribution plots beyond a simple histogram, have a look at the seaborn [gallery](https://seaborn.pydata.org/examples/index.html) for (too?) many options.\n",
......@@ -236,7 +236,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "79fb7453",
"id": "00caf192",
"metadata": {},
"outputs": [],
"source": [
......@@ -249,7 +249,7 @@
},
{
"cell_type": "markdown",
"id": "d54c0bbc",
"id": "dd9fb30f",
"metadata": {},
"source": [
"<a class=\"anchor\" id=\"shade\"></a>\n",
......@@ -260,7 +260,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "599096f8",
"id": "bb53b679",
"metadata": {},
"outputs": [],
"source": [
......@@ -270,7 +270,7 @@
},
{
"cell_type": "markdown",
"id": "369696fa",
"id": "e47aefc6",
"metadata": {},
"source": [
"This can be nicely combined with a polar projection, to create 2D orientation distribution functions:"
......@@ -279,7 +279,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "b97730b0",
"id": "84538d49",
"metadata": {},
"outputs": [],
"source": [
......@@ -290,7 +290,7 @@
},
{
"cell_type": "markdown",
"id": "049fbd3b",
"id": "91a936ab",
"metadata": {},
"source": [
"The area between two lines can be shaded using `fill_between`:"
......@@ -299,7 +299,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "672b1757",
"id": "ebb0f958",
"metadata": {},
"outputs": [],
"source": [
......@@ -313,7 +313,7 @@
},
{
"cell_type": "markdown",
"id": "e866c409",
"id": "8ae52787",
"metadata": {},
"source": [
"<a class=\"anchor\" id=\"image\"></a>\n",
......@@ -324,7 +324,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "42fc8081",
"id": "0fe3f185",
"metadata": {},
"outputs": [],
"source": [
......@@ -335,12 +335,12 @@
"imslc = imdat[:,:,70]\n",
"plt.imshow(imslc, cmap=plt.cm.gray)\n",
"plt.colorbar()\n",
"plt.grid('off')"
"plt.axis('off')"
]
},
{
"cell_type": "markdown",
"id": "83fb2bb3",
"id": "dabed6ef",
"metadata": {},
"source": [
"Note that matplotlib will use the **voxel data orientation**, and that\n",
......@@ -352,7 +352,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "8aa53d09",
"id": "65e1381a",
"metadata": {},
"outputs": [],
"source": [
......@@ -360,12 +360,12 @@
"plt.xlim(reversed(plt.xlim()))\n",
"plt.ylim(reversed(plt.ylim()))\n",
"plt.colorbar()\n",
"plt.grid('off')"
"plt.axis('off')"
]
},
{
"cell_type": "markdown",
"id": "43fce6ce",
"id": "0e20f40f",
"metadata": {},
"source": [
"> It is easier to produce informative brain images using nilearn or fsleyes\n",
......@@ -377,7 +377,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "37c81436",
"id": "c5101a5b",
"metadata": {},
"outputs": [],
"source": [
......@@ -390,7 +390,7 @@
},
{
"cell_type": "markdown",
"id": "40daff02",
"id": "6c91efd8",
"metadata": {},
"source": [
"By default the locations of the arrows and text will be in data coordinates (i.e., whatever is on the axes),\n",
......@@ -425,7 +425,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "660d1559",
"id": "994a4e47",
"metadata": {},
"outputs": [],
"source": [
......@@ -436,7 +436,7 @@
},
{
"cell_type": "markdown",
"id": "2ad9cfbf",
"id": "0c244a1a",
"metadata": {},
"source": [
"Note that here we explicitly create the figure and add a single sub-plot to the figure.\n",
......@@ -450,7 +450,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "7be3b246",
"id": "87b60efe",
"metadata": {},
"outputs": [],
"source": [
......@@ -459,7 +459,7 @@
},
{
"cell_type": "markdown",
"id": "7fbf6acd",
"id": "9e8785b0",
"metadata": {},
"source": [
"When going through this list carefully you might have spotted that the plotted line is stored in the `lines` property.\n",
......@@ -469,7 +469,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "80481e71",
"id": "1e9372b7",
"metadata": {},
"outputs": [],
"source": [
......@@ -478,7 +478,7 @@
},
{
"cell_type": "markdown",
"id": "fa8e6fee",
"id": "afd6a54e",
"metadata": {},
"source": [
"This shows us all the properties stored about this line,\n",
......@@ -495,7 +495,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "83b536d2",
"id": "5bff872f",
"metadata": {},
"outputs": [],
"source": [
......@@ -511,7 +511,7 @@
},
{
"cell_type": "markdown",
"id": "3438c4f7",
"id": "510185fb",
"metadata": {},
"source": [
"For such a simple example, this works fine. But for longer examples you would find yourself constantly looking back through the\n",
......@@ -523,7 +523,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "4731d436",
"id": "52ffc81d",
"metadata": {},
"outputs": [],
"source": [
......@@ -536,7 +536,7 @@
},
{
"cell_type": "markdown",
"id": "bca7abe6",
"id": "48464ab0",
"metadata": {},
"source": [
"Here we use `plt.subplots`, which creates both a new figure for us and a grid of sub-plots. \n",
......@@ -551,7 +551,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "83fab131",
"id": "d047679b",
"metadata": {},
"outputs": [],
"source": [
......@@ -565,7 +565,7 @@
},
{
"cell_type": "markdown",
"id": "609cdb3f",
"id": "c8fe247b",
"metadata": {},
"source": [
"Uncomment `fig.tight_layout` and see how it adjusts the spacings between the plots automatically to reduce the whitespace.\n",
......@@ -576,7 +576,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "972fda35",
"id": "c82a0837",
"metadata": {},
"outputs": [],
"source": [
......@@ -591,7 +591,7 @@
},
{
"cell_type": "markdown",
"id": "18d903e1",
"id": "09b2b281",
"metadata": {},
"source": [
"<a class=\"anchor\" id=\"grid-spec\"></a>\n",
......@@ -603,7 +603,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "e98b00c8",
"id": "b4f4a54b",
"metadata": {},
"outputs": [],
"source": [
......@@ -623,7 +623,7 @@
},
{
"cell_type": "markdown",
"id": "0d363e0e",
"id": "37bc60de",
"metadata": {},
"source": [
"<a class=\"anchor\" id=\"styling\"></a>\n",
......@@ -637,7 +637,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "9553b91a",
"id": "c4b8f402",
"metadata": {},
"outputs": [],
"source": [
......@@ -650,7 +650,7 @@
},
{
"cell_type": "markdown",
"id": "79b53065",
"id": "7c6e0fc1",
"metadata": {},
"source": [
"You can also set any of these properties by calling `Axes.set` directly:"
......@@ -659,7 +659,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "97643220",
"id": "ff0502fc",
"metadata": {},
"outputs": [],
"source": [
......@@ -674,7 +674,7 @@
},
{
"cell_type": "markdown",
"id": "e2fbb884",
"id": "0db1eb83",
"metadata": {},
"source": [
"> To match the matlab API and save some typing the equivalent commands in the procedural interface do not have the `set_` preset. So, they are `plt.xlabel`, `plt.ylabel`, `plt.title`. This is also true for many of the `set_` commands we will see below.\n",
......@@ -685,7 +685,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "5f16a656",
"id": "369c02d6",
"metadata": {},
"outputs": [],
"source": [
......@@ -698,7 +698,7 @@
},
{
"cell_type": "markdown",
"id": "f69eb693",
"id": "6095ba78",
"metadata": {},
"source": [
"<a class=\"anchor\" id=\"axis\"></a>\n",
......@@ -717,7 +717,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "b2d196a9",
"id": "c8f5a0dd",
"metadata": {},
"outputs": [],
"source": [
......@@ -738,7 +738,7 @@
},
{
"cell_type": "markdown",
"id": "c4c35f2a",
"id": "2dbbad8d",
"metadata": {},
"source": [
"As illustrated earlier, we can get a more complete list of the things we could change about the x-axis by looking at its properties:"
......@@ -747,16 +747,16 @@
{
"cell_type": "code",
"execution_count": null,
"id": "ab2633a6",
"id": "20ca99eb",
"metadata": {},
"outputs": [],
"source": [
"plt.getp(axes.get_xlabel())"
"plt.getp(axes.get_xaxis())"
]
},
{
"cell_type": "markdown",
"id": "440d18ce",
"id": "35fe5da3",
"metadata": {},
"source": [
"<a class=\"anchor\" id=\"faq\"></a>\n",
......@@ -788,7 +788,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "93dbeb6e",
"id": "1620a4da",
"metadata": {},
"outputs": [],
"source": [
......@@ -797,7 +797,7 @@
},
{
"cell_type": "markdown",
"id": "d53eef2b",
"id": "b0024423",
"metadata": {},
"source": [
"> If you are using Jupyterlab (new version of the jupyter notebook) the `nbagg` backend will not work. Instead you will have to install `ipympl` and then use the `widgets` backend to get an interactive backend (this also works in the old notebooks).\n",
......@@ -808,17 +808,17 @@
{
"cell_type": "code",
"execution_count": null,
"id": "c16b4394",
"id": "b67ee344",
"metadata": {},
"outputs": [],
"source": [
"import matplotlib\n",
"matplotlib.use(\"osx\")"
"matplotlib.use(\"MacOSX\")"
]
},
{
"cell_type": "markdown",
"id": "96529a3c",
"id": "36ca2dfc",
"metadata": {},
"source": [
"Usually, the default backend will be fine, so you will not have to set it. \n",
......
%% Cell type:markdown id:4ba14387 tags:
%% Cell type:markdown id:dda775e0 tags:
# Matplotlib tutorial
The main plotting library in python is `matplotlib`.
It provides a simple interface to just explore the data,
while also having a lot of flexibility to create publication-worthy plots.
In fact, the vast majority of python-produced plots in papers will be either produced
directly using matplotlib or by one of the many plotting libraries built on top of
matplotlib (such as [seaborn](https://seaborn.pydata.org/) or [nilearn](https://nilearn.github.io/)).
Like everything in python, there is a lot of help available online (just google it or ask your local pythonista).
A particularly useful resource for matplotlib is the [gallery](https://matplotlib.org/gallery/index.html).
Here you can find a wide range of plots.
Just find one that looks like what you want to do and click on it to see (and copy) the code used to generate the plot.
## Contents
- [Basic plotting commands](#basic-plotting-commands)
- [Line plots](#line)
- [Scatter plots](#scatter)
- [Histograms and bar plots](#histograms)
- [Adding error bars](#error)
- [Shading regions](#shade)
- [Displaying images](#image)
- [Adding lines, arrows, text](#annotations)
- [Using the object-oriented interface](#OO)
- [Multiple plots (i.e., subplots)](#subplots)
- [Adjusting plot layouts](#layout)
- [Advanced grid configurations (GridSpec)](#grid-spec)
- [Styling your plot](#styling)
- [Setting title and labels](#labels)
- [Editing the x- and y-axis](#axis)
- [FAQ](#faq)
- [Why am I getting two images?](#double-image)
- [I produced a plot in my python script, but it does not show up](#show)
- [Changing where the image appears: backends](#backends)
<a class="anchor" id="basic-plotting-commands"></a>
## Basic plotting commands
Let's start with the basic imports:
%% Cell type:code id:3f5212f2 tags:
%% Cell type:code id:5ccc9a73 tags:
```
import matplotlib.pyplot as plt
import numpy as np
```
%% Cell type:markdown id:9b66b866 tags:
%% Cell type:markdown id:ed561bcb tags:
<a class="anchor" id="line"></a>
### Line plots
A basic lineplot can be made just by calling `plt.plot`:
%% Cell type:code id:520e577b tags:
%% Cell type:code id:80e15ee1 tags:
```
plt.plot([1, 2, 3], [1.3, 4.2, 3.1])
```
%% Cell type:markdown id:88a5db94 tags:
%% Cell type:markdown id:c2923739 tags:
To adjust how the line is plotted, check the documentation:
%% Cell type:code id:ba7b1bf7 tags:
%% Cell type:code id:0ce4a966 tags:
```
plt.plot?
```
%% Cell type:markdown id:1eb64212 tags:
%% Cell type:markdown id:e7263d90 tags:
As you can see there are a lot of options.
The ones you will probably use most often are:
- `linestyle`: how the line is plotted (set to '' to omit the line)
- `marker`: how the points are plotted (these are not plotted by default)
- `color`: what color to use (defaults to cycling through a set of 7 colors)
%% Cell type:code id:b0571451 tags:
%% Cell type:code id:dfbbb093 tags:
```
theta = np.linspace(0, 2 * np.pi, 101)
plt.plot(np.sin(theta), np.cos(theta))
plt.plot([-0.3, 0.3], [0.3, 0.3], marker='o', linestyle='', markersize=20)
plt.plot(0, -0.1, marker='s', color='black')
x = np.linspace(-0.5, 0.5, 5)
plt.plot(x, x ** 2 - 0.5, linestyle='--', marker='+', color='red')
```
%% Cell type:markdown id:85597103 tags:
%% Cell type:markdown id:c5cf861d tags:
Because these keywords are so common, you can actually set one or more of them by passing in a string as the third argument.
%% Cell type:code id:e8c50bcf tags:
%% Cell type:code id:9446bd5b tags:
```
x = np.linspace(0, 1, 11)
plt.plot(x, x)
plt.plot(x, x ** 2, '--') # sets the linestyle to dashed
plt.plot(x, x ** 3, 's') # sets the marker to square (and turns off the line)
plt.plot(x, x ** 4, '^y:') # sets the marker to triangles (i.e., '^'), linestyle to dotted (i.e., ':'), and the color to yellow (i.e., 'y')
```
%% Cell type:markdown id:84e7d60e tags:
%% Cell type:markdown id:f17ba1d2 tags:
<a class="anchor" id="scatter"></a>
### Scatter plots
The main extra feature of `plt.scatter` over `plt.plot` is that you can vary the color and size of the points based on some other variable array:
%% Cell type:code id:fff43424 tags:
%% Cell type:code id:31c06d59 tags:
```
x = np.random.rand(30)
y = np.random.rand(30)
plt.scatter(x, y, x * 30, y)
plt.colorbar() # adds a colorbar
```
%% Cell type:markdown id:ed3c393d tags:
%% Cell type:markdown id:777734ae tags:
The third argument is the variable determining the size, while the fourth argument is the variable setting the color.
<a class="anchor" id="histograms"></a>
### Histograms and bar plots
For a simple histogram you can do this:
%% Cell type:code id:87e83be8 tags:
%% Cell type:code id:1fd95cae tags:
```
r = np.random.rand(1000)
n,bins,_ = plt.hist((r-0.5)**2, bins=30)
```
%% Cell type:markdown id:78abc3b7 tags:
%% Cell type:markdown id:72a015c6 tags:
where it also returns the number of elements in each bin, as `n`, and
the bin centres, as `bins`.
> The `_` in the third part on the left
> hand side is a shorthand for just throwing away the corresponding part
> of the return structure.
There is also a call for doing bar plots:
%% Cell type:code id:b2945a9f tags:
%% Cell type:code id:0c410bcd tags:
```
samp1 = r[0:10]
samp2 = r[10:20]
bwidth = 0.3
xcoord = np.arange(10)
plt.bar(xcoord-bwidth, samp1, width=bwidth, color='red', label='Sample 1')
plt.bar(xcoord, samp2, width=bwidth, color='blue', label='Sample 2')
plt.legend(loc='upper left')
```
%% Cell type:markdown id:09f070f4 tags:
%% Cell type:markdown id:75c96456 tags:
> If you want more advanced distribution plots beyond a simple histogram, have a look at the seaborn [gallery](https://seaborn.pydata.org/examples/index.html) for (too?) many options.
<a class="anchor" id="error"></a>
### Adding error bars
If your data is not completely perfect and has for some obscure reason some uncertainty associated with it,
you can plot these using `plt.error`:
%% Cell type:code id:79fb7453 tags:
%% Cell type:code id:00caf192 tags:
```
x = np.arange(5)
y1 = [0.3, 0.5, 0.7, 0.1, 0.3]
yerr = [0.12, 0.28, 0.1, 0.25, 0.6]
xerr = 0.3
plt.errorbar(x, y1, yerr, xerr, marker='s', linestyle='')
```
%% Cell type:markdown id:d54c0bbc tags:
%% Cell type:markdown id:dd9fb30f tags:
<a class="anchor" id="shade"></a>
### Shading regions
An area below a plot can be shaded using `plt.fill`
%% Cell type:code id:599096f8 tags:
%% Cell type:code id:bb53b679 tags:
```
x = np.linspace(0, 2, 100)
plt.fill(x, np.sin(x * np.pi))
```
%% Cell type:markdown id:369696fa tags:
%% Cell type:markdown id:e47aefc6 tags:
This can be nicely combined with a polar projection, to create 2D orientation distribution functions:
%% Cell type:code id:b97730b0 tags:
%% Cell type:code id:84538d49 tags:
```
plt.subplot(projection='polar')
theta = np.linspace(0, 2 * np.pi, 100)
plt.fill(theta, np.exp(-2 * np.cos(theta) ** 2))
```
%% Cell type:markdown id:049fbd3b tags:
%% Cell type:markdown id:91a936ab tags:
The area between two lines can be shaded using `fill_between`:
%% Cell type:code id:672b1757 tags:
%% Cell type:code id:ebb0f958 tags:
```
x = np.linspace(0, 10, 1000)
y = 5 * np.sin(5 * x) + x - 0.1 * x ** 2
yl = x - 0.1 * x ** 2 - 5
yu = yl + 10
plt.plot(x, y, 'r')
plt.fill_between(x, yl, yu)
```
%% Cell type:markdown id:e866c409 tags:
%% Cell type:markdown id:8ae52787 tags:
<a class="anchor" id="image"></a>
### Displaying images
The main command for displaying images is `plt.imshow` (use `plt.pcolor` for cases where you do not have a regular grid)
%% Cell type:code id:42fc8081 tags:
%% Cell type:code id:0fe3f185 tags:
```
import nibabel as nib
import os.path as op
nim = nib.load(op.expandvars('${FSLDIR}/data/standard/MNI152_T1_1mm.nii.gz'), mmap=False)
imdat = nim.get_data().astype(float)
imslc = imdat[:,:,70]
plt.imshow(imslc, cmap=plt.cm.gray)
plt.colorbar()
plt.grid('off')
plt.axis('off')
```
%% Cell type:markdown id:83fb2bb3 tags:
%% Cell type:markdown id:dabed6ef tags:
Note that matplotlib will use the **voxel data orientation**, and that
configuring the plot orientation is **your responsibility**. To rotate a
slice, simply transpose the data (`.T`). To invert the data along along an
axis, you don't need to modify the data - simply swap the axis limits around:
%% Cell type:code id:8aa53d09 tags:
<