|
| 1 | + |
| 2 | +What's new in Matplotlib 3.1 |
| 3 | +============================ |
| 4 | + |
| 5 | +For a list of all of the issues and pull requests since the last |
| 6 | +revision, see the :ref:`github-stats`. |
| 7 | + |
| 8 | +.. contents:: Table of Contents |
| 9 | + :depth: 4 |
| 10 | + |
| 11 | +.. toctree:: |
| 12 | + :maxdepth: 4 |
| 13 | + |
| 14 | +New Features |
| 15 | +------------ |
| 16 | + |
| 17 | +`~.dates.ConciseDateFormatter` |
| 18 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 19 | + |
| 20 | +The automatic date formatter used by default can be quite verbose. A new |
| 21 | +formatter can be accessed that tries to make the tick labels appropriately |
| 22 | +concise. |
| 23 | + |
| 24 | + .. plot:: |
| 25 | + |
| 26 | + import datetime |
| 27 | + import matplotlib.pyplot as plt |
| 28 | + import matplotlib.dates as mdates |
| 29 | + import numpy as np |
| 30 | + |
| 31 | + # make a timeseries... |
| 32 | + base = datetime.datetime(2005, 2, 1) |
| 33 | + dates = np.array([base + datetime.timedelta(hours= 2 * i) |
| 34 | + for i in range(732)]) |
| 35 | + N = len(dates) |
| 36 | + np.random.seed(19680801) |
| 37 | + y = np.cumsum(np.random.randn(N)) |
| 38 | + |
| 39 | + lims = [(np.datetime64('2005-02'), np.datetime64('2005-04')), |
| 40 | + (np.datetime64('2005-02-03'), np.datetime64('2005-02-15')), |
| 41 | + (np.datetime64('2005-02-03 11:00'), np.datetime64('2005-02-04 13:20'))] |
| 42 | + fig, axs = plt.subplots(3, 1, constrained_layout=True) |
| 43 | + for nn, ax in enumerate(axs): |
| 44 | + # activate the formatter here. |
| 45 | + locator = mdates.AutoDateLocator() |
| 46 | + formatter = mdates.ConciseDateFormatter(locator) |
| 47 | + ax.xaxis.set_major_locator(locator) |
| 48 | + ax.xaxis.set_major_formatter(formatter) |
| 49 | + |
| 50 | + ax.plot(dates, y) |
| 51 | + ax.set_xlim(lims[nn]) |
| 52 | + axs[0].set_title('Concise Date Formatter') |
| 53 | + |
| 54 | + plt.show() |
| 55 | + |
| 56 | +Secondary x/y Axis support |
| 57 | +~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 58 | + |
| 59 | +A new method provides the ability to add a second axis to an existing |
| 60 | +axes via `.Axes.secondary_xaxis` and `.Axes.secondary_yaxis`. See |
| 61 | +:doc:`/gallery/subplots_axes_and_figures/secondary_axis` for examples. |
| 62 | + |
| 63 | +.. plot:: |
| 64 | + |
| 65 | + import matplotlib.pyplot as plt |
| 66 | + |
| 67 | + fig, ax = plt.subplots(figsize=(5, 3)) |
| 68 | + ax.plot(range(360)) |
| 69 | + ax.secondary_xaxis('top', functions=(np.deg2rad, np.rad2deg)) |
| 70 | + |
| 71 | + |
| 72 | +`~.scale.FuncScale` for arbitrary axes scales |
| 73 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 74 | + |
| 75 | +A new `~.scale.FuncScale` class was added (and `~.scale.FuncTransform`) |
| 76 | +to allow the user to have arbitrary scale transformations without having to |
| 77 | +write a new subclass of `~.scale.ScaleBase`. This can be accessed by:: |
| 78 | + |
| 79 | + ax.set_yscale('function', functions=(forward, inverse)) |
| 80 | + |
| 81 | +where ``forward`` and ``inverse`` are callables that return the scale |
| 82 | +transform and its inverse. See the last example in |
| 83 | +:doc:`/gallery/scales/scales`. |
| 84 | + |
| 85 | + |
| 86 | +Legend for scatter |
| 87 | +~~~~~~~~~~~~~~~~~~ |
| 88 | + |
| 89 | +A new method for creating legends for scatter plots has been |
| 90 | +introduced. Previously, in order to obtain a legend for a |
| 91 | +:meth:`~.axes.Axes.scatter` plot, one could either plot several |
| 92 | +scatters, each with an individual label, or create proxy artists to |
| 93 | +show in the legend manually. Now, |
| 94 | +:class:`~.collections.PathCollection` provides a method |
| 95 | +:meth:`~.collections.PathCollection.legend_elements` to obtain the |
| 96 | +handles and labels for a scatter plot in an automated way. This makes |
| 97 | +creating a legend for a scatter plot as easy as |
| 98 | + |
| 99 | +.. plot :: |
| 100 | +
|
| 101 | + scatter = plt.scatter([1,2,3], [4,5,6], c=[7,2,3]) |
| 102 | + plt.legend(*scatter.legend_elements()) |
| 103 | +
|
| 104 | +An example can be found in :ref:`automatedlegendcreation`. |
| 105 | + |
| 106 | + |
| 107 | +Matplotlib no longer requires framework app build on MacOSX backend |
| 108 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 109 | + |
| 110 | +Previous versions of matplotlin required a Framework build of python to |
| 111 | +work. The app type was updated to no longer require this, so the MacOSX |
| 112 | +backend should work with non-framework python. |
| 113 | + |
| 114 | + |
| 115 | +This also adds support for the MacOSX backend for PyPy3. |
| 116 | + |
| 117 | + |
| 118 | +Figure, FigureCanvas, and Backends |
| 119 | +---------------------------------- |
| 120 | + |
| 121 | +Figure.frameon is now a direct proxy for the Figure patch visibility state |
| 122 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 123 | +Accessing ``Figure.frameon`` (including via ``get_frameon`` and ``set_frameon`` |
| 124 | +now directly forwards to the visibility of the underlying Rectangle artist |
| 125 | +(``Figure.patch.get_frameon``, ``Figure.patch.set_frameon``). |
| 126 | + |
| 127 | + |
| 128 | +*pil_kwargs* argument added to savefig |
| 129 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 130 | + |
| 131 | +Matplotlib uses Pillow to handle saving to the JPEG and TIFF formats. The |
| 132 | +`~.Figure.savefig()` function gained a *pil_kwargs* keyword argument, which can |
| 133 | +be used to forward arguments to Pillow's `pillow.Image.save()`. |
| 134 | + |
| 135 | +The *pil_kwargs* argument can also be used when saving to PNG. In that case, |
| 136 | +Matplotlib also uses Pillow's `pillow.Image.save()` instead of going through its |
| 137 | +own builtin PNG support. |
| 138 | + |
| 139 | + |
| 140 | +Add ``inaxes`` method to `.FigureCanvasBase` |
| 141 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 142 | + |
| 143 | +The `.FigureCanvasBase` class has now an `~.FigureCanvasBase.inaxes` |
| 144 | +method to check whether a point is in an axes and returns the topmost |
| 145 | +axes, else None. |
| 146 | + |
| 147 | +cairo backend defaults to pycairo instead of cairocffi |
| 148 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 149 | + |
| 150 | +This leads to faster import/runtime performance in some cases. The backend |
| 151 | +will fall back to cairocffi in case pycairo isn't available. |
| 152 | + |
| 153 | + |
| 154 | +Axes and Artists |
| 155 | +---------------- |
| 156 | + |
| 157 | +axes_grid1 and axisartist Axes no longer draw spines twice |
| 158 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 159 | + |
| 160 | +Previously, spines of `.axes_grid1` and `.axisartist` Axes would be drawn twice, |
| 161 | +leading to a "bold" appearance. This is no longer the case. |
| 162 | + |
| 163 | + |
| 164 | +Return type of ArtistInspector.get_aliases changed |
| 165 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 166 | +`.ArtistInspector.get_aliases` previously returned the set of aliases as |
| 167 | +``{fullname: {alias1: None, alias2: None, ...}}``. The dict-to-None mapping |
| 168 | +was used to simulate a set in earlier versions of Python. It has now been |
| 169 | +replaced by a set, i.e. ``{fullname: {alias1, alias2, ...}}``. |
| 170 | + |
| 171 | +This value is also stored in `.ArtistInspector.aliasd`, which has likewise |
| 172 | +changed. |
| 173 | + |
| 174 | + |
| 175 | +`.ConnectionPatch` accepts arbitrary transforms |
| 176 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 177 | + |
| 178 | +Alternatively to strings like ``"data"`` or ``"axes fraction"`` |
| 179 | +`ConnectionPatch` now accepts any `~matplotlib.transforms.Transform` |
| 180 | +as input for the ``coordsA`` and ``coordsB`` argument. This allows to |
| 181 | +draw lines between points defined in different user defined coordinate |
| 182 | +systems. Also see the :doc:`Connect Simple01 example |
| 183 | +</gallery/userdemo/connect_simple01>`. |
| 184 | + |
| 185 | + |
| 186 | +mplot3d Line3D now allows {set,get}_data_3d |
| 187 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 188 | + |
| 189 | +Lines created with the 3d projection in mplot3d can now access the |
| 190 | +data using `~.mplot3d.art3d.Line3D.get_data_3d()` which returns a |
| 191 | +tuple of array_likes containing the (x, y, z) data. The equivalent |
| 192 | +`~.mplot3d.art3d.Line3D.set_data_3d` can be used to modify the data of |
| 193 | +an existing Line3D. |
| 194 | + |
| 195 | + |
| 196 | +``Axes3D.voxels`` now shades the resulting voxels |
| 197 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 198 | + |
| 199 | +The :meth:`~mpl_toolkits.mplot3d.Axes3D.voxels` method now takes a |
| 200 | +*shade* parameter that defaults to `True`. This shades faces based |
| 201 | +on their orientation, behaving just like the matching parameters to |
| 202 | +:meth:`~mpl_toolkits.mplot3d.Axes3D.trisurf` and |
| 203 | +:meth:`~mpl_toolkits.mplot3d.Axes3D.bar3d`. The plot below shows how |
| 204 | +this affects the output. |
| 205 | + |
| 206 | +.. plot:: |
| 207 | + |
| 208 | + import matplotlib.pyplot as plt |
| 209 | + import numpy as np |
| 210 | + |
| 211 | + # prepare some coordinates |
| 212 | + x, y, z = np.indices((8, 8, 8)) |
| 213 | + |
| 214 | + # draw cuboids in the top left and bottom right corners, and a link between them |
| 215 | + cube1 = (x < 3) & (y < 3) & (z < 3) |
| 216 | + cube2 = (x >= 5) & (y >= 5) & (z >= 5) |
| 217 | + link = abs(x - y) + abs(y - z) + abs(z - x) <= 2 |
| 218 | + |
| 219 | + # combine the objects into a single boolean array |
| 220 | + voxels = cube1 | cube2 | link |
| 221 | + |
| 222 | + # set the colors of each object |
| 223 | + colors = np.empty(voxels.shape, dtype=object) |
| 224 | + colors[link] = 'red' |
| 225 | + colors[cube1] = 'blue' |
| 226 | + colors[cube2] = 'green' |
| 227 | + |
| 228 | + # and plot everything |
| 229 | + fig = plt.figure(figsize=plt.figaspect(0.5)) |
| 230 | + ax, ax_shaded = fig.subplots(1, 2, subplot_kw=dict(projection='3d')) |
| 231 | + ax.voxels(voxels, facecolors=colors, edgecolor='k', shade=False) |
| 232 | + ax.set_title("Unshaded") |
| 233 | + ax_shaded.voxels(voxels, facecolors=colors, edgecolor='k', shade=True) |
| 234 | + ax_shaded.set_title("Shaded (default)") |
| 235 | + |
| 236 | + plt.show() |
| 237 | + |
| 238 | +Axis and Ticks |
| 239 | +-------------- |
| 240 | + |
| 241 | +Added `.Axis.get_inverted` and `.Axis.set_inverted` |
| 242 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 243 | +The `.Axis.get_inverted` and `.Axis.set_inverted` methods query and set whether |
| 244 | +the axis uses "inverted" orientation (i.e. increasing to the left for the |
| 245 | +x-axis and to the bottom for the y-axis). |
| 246 | + |
| 247 | +They perform tasks similar to `.Axes.xaxis_inverted`, |
| 248 | +`.Axes.yaxis_inverted`, `.Axes.invert_xaxis`, and |
| 249 | +`.Axes.invert_yaxis`, with the specific difference that |
| 250 | +`.Axes..set_inverted` makes it easier to set the invertedness of an |
| 251 | +axis regardless of whether it had previously been inverted before. |
| 252 | + |
| 253 | +Adjust default minor tick spacing |
| 254 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 255 | + |
| 256 | +Default minor tick spacing was changed from 0.625 to 0.5 for major ticks spaced |
| 257 | +2.5 units apart. |
| 258 | + |
| 259 | + |
| 260 | +`.EngFormatter` now accepts `usetex`, `useMathText` as keyword only arguments |
| 261 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 262 | + |
| 263 | +A public API has been added to `EngFormatter` to control how the |
| 264 | +numbers in the ticklabels will be rendered. By default, |
| 265 | +``useMathText`` evaluates to |
| 266 | +:rc:`axes.formatter.use_mathtext'` and ``usetex`` evaluates |
| 267 | +to :rc:`'text.usetex'`. |
| 268 | + |
| 269 | +If either is `True` then the numbers will be encapsulated by ``$`` |
| 270 | +signs. When using ``TeX`` this implies that the numbers will be shown |
| 271 | +in TeX's math font. When using mathtext, the ``$`` signs around |
| 272 | +numbers will ensure unicode rendering (as implied by mathtext). This |
| 273 | +will make sure that the minus signs in the ticks are rendered as the |
| 274 | +unicode=minus (U+2212) when using mathtext (without relying on the |
| 275 | +`~.Fomatter.fix_minus` method). |
| 276 | + |
| 277 | + |
| 278 | + |
| 279 | +Animation and Interactivity |
| 280 | +--------------------------- |
| 281 | + |
| 282 | +Support for forward/backward mouse buttons |
| 283 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 284 | + |
| 285 | +Figure managers now support a ``button_press`` event for mouse |
| 286 | +buttons, similar to the ``key_press`` events. This allows binding |
| 287 | +actions to mouse buttons (see `.MouseButton`) The first application of |
| 288 | +this mechanism is support of forward/backward mouse buttons in figures |
| 289 | +created with the Qt5 backend. |
| 290 | + |
| 291 | + |
| 292 | +*progress_callback* argument to `~.Animation.save()` |
| 293 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 294 | + |
| 295 | +The method `.Animation.save` gained an optional |
| 296 | +*progress_callback* argument to notify the saving progress. |
| 297 | + |
| 298 | + |
| 299 | +Add ``cache_frame_data`` keyword-only argument into `.animation.FuncAnimation` |
| 300 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 301 | + |
| 302 | +`.matplotlib.animation.FuncAnimation` has been caching frame data by |
| 303 | +default; however, this caching is not ideal in certain cases e.g. When |
| 304 | +`.FuncAnimation` needs to be only drawn(not saved) interactively and |
| 305 | +memory required by frame data is quite large. By adding |
| 306 | +*cache_frame_data* keyword-only argument, users can now disable this |
| 307 | +caching; thereby, this new argument provides a fix for issue |
| 308 | +:ghissue:`8528`. |
| 309 | + |
| 310 | + |
| 311 | +Endless Looping GIFs with PillowWriter |
| 312 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 313 | + |
| 314 | +We acknowledge that most people want to watch a gif more than |
| 315 | +once. Saving an animation as a gif with PillowWriter now produces an |
| 316 | +endless looping gif. |
| 317 | + |
| 318 | + |
| 319 | +Adjusted `.matplotlib.widgets.Slider` to have vertical orientation |
| 320 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 321 | + |
| 322 | +The :class:`matplotlib.widgets.Slider` widget now takes an optional |
| 323 | +argument *orientation* which indicates the direction |
| 324 | +(``'horizontal'`` or ``'vertical'``) that the slider should take. |
| 325 | + |
| 326 | +Improved formatting of image values under cursor when a colorbar is present |
| 327 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 328 | + |
| 329 | +When a colorbar is present, its formatter is now used to format the image |
| 330 | +values under the mouse cursor in the status bar. For example, for an image |
| 331 | +displaying the values 10,000 and 10,001, the statusbar will now (using default |
| 332 | +settings) display the values as ``10000`` and ``10001``), whereas both values |
| 333 | +were previously displayed as ``1e+04``. |
| 334 | + |
| 335 | +MouseEvent button attribute is now an IntEnum |
| 336 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 337 | + |
| 338 | +The :attr:`button` attribute of `~.MouseEvent` instances can take the values |
| 339 | +None, 1 (left button), 2 (middle button), 3 (right button), "up" (scroll), and |
| 340 | +"down" (scroll). For better legibility, the 1, 2, and 3 values are now |
| 341 | +represented using the `IntEnum` class `matplotlib.backend_bases.MouseButton`, |
| 342 | +with the values `MouseButton.LEFT` (``== 1``), `MouseButton.MIDDLE` (``== 2``), |
| 343 | +and `MouseButton.RIGHT` (``== 3``). |
| 344 | + |
| 345 | + |
| 346 | +Configuration, Install, and Development |
| 347 | +--------------------------------------- |
| 348 | + |
| 349 | +The MATPLOTLIBRC environment variable can now point to any "file" path |
| 350 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 351 | +This includes device files; in particular, on Unix systems, one can set |
| 352 | +``MATPLOTLIBRC`` to ``/dev/null`` to ignore the user's matplotlibrc file and |
| 353 | +fall back to Matplotlib's defaults. |
| 354 | + |
| 355 | +As a reminder, if ``MATPLOTLIBRC`` points to a directory, Matplotlib will try |
| 356 | +to load the matplotlibrc file from ``$MATPLOTLIBRC/matplotlibrc``. |
| 357 | + |
| 358 | + |
| 359 | +Allow LaTeX code ``pgf.preamble`` and ``text.latex.preamble`` in MATPLOTLIBRC file |
| 360 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 361 | + |
| 362 | +Previously, the rc file keys :rc:`pgf.preamble` and |
| 363 | +:rc:`text.latex.preamble` were parsed using commas as separators. This |
| 364 | +would break valid LaTeX code, such as:: |
| 365 | + |
| 366 | + \usepackage[protrusion=true, expansion=false]{microtype} |
| 367 | + |
| 368 | +The parsing has been modified to pass the complete line to the LaTeX |
| 369 | +system, keeping all commas. Passing a list of strings from within a |
| 370 | +Python script still works as it used to. |
| 371 | + |
| 372 | + |
| 373 | + |
| 374 | +New logging API |
| 375 | +~~~~~~~~~~~~~~~ |
| 376 | + |
| 377 | +`matplotlib.set_loglevel` / `.pyplot.set_loglevel` can be called to |
| 378 | +display more (or less) detailed logging output. |
0 commit comments