Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Created some prose documentation about best practices #11657

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions doc/faq/best_practices.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@

.. _best-practices:

**************
Best Practices
**************

.. contents::
:backlinks: none


.. _best-practice-many-figures:

How to save many figures
========================

You have figured out how to create a plot from your data and either
show or save the figure, possibly following the tutorial :doc:`/tutorials/introductory/lifecycle`. It looks great and wonderful.::

data = data_loading_function(fname)
fig, ax = plt.subplots()
ax.plot(data.x, data.y, label=data.name)
ax.legend()
ax.set(xlabel='Time [months]', ylabel='Profits [$]', title='Sales Forecast')
fig.savefig('sales_forecast.png')

Now you want to make this plot for each of your data files. One might just
put the above code into a for-loop over all of your filenames. However, you
will eventually encounter a warning ``More than X figures hae been opened.``.
With enough looping, Python can eventually run out of memory, and calling
Copy link
Member

@timhoffm timhoffm Jul 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Proposed rewording:

With enough looping, Python can eventually run out of memory because the pyplot figure manger keeps a reference to every created figure.

garbage collection will not help. This is because figures created through
``pyplot`` will also be stored in within the module.

So, the best practice for saving many figures is to recycle the figure.::

fig = plt.figure()
for fname in data_file_names:
data = data_loading_function(fname)
ax = fig.subplots()
ax.plot(data.x, data.y, label=data.name)
ax.legend()
ax.set(xlabel='Time [months]', ylabel='Profits [$]', title='Sales Forecast')
fig.savefig('sales_forecast_%s.png' % data.name)
fig.clear()

Now, only one figure object is ever made, and it is cleaned of any plots
at each iteration, so the memory usage will not grow.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... or just call plt.close("all") at the end of the loop. This avoids having to factor out figure creation from the main loop.

Copy link
Member

@timhoffm timhoffm Jul 15, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 for close. It's more clear and (currently) even faster.

%%timeit
fig = plt.figure()
for i in range(50):
    plt.plot([1, 3, 2])
    plt.savefig('test.png')
    fig.clear()

3.12 s ± 53.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%%timeit
for i in range(50):
    plt.figure()
    plt.plot([1, 3, 2])
    plt.savefig('test.png')
    plt.close()

2.75 s ± 27.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

It's also a good general reminder, not only with looping: Close your pyplot figures when your done.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What prompted me to make this document was when someone came to me with a script that was growing in memory significantly and they did something like this. I don't remember if they were calling plt.close() or not, but recycling the figure definitely solves that problem.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that #12450 is merged one can also just create Figure()s completely bypassing pyplot to avoid the issue; perhaps this should be coordinated with #12628/#13062.



.. _best-practice-plotting-functions:

Creating your own plotting functions
====================================

For nontrivial plots, it may make sense to create your own plotting
functions for easy reuse. Here are some guidelines:

* Have an ``ax`` positional argument as the first argument.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This pattern should be discussed.

IMO ax as the first positional argument is not a good choice. I would rather use something like:

def my_plotter(data1, data2, ax=None, param_dict=None):
    if ax is None:
        ax = plt.gca()
    ...

Usage patterns:

my_plotter(data1, data2)  # similar to pyplot
my_plotter(data1, data2, ax=ax)

See also #9111

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This definitely should be discussed, because there is some discussion of a new interface that would have this pattern as the default for all plotting functions. ping @tacaswell @efiring

* Avoid creating too many plotting elements in a single function.
* Avoid putting data processing together with plotting.
Instead, have the plotting function take the processed data
created by another function. It is OK to put these two functions
within a convenience function.
* Plotting functions should return all of the artists that it creates.
Doing so enables customizations by users.

Example::

def my_plotter(ax, data1, data2, param_dict=None):
"""
A helper function to make a graph

Parameters
----------
ax : Axes
The axes to draw to

data1 : array
The x data

data2 : array
The y data

param_dict : dict
Dictionary of kwargs to pass to ax.plot

Returns
-------
out : list
list of artists added
"""
if param_dict is None:
param_dict = {}
out = ax.plot(data1, data2, **param_dict)
return out

1 change: 1 addition & 0 deletions doc/faq/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ The Matplotlib FAQ
:maxdepth: 2

installing_faq.rst
best_practices.rst
howto_faq.rst
troubleshooting_faq.rst
environment_variables_faq.rst
Expand Down
3 changes: 2 additions & 1 deletion tutorials/introductory/usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,9 @@
# complete object-oriented interface will likely make the program easier
# to write and maintain.
#
# .. _custom_plot_function::
#
# Typically one finds oneself making the same plots over and over
# Typically, you will make the same plots over and over
# again, but with different data sets, which leads to needing to write
# specialized functions to do the plotting. The recommended function
# signature is something like:
Expand Down