Commit 8ecc39a4 authored by Michiel Cottaar's avatar Michiel Cottaar Committed by Michiel Cottaar
Browse files

added link to artist tutorial

parent 96f3b243
......@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "markdown",
"id": "5567ba9e",
"id": "4ba14387",
"metadata": {},
"source": [
"# Matplotlib tutorial\n",
......@@ -48,7 +48,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "3917392c",
"id": "3f5212f2",
"metadata": {},
"outputs": [],
"source": [
......@@ -58,7 +58,7 @@
},
{
"cell_type": "markdown",
"id": "76688c00",
"id": "9b66b866",
"metadata": {},
"source": [
"<a class=\"anchor\" id=\"line\"></a>\n",
......@@ -69,7 +69,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "00d5ff18",
"id": "520e577b",
"metadata": {},
"outputs": [],
"source": [
......@@ -78,7 +78,7 @@
},
{
"cell_type": "markdown",
"id": "3d0472b7",
"id": "88a5db94",
"metadata": {},
"source": [
"To adjust how the line is plotted, check the documentation:"
......@@ -87,7 +87,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "38d3f3ab",
"id": "ba7b1bf7",
"metadata": {},
"outputs": [],
"source": [
......@@ -96,7 +96,7 @@
},
{
"cell_type": "markdown",
"id": "867ea1f5",
"id": "1eb64212",
"metadata": {},
"source": [
"As you can see there are a lot of options.\n",
......@@ -109,7 +109,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "c705366a",
"id": "b0571451",
"metadata": {},
"outputs": [],
"source": [
......@@ -123,7 +123,7 @@
},
{
"cell_type": "markdown",
"id": "f7e493a7",
"id": "85597103",
"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": "c2b6c5c5",
"id": "e8c50bcf",
"metadata": {},
"outputs": [],
"source": [
......@@ -145,7 +145,7 @@
},
{
"cell_type": "markdown",
"id": "c7dd3aa9",
"id": "84e7d60e",
"metadata": {},
"source": [
"<a class=\"anchor\" id=\"scatter\"></a>\n",
......@@ -156,7 +156,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "33b81ef4",
"id": "fff43424",
"metadata": {},
"outputs": [],
"source": [
......@@ -168,7 +168,7 @@
},
{
"cell_type": "markdown",
"id": "22de1aac",
"id": "ed3c393d",
"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": "0c445269",
"id": "87e83be8",
"metadata": {},
"outputs": [],
"source": [
......@@ -190,7 +190,7 @@
},
{
"cell_type": "markdown",
"id": "41a54dd8",
"id": "78abc3b7",
"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": "9d7c817d",
"id": "b2945a9f",
"metadata": {},
"outputs": [],
"source": [
......@@ -222,7 +222,7 @@
},
{
"cell_type": "markdown",
"id": "eca1cea7",
"id": "09f070f4",
"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": "939fcf82",
"id": "79fb7453",
"metadata": {},
"outputs": [],
"source": [
......@@ -249,7 +249,7 @@
},
{
"cell_type": "markdown",
"id": "cb1a8d17",
"id": "d54c0bbc",
"metadata": {},
"source": [
"<a class=\"anchor\" id=\"shade\"></a>\n",
......@@ -260,7 +260,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "f7221bc3",
"id": "599096f8",
"metadata": {},
"outputs": [],
"source": [
......@@ -270,7 +270,7 @@
},
{
"cell_type": "markdown",
"id": "86d77cf6",
"id": "369696fa",
"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": "b96cbc10",
"id": "b97730b0",
"metadata": {},
"outputs": [],
"source": [
......@@ -290,7 +290,7 @@
},
{
"cell_type": "markdown",
"id": "5a0defe8",
"id": "049fbd3b",
"metadata": {},
"source": [
"The area between two lines can be shaded using `fill_between`:"
......@@ -299,7 +299,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "3f11d97b",
"id": "672b1757",
"metadata": {},
"outputs": [],
"source": [
......@@ -313,7 +313,7 @@
},
{
"cell_type": "markdown",
"id": "aa3fb87b",
"id": "e866c409",
"metadata": {},
"source": [
"<a class=\"anchor\" id=\"image\"></a>\n",
......@@ -324,7 +324,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "63f18c75",
"id": "42fc8081",
"metadata": {},
"outputs": [],
"source": [
......@@ -340,7 +340,7 @@
},
{
"cell_type": "markdown",
"id": "be2facc0",
"id": "83fb2bb3",
"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": "1a960ddf",
"id": "8aa53d09",
"metadata": {},
"outputs": [],
"source": [
......@@ -365,7 +365,7 @@
},
{
"cell_type": "markdown",
"id": "070f5772",
"id": "43fce6ce",
"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": "c7f6c0f6",
"id": "37c81436",
"metadata": {},
"outputs": [],
"source": [
......@@ -390,7 +390,7 @@
},
{
"cell_type": "markdown",
"id": "de291180",
"id": "40daff02",
"metadata": {},
"source": [
"By default the locations of the arrows and text will be in data coordinates (i.e., whatever is on the axes),\n",
......@@ -402,7 +402,7 @@
"<a class=\"anchor\" id=\"OO\"></a>\n",
"## Using the object-oriented interface\n",
"In the examples above we simply added multiple lines/points/bars/images \n",
"(collectively called artists in matplotlib) to a single plot.\n",
"(collectively called [artists](https://matplotlib.org/stable/tutorials/intermediate/artists.html) in matplotlib) to a single plot.\n",
"To prettify this plots, we first need to know what all the features are called:\n",
"\n",
"![anatomy of a plot](https://matplotlib.org/stable/_images/anatomy.png)\n",
......@@ -425,7 +425,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "3d3482ef",
"id": "660d1559",
"metadata": {},
"outputs": [],
"source": [
......@@ -436,7 +436,7 @@
},
{
"cell_type": "markdown",
"id": "4923481d",
"id": "2ad9cfbf",
"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": "c0c64d42",
"id": "7be3b246",
"metadata": {},
"outputs": [],
"source": [
......@@ -459,7 +459,7 @@
},
{
"cell_type": "markdown",
"id": "76b789ab",
"id": "7fbf6acd",
"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": "02f9cb81",
"id": "80481e71",
"metadata": {},
"outputs": [],
"source": [
......@@ -478,7 +478,7 @@
},
{
"cell_type": "markdown",
"id": "9a192752",
"id": "fa8e6fee",
"metadata": {},
"source": [
"This shows us all the properties stored about this line,\n",
......@@ -495,7 +495,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "3cafa27a",
"id": "83b536d2",
"metadata": {},
"outputs": [],
"source": [
......@@ -511,7 +511,7 @@
},
{
"cell_type": "markdown",
"id": "51141fee",
"id": "3438c4f7",
"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": "a7a6b2c7",
"id": "4731d436",
"metadata": {},
"outputs": [],
"source": [
......@@ -536,7 +536,7 @@
},
{
"cell_type": "markdown",
"id": "a1ec90e2",
"id": "bca7abe6",
"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": "0a5f85cc",
"id": "83fab131",
"metadata": {},
"outputs": [],
"source": [
......@@ -565,7 +565,7 @@
},
{
"cell_type": "markdown",
"id": "ab07e5ab",
"id": "609cdb3f",
"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": "8e420304",
"id": "972fda35",
"metadata": {},
"outputs": [],
"source": [
......@@ -591,7 +591,7 @@
},
{
"cell_type": "markdown",
"id": "260dc7eb",
"id": "18d903e1",
"metadata": {},
"source": [
"<a class=\"anchor\" id=\"grid-spec\"></a>\n",
......@@ -603,7 +603,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "f58613d6",
"id": "e98b00c8",
"metadata": {},
"outputs": [],
"source": [
......@@ -623,7 +623,7 @@
},
{
"cell_type": "markdown",
"id": "cf0ef5c8",
"id": "0d363e0e",
"metadata": {},
"source": [
"<a class=\"anchor\" id=\"styling\"></a>\n",
......@@ -637,7 +637,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "6f49e003",
"id": "9553b91a",
"metadata": {},
"outputs": [],
"source": [
......@@ -650,7 +650,7 @@
},
{
"cell_type": "markdown",
"id": "d1c2e77c",
"id": "79b53065",
"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": "5d744cf1",
"id": "97643220",
"metadata": {},
"outputs": [],
"source": [
......@@ -674,7 +674,7 @@
},
{
"cell_type": "markdown",
"id": "9e621844",
"id": "e2fbb884",
"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": "389a85ff",
"id": "5f16a656",
"metadata": {},
"outputs": [],
"source": [
......@@ -698,7 +698,7 @@
},
{
"cell_type": "markdown",
"id": "c7612d2c",
"id": "f69eb693",
"metadata": {},
"source": [
"<a class=\"anchor\" id=\"axis\"></a>\n",
......@@ -717,7 +717,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "0b91948e",
"id": "b2d196a9",
"metadata": {},
"outputs": [],
"source": [
......@@ -738,7 +738,7 @@
},
{
"cell_type": "markdown",
"id": "3d5db817",
"id": "c4c35f2a",
"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,7 +747,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "ca4c208f",
"id": "ab2633a6",
"metadata": {},
"outputs": [],
"source": [
......@@ -756,7 +756,7 @@
},
{
"cell_type": "markdown",
"id": "61b4f566",
"id": "440d18ce",
"metadata": {},
"source": [
"<a class=\"anchor\" id=\"faq\"></a>\n",
......@@ -788,7 +788,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "33900afe",
"id": "93dbeb6e",
"metadata": {},
"outputs": [],
"source": [
......@@ -797,7 +797,7 @@
},
{
"cell_type": "markdown",
"id": "7273f530",
"id": "d53eef2b",
"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,7 +808,7 @@
{
"cell_type": "code",
"execution_count": null,
"id": "b50352a0",
"id": "c16b4394",
"metadata": {},
"outputs": [],
"source": [
......@@ -818,7 +818,7 @@
},
{
"cell_type": "markdown",
"id": "2a436f07",
"id": "96529a3c",
"metadata": {},
"source": [
"Usually, the default backend will be fine, so you will not have to set it. \n",
......
%% Cell type:markdown id:5567ba9e tags:
%% Cell type:markdown id:4ba14387 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:3917392c tags:
%% Cell type:code id:3f5212f2 tags:
```
import matplotlib.pyplot as plt
import numpy as np
```
%% Cell type:markdown id:76688c00 tags:
%% Cell type:markdown id:9b66b866 tags:
<a class="anchor" id="line"></a>
### Line plots
A basic lineplot can be made just by calling `plt.plot`:
%% Cell type:code id:00d5ff18 tags:
%% Cell type:code id:520e577b tags:
```
plt.plot([1, 2, 3], [1.3, 4.2, 3.1])
```
%% Cell type:markdown id:3d0472b7 tags:
%% Cell type:markdown id:88a5db94 tags:
To adjust how the line is plotted, check the documentation:
%% Cell type:code id:38d3f3ab tags:
%% Cell type:code id:ba7b1bf7 tags:
```
plt.plot?
```
%% Cell type:markdown id:867ea1f5 tags:
%% Cell type:markdown id:1eb64212 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:c705366a tags:
%% Cell type:code id:b0571451 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:f7e493a7 tags:
%% Cell type:markdown id:85597103 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:c2b6c5c5 tags:
%% Cell type:code id:e8c50bcf 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:c7dd3aa9 tags:
%% Cell type:markdown id:84e7d60e 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:33b81ef4 tags:
%% Cell type:code id:fff43424 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:22de1aac tags:
%% Cell type:markdown id:ed3c393d 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:0c445269 tags:
%% Cell type:code id:87e83be8 tags:
```
r = np.random.rand(1000)
n,bins,_ = plt.hist((r-0.5)**2, bins=30)
```
%% Cell type:markdown id:41a54dd8 tags:
%% Cell type:markdown id:78abc3b7 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:9d7c817d tags:
%% Cell type:code id:b2945a9f 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:eca1cea7 tags:
%% Cell type:markdown id:09f070f4 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:939fcf82 tags:
%% Cell type:code id:79fb7453 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:cb1a8d17 tags:
%% Cell type:markdown id:d54c0bbc tags:
<a class="anchor" id="shade"></a>
### Shading regions
An area below a plot can be shaded using `plt.fill`
%% Cell type:code id:f7221bc3 tags:
%% Cell type:code id:599096f8 tags:
```
x = np.linspace(0, 2, 100)
plt.fill(x, np.sin(x * np.pi))
```
%% Cell type:markdown id:86d77cf6 tags:
%% Cell type:markdown id:369696fa tags:
This can be nicely combined with a polar projection, to create 2D orientation distribution functions:
%% Cell type:code id:b96cbc10 tags:
%% Cell type:code id:b97730b0 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:5a0defe8 tags:
%% Cell type:markdown id:049fbd3b tags:
The area between two lines can be shaded using `fill_between`:
%% Cell type:code id:3f11d97b tags:
%% Cell type:code id:672b1757 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:aa3fb87b tags:
%% Cell type:markdown id:e866c409 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:63f18c75 tags:
%% Cell type:code id:42fc8081 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')
```
%% Cell type:markdown id:be2facc0 tags:
%% Cell type:markdown id:83fb2bb3 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:1a960ddf tags:
%% Cell type:code id:8aa53d09 tags:
```
plt.imshow(imslc.T, cmap=plt.cm.gray)
plt.xlim(reversed(plt.xlim()))
plt.ylim(reversed(plt.ylim()))
plt.colorbar()
plt.grid('off')
```
%% Cell type:markdown id:070f5772 tags:
%% Cell type:markdown id:43fce6ce tags:
> It is easier to produce informative brain images using nilearn or fsleyes
<a class="anchor" id="annotations"></a>
### Adding lines, arrows, and text
Adding horizontal/vertical lines, arrows, and text:
%% Cell type:code id:c7f6c0f6 tags:
%% Cell type:code id:37c81436 tags:
```
plt.axhline(-1) # horizontal line
plt.axvline(1) # vertical line
plt.arrow(0.2, -0.2, 0.2, -0.8, length_includes_head=True, width=0.01)
plt.text(0.5, 0.5, 'middle of the plot', transform=plt.gca().transAxes, ha='center', va='center')
plt.annotate("line crossing", (1, -1), (0.8, -0.8), arrowprops={}) # adds both text and arrow; need to set the arrowprops keyword for the arrow to be plotted
```
%% Cell type:markdown id:de291180 tags:
%% Cell type:markdown id:40daff02 tags: