.. _performance:

Performance
===========

Whether exploring data in interactive mode or programmatically
saving lots of plots, rendering performance can be a challenging
bottleneck in your pipeline. Matplotlib provides multiple
ways to greatly reduce rendering time at the cost of a slight
change (to a settable tolerance) in your plot's appearance.
The methods available to reduce rendering time depend on the
type of plot that is being created.

Line segment simplification
---------------------------

For plots that have line segments (e.g. typical line plots, outlines
of polygons, etc.), rendering performance can be controlled by
:rc:`path.simplify` and :rc:`path.simplify_threshold`, which
can be defined e.g. in the :file:`matplotlibrc` file (see
:doc:`/tutorials/introductory/customizing` for more information about
the :file:`matplotlibrc` file). :rc:`path.simplify` is a Boolean
indicating whether or not line segments are simplified at all.
:rc:`path.simplify_threshold` controls how much line segments are simplified;
higher thresholds result in quicker rendering.

The following script will first display the data without any
simplification, and then display the same data with simplification.
Try interacting with both of them::

  import numpy as np
  import matplotlib.pyplot as plt
  import matplotlib as mpl

  # Setup, and create the data to plot
  y = np.random.rand(100000)
  y[50000:] *= 2
  y[np.geomspace(10, 50000, 400).astype(int)] = -1
  mpl.rcParams['path.simplify'] = True

  mpl.rcParams['path.simplify_threshold'] = 0.0
  plt.plot(y)
  plt.show()

  mpl.rcParams['path.simplify_threshold'] = 1.0
  plt.plot(y)
  plt.show()

Matplotlib currently defaults to a conservative simplification
threshold of ``1/9``. To change default settings to use a different
value, change the :file:`matplotlibrc` file. Alternatively, users
can create a new style for interactive plotting (with maximal
simplification) and another style for publication quality plotting
(with minimal simplification) and activate them as necessary. See
:doc:`/tutorials/introductory/customizing` for instructions on
how to perform these actions.

The simplification works by iteratively merging line segments
into a single vector until the next line segment's perpendicular
distance to the vector (measured in display-coordinate space)
is greater than the ``path.simplify_threshold`` parameter.

.. note::
  Changes related to how line segments are simplified were made
  in version 2.1. Rendering time will still be improved by these
  parameters prior to 2.1, but rendering time for some kinds of
  data will be vastly improved in versions 2.1 and greater.

Marker subsampling
------------------

Markers can also be simplified, albeit less robustly than line
segments. Marker subsampling is only available to `.Line2D` objects
(through the ``markevery`` property). Wherever `.Line2D` construction
parameters are passed through, such as `.pyplot.plot` and `.Axes.plot`,
the ``markevery`` parameter can be used::

  plt.plot(x, y, markevery=10)

The ``markevery`` argument allows for naive subsampling, or an
attempt at evenly spaced (along the *x* axis) sampling. See the
:doc:`/gallery/lines_bars_and_markers/markevery_demo`
for more information.

Splitting lines into smaller chunks
-----------------------------------

If you are using the Agg backend (see :ref:`what-is-a-backend`),
then you can make use of :rc:`agg.path.chunksize`
This allows users to specify a chunk size, and any lines with
greater than that many vertices will be split into multiple
lines, each of which has no more than ``agg.path.chunksize``
many vertices. (Unless ``agg.path.chunksize`` is zero, in
which case there is no chunking.) For some kind of data,
chunking the line up into reasonable sizes can greatly
decrease rendering time.

The following script will first display the data without any
chunk size restriction, and then display the same data with
a chunk size of 10,000. The difference can best be seen when
the figures are large, try maximizing the GUI and then
interacting with them::

  import numpy as np
  import matplotlib.pyplot as plt
  import matplotlib as mpl
  mpl.rcParams['path.simplify_threshold'] = 1.0

  # Setup, and create the data to plot
  y = np.random.rand(100000)
  y[50000:] *= 2
  y[np.geomspace(10, 50000, 400).astype(int)] = -1
  mpl.rcParams['path.simplify'] = True

  mpl.rcParams['agg.path.chunksize'] = 0
  plt.plot(y)
  plt.show()

  mpl.rcParams['agg.path.chunksize'] = 10000
  plt.plot(y)
  plt.show()

Legends
-------

The default legend behavior for axes attempts to find the location
that covers the fewest data points (``loc='best'``). This can be a
very expensive computation if there are lots of data points. In
this case, you may want to provide a specific location.

Using the *fast* style
----------------------

The *fast* style can be used to automatically set
simplification and chunking parameters to reasonable
settings to speed up plotting large amounts of data.
The following code runs it::

  import matplotlib.style as mplstyle
  mplstyle.use('fast')

It is very lightweight, so it works well with other
styles. Be sure the fast style is applied last
so that other styles do not overwrite the settings::

  mplstyle.use(['dark_background', 'ggplot', 'fast'])
