-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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 | ||
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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 for close. It's more clear and (currently) even faster.
3.12 s ± 53.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
|
||
.. _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. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This pattern should be discussed. IMO
Usage patterns:
See also #9111 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Proposed rewording: