Skip to content

Commit cbf990d

Browse files
committed
DOC: Add additional plots to 3.6 What's new
1 parent 6b57bca commit cbf990d

File tree

1 file changed

+219
-40
lines changed

1 file changed

+219
-40
lines changed

doc/users/prev_whats_new/whats_new_3.6.0.rst

+219-40
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,19 @@ Figure and Axes creation / management
1919

2020
The relative width and height of columns and rows in `~.Figure.subplots` and
2121
`~.Figure.subplot_mosaic` can be controlled by passing *height_ratios* and
22-
*width_ratios* keyword arguments to the methods. Previously, this required
23-
passing the ratios in *gridspec_kws* arguments.
22+
*width_ratios* keyword arguments to the methods:
23+
24+
.. plot::
25+
:include-source: true
26+
27+
fig = plt.figure()
28+
axs = fig.subplots(3, 1, sharex=True, height_ratios=[3, 1, 1])
29+
30+
Previously, this required passing the ratios in *gridspec_kw* arguments::
31+
32+
fig = plt.figure()
33+
axs = fig.subplots(3, 1, sharex=True,
34+
gridspec_kw=dict(height_ratios=[3, 1, 1]))
2435

2536
Constrained layout is no longer considered experimental
2637
-------------------------------------------------------
@@ -41,11 +52,31 @@ Compressed layout for fixed-aspect ratio Axes
4152
---------------------------------------------
4253

4354
Simple arrangements of Axes with fixed aspect ratios can now be packed together
44-
with ``fig, axs = plt.subplots(2, 3, layout='compressed')``. With
45-
``layout='tight'`` or ``'constrained'``, Axes with a fixed aspect ratio can
46-
leave large gaps between each other. Using the ``layout='compressed'`` layout
47-
reduces the space between the Axes, and adds the extra space to the outer
48-
margins. See :ref:`compressed_layout`.
55+
with ``fig, axs = plt.subplots(2, 3, layout='compressed')``.
56+
57+
With ``layout='tight'`` or ``'constrained'``, Axes with a fixed aspect ratio
58+
can leave large gaps between each other:
59+
60+
.. plot::
61+
62+
fig, axs = plt.subplots(2, 2, figsize=(5, 3),
63+
sharex=True, sharey=True, layout="constrained")
64+
for ax in axs.flat:
65+
ax.imshow([[0, 1], [2, 3]])
66+
fig.suptitle("fixed-aspect plots, layout='constrained'")
67+
68+
Using the ``layout='compressed'`` layout reduces the space between the Axes,
69+
and adds the extra space to the outer margins:
70+
71+
.. plot::
72+
73+
fig, axs = plt.subplots(2, 2, figsize=(5, 3),
74+
sharex=True, sharey=True, layout='compressed')
75+
for ax in axs.flat:
76+
ax.imshow([[0, 1], [2, 3]])
77+
fig.suptitle("fixed-aspect plots, layout='compressed'")
78+
79+
See :ref:`compressed_layout` for further details.
4980

5081
Layout engines may now be removed
5182
---------------------------------
@@ -64,6 +95,16 @@ with the previous layout engine.
6495
*axes_class* keyword arguments, so that subclasses of `matplotlib.axes.Axes`
6596
may be returned.
6697

98+
.. plot::
99+
:include-source: true
100+
101+
fig, ax = plt.subplots()
102+
103+
ax.plot([0, 2], [1, 2])
104+
105+
polar_ax = ax.inset_axes([0.75, 0.25, 0.2, 0.2], projection='polar')
106+
polar_ax.plot([0, 2], [1, 2])
107+
67108
WebP is now a supported output format
68109
-------------------------------------
69110

@@ -127,37 +168,72 @@ controlling the widths of the caps in box and whisker plots.
127168

128169
x = np.linspace(-7, 7, 140)
129170
x = np.hstack([-25, x, 25])
171+
capwidths = [0.01, 0.2]
172+
130173
fig, ax = plt.subplots()
131-
ax.boxplot([x, x], notch=True, capwidths=[0.01, 0.2])
174+
ax.boxplot([x, x], notch=True, capwidths=capwidths)
175+
ax.set_title(f'{capwidths=}')
132176

133177
Easier labelling of bars in bar plot
134178
------------------------------------
135179

136-
The *label* argument of `~.Axes.bar` can now be passed a list of labels for the
137-
bars.
180+
The *label* argument of `~.Axes.bar` and `~.Axes.barh` can now be passed a list
181+
of labels for the bars. The list must be the same length as *x* and labels the
182+
individual bars. Repeated labels are not de-duplicated and will cause repeated
183+
label entries, so this is best used when bars also differ in style (e.g., by
184+
passing a list to *color*, as below.)
138185

139-
.. code-block:: python
186+
.. plot::
187+
:include-source: true
140188

141189
x = ["a", "b", "c"]
142190
y = [10, 20, 15]
191+
color = ['C0', 'C1', 'C2']
143192

144193
fig, ax = plt.subplots()
145-
bar_container = ax.barh(x, y, label=x)
146-
[bar.get_label() for bar in bar_container]
194+
ax.bar(x, y, color=color, label=x)
195+
ax.legend()
147196

148197
New style format string for colorbar ticks
149198
------------------------------------------
150199

151200
The *format* argument of `~.Figure.colorbar` (and other colorbar methods) now
152201
accepts ``{}``-style format strings.
153202

203+
.. code-block:: python
204+
205+
fig, ax = plt.subplots()
206+
im = ax.imshow(z)
207+
fig.colorbar(im, format='{x:.2e}') # Instead of '%.2e'
208+
154209
Linestyles for negative contours may be set individually
155210
--------------------------------------------------------
156211

157212
The line style of negative contours may be set by passing the
158213
*negative_linestyles* argument to `.Axes.contour`. Previously, this style could
159214
only be set globally via :rc:`contour.negative_linestyles`.
160215

216+
.. plot::
217+
:include-source: true
218+
219+
delta = 0.025
220+
x = np.arange(-3.0, 3.0, delta)
221+
y = np.arange(-2.0, 2.0, delta)
222+
X, Y = np.meshgrid(x, y)
223+
Z1 = np.exp(-X**2 - Y**2)
224+
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
225+
Z = (Z1 - Z2) * 2
226+
227+
fig, axs = plt.subplots(1, 2)
228+
229+
CS = axs[0].contour(X, Y, Z, 6, colors='k')
230+
axs[0].clabel(CS, fontsize=9, inline=True)
231+
axs[0].set_title('Default negative contours')
232+
233+
CS = axs[1].contour(X, Y, Z, 6, colors='k', negative_linestyles='dotted')
234+
axs[1].clabel(CS, fontsize=9, inline=True)
235+
axs[1].set_title('Dotted negative contours')
236+
161237
ContourPy used for quad contour calculations
162238
--------------------------------------------
163239

@@ -193,6 +269,19 @@ The *markerfacecoloralt* parameter is now passed to the line plotter from
193269
passed to `.Line2D`, rather than claiming that all keyword arguments are passed
194270
on.
195271

272+
.. plot::
273+
:include-source: true
274+
275+
x = np.arange(0.1, 4, 0.5)
276+
y = np.exp(-x)
277+
278+
fig, ax = plt.subplots()
279+
ax.errorbar(x, y, xerr=0.2, yerr=0.4,
280+
linestyle=':', color='darkgrey',
281+
marker='o', markersize=20, fillstyle='left',
282+
markerfacecolor='tab:blue', markerfacecoloralt='tab:orange',
283+
markeredgecolor='tab:brown', markeredgewidth=2)
284+
196285
``streamplot`` can disable streamline breaks
197286
--------------------------------------------
198287

@@ -266,7 +355,40 @@ Rectangle patch rotation point
266355
------------------------------
267356

268357
The rotation point of the `~matplotlib.patches.Rectangle` can now be set to
269-
'xy', 'center' or a 2-tuple of numbers.
358+
'xy', 'center' or a 2-tuple of numbers using the *rotation_point* argument.
359+
360+
.. plot::
361+
362+
fig, ax = plt.subplots()
363+
364+
rect = plt.Rectangle((0, 0), 1, 1, facecolor='none', edgecolor='C0')
365+
ax.add_patch(rect)
366+
ax.annotate('Unrotated', (1, 0), color='C0',
367+
horizontalalignment='right', verticalalignment='top',
368+
xytext=(0, -3), textcoords='offset points')
369+
370+
for rotation_point, color in zip(['xy', 'center', (0.75, 0.25)],
371+
['C1', 'C2', 'C3']):
372+
ax.add_patch(
373+
plt.Rectangle((0, 0), 1, 1, facecolor='none', edgecolor=color,
374+
angle=45, rotation_point=rotation_point))
375+
376+
if rotation_point == 'center':
377+
point = 0.5, 0.5
378+
elif rotation_point == 'xy':
379+
point = 0, 0
380+
else:
381+
point = rotation_point
382+
ax.plot(point[:1], point[1:], color=color, marker='o')
383+
384+
label = f'{rotation_point}'
385+
if label == 'xy':
386+
label += ' (default)'
387+
ax.annotate(label, point, color=color,
388+
xytext=(3, 3), textcoords='offset points')
389+
390+
ax.set_aspect(1)
391+
ax.set_title('rotation_point options')
270392

271393
Colors and colormaps
272394
====================
@@ -317,7 +439,7 @@ It is now possible to set or get minor ticks using `.pyplot.xticks` and
317439
plt.figure()
318440
plt.plot([1, 2, 3, 3.5], [2, 1, 0, -0.5])
319441
plt.xticks([1, 2, 3], ["One", "Zwei", "Trois"])
320-
plt.xticks([1.414, 2.5, 3.142],
442+
plt.xticks([np.sqrt(2), 2.5, np.pi],
321443
[r"$\sqrt{2}$", r"$\frac{5}{2}$", r"$\pi$"], minor=True)
322444

323445
Legends
@@ -330,6 +452,14 @@ Legend can control alignment of title and handles
330452
the keyword argument *alignment*. You can also use `.Legend.set_alignment` to
331453
control the alignment on existing Legends.
332454

455+
.. plot::
456+
:include-source: true
457+
458+
fig, axs = plt.subplots(3, 1)
459+
for i, alignment in enumerate(['left', 'center', 'right']):
460+
axs[i].plot(range(10), label='test')
461+
axs[i].legend(title=f'{alignment=}', alignment=alignment)
462+
333463
*ncol* keyword argument to ``legend`` renamed to *ncols*
334464
--------------------------------------------------------
335465

@@ -358,15 +488,40 @@ rotation).
358488
.. plot::
359489
:include-source: true
360490

361-
from matplotlib.markers import MarkerStyle
491+
from matplotlib.markers import CapStyle, JoinStyle, MarkerStyle
362492
from matplotlib.transforms import Affine2D
363-
fig, ax = plt.subplots(figsize=(6, 1))
364-
fig.suptitle('New markers', fontsize=14)
365-
for col, (size, rot) in enumerate(zip([2, 5, 10], [0, 45, 90])):
493+
494+
fig, axs = plt.subplots(3, 1, layout='constrained')
495+
for ax in axs:
496+
ax.axis('off')
497+
ax.set_xlim(-0.5, 2.5)
498+
499+
axs[0].set_title('Cap styles', fontsize=14)
500+
for col, cap in enumerate(CapStyle):
501+
axs[0].plot(col, 0, markersize=32, markeredgewidth=8,
502+
marker=MarkerStyle('1', capstyle=cap))
503+
# Show the marker edge for comparison with the cap.
504+
axs[0].plot(col, 0, markersize=32, markeredgewidth=1,
505+
markerfacecolor='none', markeredgecolor='lightgrey',
506+
marker=MarkerStyle('1'))
507+
axs[0].annotate(cap.name, (col, 0),
508+
xytext=(20, -5), textcoords='offset points')
509+
510+
axs[1].set_title('Join styles', fontsize=14)
511+
for col, join in enumerate(JoinStyle):
512+
axs[1].plot(col, 0, markersize=32, markeredgewidth=8,
513+
marker=MarkerStyle('*', joinstyle=join))
514+
# Show the marker edge for comparison with the join.
515+
axs[1].plot(col, 0, markersize=32, markeredgewidth=1,
516+
markerfacecolor='none', markeredgecolor='lightgrey',
517+
marker=MarkerStyle('*'))
518+
axs[1].annotate(join.name, (col, 0),
519+
xytext=(20, -5), textcoords='offset points')
520+
521+
axs[2].set_title('Arbitrary transforms', fontsize=14)
522+
for col, (size, rot) in enumerate(zip([2, 5, 7], [0, 45, 90])):
366523
t = Affine2D().rotate_deg(rot).scale(size)
367-
ax.plot(col, 0, marker=MarkerStyle("*", transform=t))
368-
ax.axis("off")
369-
ax.set_xlim(-0.1, 2.4)
524+
axs[2].plot(col, 0, marker=MarkerStyle('*', transform=t))
370525

371526
Fonts and Text
372527
==============
@@ -382,10 +537,10 @@ them in order to locate a required glyph.
382537
:alt: The phrase "There are 几个汉字 in between!" rendered in various fonts.
383538
:include-source: True
384539

385-
text = "There are 几个汉字 in between!"
386-
387540
plt.rcParams["font.size"] = 20
388541
fig = plt.figure(figsize=(4.75, 1.85))
542+
543+
text = "There are 几个汉字 in between!"
389544
fig.text(0.05, 0.85, text, family=["WenQuanYi Zen Hei"])
390545
fig.text(0.05, 0.65, text, family=["Noto Sans CJK JP"])
391546
fig.text(0.05, 0.45, text, family=["DejaVu Sans", "Noto Sans CJK JP"])
@@ -434,6 +589,25 @@ For figure labels, ``Figure.supxlabel`` and ``Figure.supylabel``, the size and
434589
weight can be set separately from the figure title using :rc:`figure.labelsize`
435590
and :rc:`figure.labelweight`.
436591

592+
.. plot::
593+
:include-source: true
594+
595+
# Original (previously combined with below) rcParams:
596+
plt.rcParams['figure.titlesize'] = 64
597+
plt.rcParams['figure.titleweight'] = 'bold'
598+
599+
# New rcParams:
600+
plt.rcParams['figure.labelsize'] = 32
601+
plt.rcParams['figure.labelweight'] = 'bold'
602+
603+
fig, axs = plt.subplots(2, 2, layout='constrained')
604+
for ax in axs.flat:
605+
ax.set(xlabel='xlabel', ylabel='ylabel')
606+
607+
fig.suptitle('suptitle')
608+
fig.supxlabel('supxlabel')
609+
fig.supylabel('supylabel')
610+
437611
Note that if you have changed :rc:`figure.titlesize` or
438612
:rc:`figure.titleweight`, you must now also change the introduced parameters
439613
for a result consistent with past behaviour.
@@ -487,14 +661,15 @@ The focal length can be calculated from a desired FOV via the equation:
487661

488662
from mpl_toolkits.mplot3d import axes3d
489663

490-
fig, axs = plt.subplots(1, 3, subplot_kw={'projection': '3d'})
491664
X, Y, Z = axes3d.get_test_data(0.05)
492-
focal_lengths = [0.2, 1, np.inf]
493-
for ax, fl in zip(axs, focal_lengths):
665+
666+
fig, axs = plt.subplots(1, 3, figsize=(7, 4),
667+
subplot_kw={'projection': '3d'})
668+
669+
for ax, focal_length in zip(axs, [0.2, 1, np.inf]):
494670
ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10)
495-
ax.set_proj_type('persp', focal_length=fl)
496-
ax.set_title(f"focal_length = {fl}")
497-
fig.set_size_inches(10, 4)
671+
ax.set_proj_type('persp', focal_length=focal_length)
672+
ax.set_title(f"{focal_length=}")
498673

499674
3D plots gained a 3rd "roll" viewing angle
500675
------------------------------------------
@@ -510,9 +685,11 @@ existing 3D plots.
510685
:include-source: true
511686

512687
from mpl_toolkits.mplot3d import axes3d
513-
fig = plt.figure()
514-
ax = fig.add_subplot(projection='3d')
688+
515689
X, Y, Z = axes3d.get_test_data(0.05)
690+
691+
fig, ax = plt.subplots(subplot_kw={'projection': '3d'})
692+
516693
ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10)
517694
ax.view_init(elev=0, azim=0, roll=30)
518695
ax.set_title('elev=0, azim=0, roll=30')
@@ -528,25 +705,27 @@ Users can set the aspect ratio for the X, Y, Z axes of a 3D plot to be 'equal',
528705

529706
from itertools import combinations, product
530707

531-
aspects = ('auto', 'equal', 'equalxy', 'equalyz', 'equalxz')
532-
fig, axs = plt.subplots(1, len(aspects), subplot_kw={'projection': '3d'})
708+
aspects = [
709+
['auto', 'equal', '.'],
710+
['equalxy', 'equalyz', 'equalxz'],
711+
]
712+
fig, axs = plt.subplot_mosaic(aspects, figsize=(7, 6),
713+
subplot_kw={'projection': '3d'})
533714

534715
# Draw rectangular cuboid with side lengths [1, 1, 5]
535716
r = [0, 1]
536717
scale = np.array([1, 1, 5])
537718
pts = combinations(np.array(list(product(r, r, r))), 2)
538719
for start, end in pts:
539720
if np.sum(np.abs(start - end)) == r[1] - r[0]:
540-
for ax in axs:
721+
for ax in axs.values():
541722
ax.plot3D(*zip(start*scale, end*scale), color='C0')
542723
543724
# Set the aspect ratios
544-
for i, ax in enumerate(axs):
725+
for aspect, ax in axs.items():
545726
ax.set_box_aspect((3, 4, 5))
546-
ax.set_aspect(aspects[i])
547-
ax.set_title(f"set_aspect('{aspects[i]}')")
548-
549-
fig.set_size_inches(13, 3)
727+
ax.set_aspect(aspect)
728+
ax.set_title(f'set_aspect({aspect!r})')
550729

551730
Interactive tool improvements
552731
=============================

0 commit comments

Comments
 (0)