10000 Allow constructing boxplots over multiple calls. by anntzer · Pull Request #13444 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

Allow constructing boxplots over multiple calls. #13444

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

Merged
merged 1 commit into from
Feb 18, 2019
Merged
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
11 changes: 11 additions & 0 deletions doc/api/next_api_changes/2018-02-16-AL.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
API changes
```````````

The ``manage_xticks`` parameter of `~Axes.boxplot` and `~Axes.bxp` has been
renamed (with a deprecation period) to ``manage_ticks``, to take into account
the fact that it manages either x or y ticks depending on the ``vert``
parameter.

When ``manage_ticks`` is True (the default), these methods now attempt to take
previously drawn boxplots into account when setting the axis limits, ticks, and
tick labels.
69 changes: 45 additions & 24 deletions lib/matplotlib/axes/_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3429,6 +3429,7 @@ def extract_err(err, data):

return errorbar_container # (l0, caplines, barcols)

@cbook._rename_parameter("3.1", "manage_xticks", "manage_ticks")
@_preprocess_data()
def boxplot(self, x, notch=None, sym=None, vert=None, whis=None,
positions=None, widths=None, patch_artist=None,
Expand All @@ -3437,7 +3438,7 @@ def boxplot(self, x, notch=None, sym=None, vert=None, whis=None,
showbox=None, showfliers=None, boxprops=None,
labels=None, flierprops=None, medianprops=None,
meanprops=None, capprops=None, whiskerprops=None,
manage_xticks=True, autorange=False, zorder=None):
manage_ticks=True, autorange=False, zorder=None):
"""
Make a box and whisker plot.

Expand Down Expand Up @@ -3538,8 +3539,9 @@ def boxplot(self, x, notch=None, sym=None, vert=None, whis=None,
Labels for each dataset. Length must be compatible with
dimensions of ``x``.

manage_xticks : bool, optional (True)
If the function should adjust the xlim and xtick locations.
manage_ticks : bool, optional (True)
If True, the tick locations and labels will be adjusted to match
the boxplot positions.

autorange : bool, optional (False)
When `True` and the data are distributed such that the 25th and
Expand Down Expand Up @@ -3729,15 +3731,16 @@ def _update_dict(dictionary, rc_name, properties):
medianprops=medianprops, meanprops=meanprops,
meanline=meanline, showfliers=showfliers,
capprops=capprops, whiskerprops=whiskerprops,
manage_xticks=manage_xticks, zorder=zorder)
manage_ticks=manage_ticks, zorder=zorder)
return artists

@cbook._rename_parameter("3.1", "manage_xticks", "manage_ticks")
def bxp(self, bxpstats, positions=None, widths=None, vert=True,
patch_artist=False, shownotches=False, showmeans=False,
showcaps=True, showbox=True, showfliers=True,
boxprops=None, whiskerprops=None, flierprops=None,
medianprops=None, capprops=None, meanprops=None,
meanline=False, manage_xticks=True, zorder=None):
meanline=False, manage_ticks=True, zorder=None):
"""
Drawing function for box and whisker plots.

Expand Down Expand Up @@ -3841,11 +3844,12 @@ def bxp(self, bxpstats, positions=None, widths=None, vert=True,
*meanprops*. Not recommended if *shownotches* is also True.
Otherwise, means will be shown as points.

manage_xticks : bool, default = True
If the function should adjust the xlim and xtick locations.
manage_ticks : bool, default = True
If True, the tick locations and labels will be adjusted to match the
boxplot positions.

zorder : scalar, default = None
The zorder of the resulting boxplot
zorder : scalar, default = None
The zorder of the resulting boxplot.

Returns
-------
Expand Down Expand Up @@ -4117,21 +4121,38 @@ def dopatch(xs, ys, **kwargs):
flier_x, flier_y, **final_flierprops
))

# fix our axes/ticks up a little
if vert:
setticks = self.set_xticks
setlim = self.set_xlim
setlabels = self.set_xticklabels
else:
setticks = self.set_yticks
setlim = self.set_ylim
setlabels = self.set_yticklabels

if manage_xticks:
newlimits = min(positions) - 0.5, max(positions) + 0.5
setlim(newlimits)
setticks(positions)
setlabels(datalabels)
if manage_ticks:
axis_name = "x" if vert else "y"
interval = getattr(self.dataLim, f"interval{axis_name}")
axis = getattr(self, f"{axis_name}axis")
positions = axis.convert_units(positions)
# The 0.5 additional padding ensures reasonable-looking boxes
# even when drawing a single box. We set the sticky edge to
# prevent margins expansion, in order to match old behavior (back
# when separate calls to boxplot() would completely reset the axis
# limits regardless of what was drawn before). The sticky edges
# are attached to the median lines, as they are always present.
interval[:] = (min(interval[0], min(positions) - .5),
max(interval[1], max(positions) + .5))
for median, position in zip(medians, positions):
getattr(median.sticky_edges, axis_name).extend(
[position - .5, position + .5])
# Modified from Axis.set_ticks and Axis.set_ticklabels.
locator = axis.get_major_locator()
if not isinstance(axis.get_major_locator(),
mticker.FixedLocator):
locator = mticker.FixedLocator([])< 8000 /td>
axis.set_major_locator(locator)
locator.locs = np.array([*locator.locs, *positions])
formatter = axis.get_major_formatter()
if not isinstance(axis.get_major_formatter(),
mticker.FixedFormatter):
formatter = mticker.FixedFormatter([])
axis.set_major_formatter(formatter)
formatter.seq = [*formatter.seq, *datalabels]

self.autoscale_view(
scalex=self._autoscaleXon, scaley=self._autoscaleYon)

return dict(whiskers=whiskers, caps=caps, boxes=boxes,
medians=medians, fliers=fliers, means=means)
Expand Down
4 changes: 2 additions & 2 deletions lib/matplotlib/pyplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2436,7 +2436,7 @@ def boxplot(
meanline=None, showmeans=None, showcaps=None, showbox=None,
showfliers=None, boxprops=None, labels=None, flierprops=None,
medianprops=None, meanprops=None, capprops=None,
whiskerprops=None, manage_xticks=True, autorange=False,
whiskerprops=None, manage_ticks=True, autorange=False,
zorder=None, *, data=None):
return gca().boxplot(
x, notch=notch, sym=sym, vert=vert, whis=whis,
Expand All @@ -2447,7 +2447,7 @@ def boxplot(
showfliers=showfliers, boxprops=boxprops, labels=labels,
flierprops=flierprops, medianprops=medianprops,
meanprops=meanprops, capprops=capprops,
whiskerprops=whiskerprops, manage_xticks=manage_xticks,
whiskerprops=whiskerprops, manage_ticks=manage_ticks,
autorange=autorange, zorder=zorder, **({"data": data} if data
is not None else {}))

Expand Down
13 changes: 11 additions & 2 deletions lib/matplotlib/tests/test_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2911,12 +2911,21 @@ def test_manage_xticks():
np.random.seed(0)
y1 = np.random.normal(10, 3, 20)
y2 = np.random.normal(3, 1, 20)
ax.boxplot([y1, y2], positions=[1, 2],
manage_xticks=False)
ax.boxplot([y1, y2], positions=[1, 2], manage_ticks=False)
new_xlim = ax.get_xlim()
assert_array_equal(old_xlim, new_xlim)


def test_boxplot_not_single():
fig, ax = plt.subplots()
ax.boxplot(np.random.rand(100), positions=[3])
ax.boxplot(np.random.rand(100), positions=[5])
fig.canvas.draw()
assert ax.get_xlim() == (2.5, 5.5)
< A979 /td> assert list(ax.get_xticks()) == [3, 5]
assert [t.get_text() for t in ax.get_xticklabels()] == ["3", "5"]


def test_tick_space_size_0():
# allow font size to be zero, which affects ticks when there is
# no other text in the figure.
Expand Down
3 changes: 1 addition & 2 deletions lib/matplotlib/ticker.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,7 @@ def __call__(self, x, pos=None):

class FixedFormatter(Formatter):
"""
Return fixed strings for tick labels based only on position, not
value.
Return fixed strings for tick labels based only on position, not value.
"""
def __init__(self, seq):
"""
Expand Down
0