This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision Next revision Both sides next revision | ||
other:python:matplotlib_by_jyp [2020/01/28 10:34] jypeter [Working with matplotlib (JYP version)] Added links to map resources |
other:python:matplotlib_by_jyp [2020/04/10 10:46] jypeter [Useful matplotlib reference pages] Improved 'X and Y axes parameters' and added grid lines |
||
---|---|---|---|
Line 3: | Line 3: | ||
**Summary**: there are lots of python libraries that you can use for plotting, but Matplotlib has become a //de facto// standard | **Summary**: there are lots of python libraries that you can use for plotting, but Matplotlib has become a //de facto// standard | ||
- | You can get more python information on the [[other:python:jyp_steps|JYP's recommended steps for learning python]] page. | + | You can get more **information about python** on the [[other:python:jyp_steps|JYP's recommended steps for learning python]] page. |
- | You can get more information on plotting maps in the [[other:python:jyp_steps#cartopy_iris|Cartopy + Iris section]] and the [[other:python:jyp_steps#basemap|basemap section]] (no python3 support in basemap, basemap is being slowly replaced by cartopy), with examples on the [[other:python:maps_by_jyp|JYP's map room]] page. | + | You can get more **information on plotting maps** in the [[other:python:jyp_steps#cartopy_iris|Cartopy + Iris section]] and the [[other:python:jyp_steps#basemap|basemap section]], with examples on the [[other:python:maps_by_jyp|JYP's map room]] page. Note: there will be no python3 support in basemap, and basemap is being slowly replaced by cartopy |
- | Where: [[http://matplotlib.org|Matplotlib web site]] | + | matplotlib official website: [[http://matplotlib.org|Matplotlib web site]] |
- | Help on //stack overflow//: [[https://stackoverflow.com/questions/tagged/matplotlib|matplotlib help]] | + | matplotlib help on //stack overflow//: [[https://stackoverflow.com/questions/tagged/matplotlib|matplotlib help]] |
===== Starting (and more) with matplotlib ===== | ===== Starting (and more) with matplotlib ===== | ||
Line 17: | Line 17: | ||
There are lots of online tutorials that often don't show much more than a simple scatter plot with basic page setup. | There are lots of online tutorials that often don't show much more than a simple scatter plot with basic page setup. | ||
- | This page will hopefully give you some keeys to become (progressively) a matplotlib power user. | + | This page will hopefully give you some keys to become (progressively) a matplotlib power user. |
<wrap hi>A good way to start with matplotlib</wrap> is to quickly read this section, practice, and read this section again (and again) | <wrap hi>A good way to start with matplotlib</wrap> is to quickly read this section, practice, and read this section again (and again) | ||
Line 28: | Line 28: | ||
* later, you may need other matplotlib related modules, for advanced usage | * later, you may need other matplotlib related modules, for advanced usage | ||
- You need to know some **matplotlib specific vocabulary**: | - You need to know some **matplotlib specific vocabulary**: | ||
- | * a Matplotlib **//Figure//** (or //canvas//) is a **graphical window** in which you create your plots... | + | * a Matplotlib **//Figure//** (or //canvas//) is a **graphical window** or //page// in which you create your plots... |
* example: ''my_page = plt.figure()'' | * example: ''my_page = plt.figure()'' | ||
* if you need several display windows at the same time, create several figures!\\ <code>win_1 = plt.figure() | * if you need several display windows at the same time, create several figures!\\ <code>win_1 = plt.figure() | ||
win_2 = plt.figure()</code> | win_2 = plt.figure()</code> | ||
- | * the [[http://matplotlib.org/faq/usage_faq.html#parts-of-a-figure|parts of a figure]] are often positioned in //normalized coordinates//: ''(0, 0)'' is the bottom left of the figure, and ''(1, 1)'' the top right | + | * the [[http://matplotlib.org/faq/usage_faq.html#parts-of-a-figure|parts of a figure]] are usually positioned in //normalized coordinates//: ''(0, 0)'' is the bottom left of the figure, and ''(1, 1)'' is the top right |
* You don't really specify the **page orientation** (//portrait// or //landscape//) of a plot. If you want a portrait plot, it's up to you to create a plot that will look higher than it is large. The idea is not to worry about this and just check the final resulting plot: create a plot, save it, display the resulting png/pdf and then adjust the creation script | * You don't really specify the **page orientation** (//portrait// or //landscape//) of a plot. If you want a portrait plot, it's up to you to create a plot that will look higher than it is large. The idea is not to worry about this and just check the final resulting plot: create a plot, save it, display the resulting png/pdf and then adjust the creation script | ||
* If you do have an idea of the layout of what you want to plot, it may be easier to explicitly specify the figure size/ratio at creation time, and then try to //fill// the normalized coordinates space of the figure | * If you do have an idea of the layout of what you want to plot, it may be easier to explicitly specify the figure size/ratio at creation time, and then try to //fill// the normalized coordinates space of the figure | ||
* ''my_page = plt.figure()'': the ratio of the default figure is ''landscape'', because it is 33% larger than it is high. Creating a default figure will be OK most of the time! | * ''my_page = plt.figure()'': the ratio of the default figure is ''landscape'', because it is 33% larger than it is high. Creating a default figure will be OK most of the time! | ||
- | * ''my_page = plt.figure(figsize=(width, height))'': create a figure with a custom ratio (sizes are considered to be in inches) | + | * ''my_page = plt.figure(figsize=(width, height))'': create a figure with a custom ratio |
- | * ''my_page = plt.figure(figsize=(8.3, 11.7))'': create a figure that will theoretically fill an A4 size page in portrait mode (check [[https://www.papersizes.org/a-paper-sizes.htm|Dimensions Of A Series Paper Sizes]] if you need more size details) | + | * The specified ''width'' and ''height'' are supposed to be in inches (1 inch = 2.54 cm) |
+ | * ''my_page = plt.figure(figsize=(8.3, 11.7))'': create a figure that will theoretically fill an A4 size page in portrait mode (check [[https://www.papersizes.org/a-paper-sizes.htm|Dimensions Of A Series Paper Sizes]] if you need more details about standard paper sizes) | ||
* a Matplotlib **//Axis//** is a **plot** inside a Figure... [[http://matplotlib.org/faq/usage_faq.html#parts-of-a-figure|More details]] | * a Matplotlib **//Axis//** is a **plot** inside a Figure... [[http://matplotlib.org/faq/usage_faq.html#parts-of-a-figure|More details]] | ||
* reserve space for **one plot** that will use most of the available area of the figure/page: | * reserve space for **one plot** that will use most of the available area of the figure/page: | ||
- | * ''my_plot = my_page.add_subplot(1, 1, 1)'': syntax is ''add_subplot(nrows, ncols, index)'' | + | * ''my_plot = my_page.add_subplot(1, 1, 1)'' or ''my_plot = my_page.subplot**s**()'' |
- | * ''my_plot = my_page.subplot**s**()'' | + | * the syntax for **more than one plot** is: ''add_subplot(nrows, ncols, index)'' |
* create **3 plots on 1 column** (each plot uses the full width of the figure): | * create **3 plots on 1 column** (each plot uses the full width of the figure): | ||
* <code>top_plot = my_page.add_subplot(3, 1, 1) | * <code>top_plot = my_page.add_subplot(3, 1, 1) | ||
- | middle_plot = my_page.add_subplot(3, 1, 2) | + | mid_plot = my_page.add_subplot(3, 1, 2) |
- | bottom_plot = my_page.add_subplot(3, 1, 3)</code> | + | bot_plot = my_page.add_subplot(3, 1, 3)</code> |
- | * the following method is more efficient than add_subplot when there are lots of plots on a page<code>plot_array = my_page.subplots(3, 1) | + | * creating an //array of plots// with ''subplots'' is **more efficient** than ''add_subplot'', when there are lots of plots on a page<code>plot_array = my_page.subplots(3, 1) |
top_plot = plot_array[0] | top_plot = plot_array[0] | ||
- | middle_plot = plot_array[1] | + | mid_plot = plot_array[1] |
- | bottom_plot = plot_array[2]</code> | + | bot_plot = plot_array[2]</code> |
- | * creating a figure and axes with a single line: ''my_page, plot_array = **plt**.subplots(3, 1)'' | + | * it is **even more efficient** to create a //figure// **and** //axes// with a single line: |
+ | * one plot on one page: ''my_page, my_plot = **plt**.subplots()'' | ||
+ | * three plots on one A4 portrait page:\\ <code> | ||
+ | my_page, plot_array = plt.subplots(nrows=3, ncols=1, | ||
+ | figsize=(8.3, 11.) # A4 portrait) | ||
+ | top_plot = plot_array[0] | ||
+ | mid_plot = plot_array[1] | ||
+ | bot_plot = plot_array[2] | ||
+ | </code> | ||
+ | * if you need to specify the same additional //keyword// parameters (that will be passed //behind the scene// to the ''add_subplot'' function), you can use the ''subplot_kw'' parameter. For example, if all the plots will be created with cartopy, using the //Robinson// projection, the example above becomes:\\ <code> | ||
+ | my_page, plot_array = plt.subplots(nrows=3, ncols=1, | ||
+ | figsize=(8.3, 11.), # A4 portrait | ||
+ | subplot_kw=dict(projection=ccrs.Robinson())) | ||
+ | </code> | ||
* use [[https://matplotlib.org/api/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.add_axes|my_page.add_axes(...)]] to add an axis in an arbirary location of the page\\ ''my_page.add_axes([left, bottom, width, height])'' | * use [[https://matplotlib.org/api/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.add_axes|my_page.add_axes(...)]] to add an axis in an arbirary location of the page\\ ''my_page.add_axes([left, bottom, width, height])'' | ||
* a Matplotlib **//Artist//** or //Patch// is //something// (e.g a line, a group of markers, text, the legend...) plotted on the Figure/Axis | * a Matplotlib **//Artist//** or //Patch// is //something// (e.g a line, a group of markers, text, the legend...) plotted on the Figure/Axis | ||
Line 90: | Line 104: | ||
- you can use **transparency** to partially show what is behind some markers or other objects. Many //artists// accept the ''alpha'' parameter where ''0.0'' means that the object is completely transparent, and ''1.0'' means completely opaque\\ e.g. ''my_plot.scatter(..., alpha=0.7)'' | - you can use **transparency** to partially show what is behind some markers or other objects. Many //artists// accept the ''alpha'' parameter where ''0.0'' means that the object is completely transparent, and ''1.0'' means completely opaque\\ e.g. ''my_plot.scatter(..., alpha=0.7)'' | ||
- sometimes the results of the python/matplolib commands are displayed immediately, sometimes not. It depends if you are in [[http://matplotlib.org/faq/usage_faq.html#what-is-interactive-mode|interactive or non-interactive]] mode | - sometimes the results of the python/matplolib commands are displayed immediately, sometimes not. It depends if you are in [[http://matplotlib.org/faq/usage_faq.html#what-is-interactive-mode|interactive or non-interactive]] mode | ||
- | - if your matplotlib is executed in a batch script, it will generate an error when trying to create (''show()'') a plot, because matplotlib expects to be able to display the figure on a screen by default. | + | - if your matplotlib is executed in a batch script, it will generate an error when trying to create (''show()'') a plot, because matplotlib expects to be able to display the figure on a screen (with an X server running) by default.\\ Find out how to deal with this in [[other:python:matplotlib_by_jyp#creating_a_plot_offline|Creating a plot offline]]. |
- | * Check how you can [[https://matplotlib.org/faq/howto_faq.html?highlight=web#generate-images-without-having-a-window-appear|generate images offline]] | + | |
- the documentation may mention [[http://matplotlib.org/faq/usage_faq.html#what-is-a-backend|backends]]. What?? Basically, you use python commands to create a plot, and the backend is the //thing// that will render your plot on the screen or in a file (png, pdf, etc...) | - the documentation may mention [[http://matplotlib.org/faq/usage_faq.html#what-is-a-backend|backends]]. What?? Basically, you use python commands to create a plot, and the backend is the //thing// that will render your plot on the screen or in a file (png, pdf, etc...) | ||
- Read the [[https://github.com/rougier/matplotlib-tutorial|Matplotlib tutorial by Nicolas Rougier]] | - Read the [[https://github.com/rougier/matplotlib-tutorial|Matplotlib tutorial by Nicolas Rougier]] | ||
Line 103: | Line 116: | ||
* The ''plot'' function will be faster for scatterplots where markers don't vary in size or color | * The ''plot'' function will be faster for scatterplots where markers don't vary in size or color | ||
* [[https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.contourf.html|contour(...) and contourf(...)]]: draw contour lines and filled contours | * [[https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.contourf.html|contour(...) and contourf(...)]]: draw contour lines and filled contours | ||
- | * X and Y axes parameters | + | * **X and Y axes parameters** (see also [[https://matplotlib.org/examples/showcase/anatomy.html|Anatomy of a figure]]): |
- | * Axis range: ''my_plot.set_xlim(x_leftmost_value, x_rightmost_value)'' | + | * **Axis range**: ''my_plot.set_xlim(x_leftmost_value, x_rightmost_value)'' |
* Use the leftmost and rightmost values to specify the orientation of the axis (i.e the rightmost value can be smaller than the leftmost) | * Use the leftmost and rightmost values to specify the orientation of the axis (i.e the rightmost value can be smaller than the leftmost) | ||
- | * Axis label: ''my_plot.set_xlabel(x_label_string, fontsize=axis_label_fontsize)'' | + | * **Axis label**: ''my_plot.set_xlabel(x_label_string, fontsize=axis_label_fontsize)'' |
* Use the extra labelpad parameter to move the label closer (negative value) to the axis or farther (positive value): e.g. ''my_plot.set_xlabel('A closer label', labelpad=-20'' | * Use the extra labelpad parameter to move the label closer (negative value) to the axis or farther (positive value): e.g. ''my_plot.set_xlabel('A closer label', labelpad=-20'' | ||
- | * Major (and minor) tick marks location: ''my_plot.set_xticks(x_ticks_values, minor=False)'' | + | * Major (and minor) **tick marks location**: ''my_plot.set_xticks(x_ticks_values, minor=False)'' |
* Use an empty list if you don't want tick marks: ''my_plot.set_xticks([])'' | * Use an empty list if you don't want tick marks: ''my_plot.set_xticks([])'' | ||
- | * Tick labels (if you don't want the default values): ''my_plot.set_xticklabels(x_ticks_labels, minor=False, fontsize=ticklabels_fontsize)'' | + | * **Tick labels** (if you don't want the default values): ''my_plot.set_xticklabels(x_ticks_labels, minor=False, fontsize=ticklabels_fontsize)'' |
* ''x_ticks_labels'' is a list of strings that has the same length as ''x_ticks_values''. Use an empty string in the positions where you don't want a label | * ''x_ticks_labels'' is a list of strings that has the same length as ''x_ticks_values''. Use an empty string in the positions where you don't want a label | ||
* Many more options for ticks, labels, orientation, ... | * Many more options for ticks, labels, orientation, ... | ||
+ | * **Grid lines**: | ||
+ | * Their position is determined by the values used for ''set_xticks'' and ''set_yticks'' | ||
+ | * Activate all (horizontal **and** vertical) grid lines with: ''my_plot.grid(True, linestyle="%%--%%", linewidth=0.5, color='.25',zorder=some_value)''\\ You can adjust the ''zorder'' value to determine if the grid lines should be above or below other parts of the plot! | ||
+ | * Plot only the horizontal **or** vertical lines with:\\ ''ax.yaxis.grid(True)''\\ or ''ax.xaxis.grid(True)'' | ||
+ | * Note: <wrap hi>special case of //cartopy// plots</wrap>: the location of the gridlines, and the properties of the associated labels are determined by ''myplot.gridlines''! See [[https://scitools.org.uk/cartopy/docs/latest/matplotlib/gridliner.html|Cartopy map gridlines and tick labels]] | ||
* [[https://matplotlib.org/api/_as_gen/matplotlib.lines.Line2D.html|line]] parameters | * [[https://matplotlib.org/api/_as_gen/matplotlib.lines.Line2D.html|line]] parameters | ||
* ''linestyle'': ''solid'', ''None'', [[https://matplotlib.org/api/_as_gen/matplotlib.lines.Line2D.html#matplotlib.lines.Line2D.set_linestyle|other]] ([[https://matplotlib.org/examples/lines_bars_and_markers/line_styles_reference.html|default styles example]], [[https://matplotlib.org/examples/lines_bars_and_markers/linestyles.html|custom styles example]]) | * ''linestyle'': ''solid'', ''None'', [[https://matplotlib.org/api/_as_gen/matplotlib.lines.Line2D.html#matplotlib.lines.Line2D.set_linestyle|other]] ([[https://matplotlib.org/examples/lines_bars_and_markers/line_styles_reference.html|default styles example]], [[https://matplotlib.org/examples/lines_bars_and_markers/linestyles.html|custom styles example]]) | ||
Line 173: | Line 191: | ||
===== Misc Matplotlib tricks ===== | ===== Misc Matplotlib tricks ===== | ||
- | * Specifying the background color of a plot (e.g. when plotting a masked variable and you don't want the masked areas to be white) | + | ==== Creating a plot offline ==== |
- | * ''# make the background dark gray (call this before the contourf)''\\ ''plt.gca().patch.set_color('.25')''\\ ''plt.contourf(d)''\\ ''plt.show()'' | + | |
- | * [[https://stackoverflow.com/questions/9797520/masking-part-of-a-contourf-plot-in-matplotlib|trick source]] | + | You may need to create a plot offline when your network connection is not good enough, you don't have an X server running to display the plot (possibly because the script is running on a cluster), etc... This is easily done with the following code: |
+ | |||
+ | <code># offline_plot = False | ||
+ | offline_plot = True | ||
+ | |||
+ | import matplotlib as mpl | ||
+ | if offline_plot: | ||
+ | # Define the graphic back-end BEFORE importing pyplot | ||
+ | mpl.use('Agg') | ||
+ | |||
+ | # Import the rest of the matplotlib based modules | ||
+ | import matplotlib.pyplot as plt | ||
+ | |||
+ | [ ...your actual code... ] | ||
+ | |||
+ | # Done at last! Save the result | ||
+ | my_page.savefig(out_name, dpi=300, transparent=True, bbox_inches='tight') | ||
+ | |||
+ | if not offline_plot: | ||
+ | # Enter the interactive mode to display the plot | ||
+ | plt.show()</code> | ||
+ | |||
+ | Note: see also [[https://matplotlib.org/gallery/user_interfaces/canvasagg.html|CanvasAgg demo]] for a pure offline plot, and [[https://matplotlib.org/faq/howto_faq.html?highlight=web#howto-webapp|How to use Matplotlib in a web application server]]. But the code above is much easier! | ||
+ | |||
+ | ==== Specifying the background color of a plot ==== | ||
+ | |||
+ | e.g. You need to plot a masked variable, but you don't want the masked areas to be white | ||
+ | |||
+ | <code># make the background dark gray (call this before the contourf) | ||
+ | plt.gca().patch.set_color('.25') | ||
+ | plt.contourf(d) | ||
+ | plt.show()</code> | ||
+ | |||
+ | [[https://stackoverflow.com/questions/9797520/masking-part-of-a-contourf-plot-in-matplotlib|trick source]] | ||