Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Adds option to plot average in boxplot, besides the median #2520

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

Closed
wants to merge 6 commits into from
Closed
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
4 changes: 4 additions & 0 deletions doc/api/api_changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ For new features that were added to matplotlib, please see
Changes in 1.4.x
================

* Option to display averages in boxplots introduces two new arguments
to :func:`~matplotlib.pyplot.boxplot`. Arguments `averages` and
`useraverages`.

* A major refactoring of the axes module was made. The axes module has been
splitted into smaller modules:

Expand Down
13 changes: 9 additions & 4 deletions doc/users/whats_new.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,13 @@ New plotting features
Support for datetime axes in 2d plots
`````````````````````````````````````
Andrew Dawson added support for datetime axes to
:func:`~matplotlib.pyplot.contour`, :func:`~matplotlib.pyplot.contourf`,
:func:`~matplotlib.pyplot.pcolormesh` and :func:`~matplotlib.pyplot.pcolor`.
:func:`~matplotlib.pyplot.contour`, :func:`~matplotlib.pyplot.contourf`,
:func:`~matplotlib.pyplot.pcolormesh` and :func:`~matplotlib.pyplot.pcolor`.

Display average in boxplots, besides de median value
`````````````````````````````````````
Miguel Gaiowski added option to display the average in boxplots
:func:`~matplotlib.pyplot.boxplot`.


Date handling
Expand All @@ -46,10 +51,10 @@ conversion interfaces :class:`matplotlib.dates.DateConverter` and

Configuration (rcParams)
------------------------

``savefig.transparent`` added
`````````````````````````````
Controls whether figures are saved with a transparent
Controls whether figures are saved with a transparent
background by default. Previously `savefig` always defaulted
to a non-transparent background.

Expand Down
52 changes: 48 additions & 4 deletions lib/matplotlib/axes/_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2788,15 +2788,17 @@ def xywhere(xs, ys, mask):

def boxplot(self, x, notch=False, sym='b+', vert=True, whis=1.5,
positions=None, widths=None, patch_artist=False,
bootstrap=None, usermedians=None, conf_intervals=None):
bootstrap=None, usermedians=None, conf_intervals=None,
averages=False, useraverages=None):
"""
Make a box and whisker plot.

Call signature::

boxplot(x, notch=False, sym='+', vert=True, whis=1.5,
positions=None, widths=None, patch_artist=False,
bootstrap=None, usermedians=None, conf_intervals=None)
bootstrap=None, usermedians=None, conf_intervals=None,
averages=False, useraverages=None)

Make a box and whisker plot for each column of *x* or each
vector in sequence *x*. The box extends from the lower to
Expand Down Expand Up @@ -2851,6 +2853,17 @@ def boxplot(self, x, notch=False, sym='b+', vert=True, whis=1.5,
element of *conf_intervals* is None, boxplot compute notches the
method specified by the other kwargs (e.g., *bootstrap*).

*averages* : [ False (default) | True ]
If False (default), does not include an average line.
If True, will produce a line in the box with the average value.

*useraverages* : [ default None ]
An array or sequence whose first dimension (or length) is
compatible with *x*. This overrides the averages computed by
matplotlib for each element of *useraverages* that is not None.
When an element of *useraverages* == None, the median will be
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mean?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch =)

computed directly as normal.

*positions* : [ default 1,2,...,n ]
Sets the horizontal positions of the boxes. The ticks and limits
are automatically set to match the positions.
Expand All @@ -2872,6 +2885,7 @@ def boxplot(self, x, notch=False, sym='b+', vert=True, whis=1.5,
- boxes: the main body of the boxplot showing the quartiles
and the median's confidence intervals if enabled.
- medians: horizonal lines at the median of each box.
- averages: horizonal lines at the average of each box.
- whiskers: the vertical lines extending to the most extreme,
n-outlier data points.
- caps: the horizontal lines at the ends of the whiskers.
Expand Down Expand Up @@ -2916,7 +2930,8 @@ def computeConfInterval(data, med, iq, bootstrap):
if not self._hold:
self.cla()
holdStatus = self._hold
whiskers, caps, boxes, medians, fliers = [], [], [], [], []
whiskers, caps, boxes = [], [], []
medians, average_values, fliers = [], [], []

# convert x to a list of vectors
if hasattr(x, 'shape'):
Expand Down Expand Up @@ -2951,6 +2966,19 @@ def computeConfInterval(data, med, iq, bootstrap):
elif len(usermedians) != col:
raise ValueError(msg2)

if averages:
# sanitize user-input averages
msg1 = "useraverages must either be a list/tuple or a 1d array"
msg2 = "useraverages' length must be compatible with x"
if useraverages is not None:
if hasattr(useraverages, 'shape'):
if len(useraverages.shape) != 1:
raise ValueError(msg1)
elif useraverages.shape[0] != col:
raise ValueError(msg2)
elif len(useraverages) != col:
raise ValueError(msg2)

#sanitize user-input confidence intervals
msg1 = "conf_intervals must either be a list of tuples or a 2d array"
msg2 = "conf_intervals' length must be compatible with x"
Expand Down Expand Up @@ -2996,6 +3024,13 @@ def computeConfInterval(data, med, iq, bootstrap):
if usermedians[i] is not None:
med = usermedians[i]

if averages:
# use input averages if available
if useraverages is not None and useraverages[i] is not None:
avg = useraverages[i]
else:
avg = np.average(d)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a minor point, but would it make more sense to re-arrange this logic so that the average is only computed if needed?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good, diff on the way.

On Tue, Oct 15, 2013 at 12:25 PM, Thomas A Caswell <[email protected]

wrote:

In lib/matplotlib/axes/_axes.py:

@@ -2996,6 +3024,15 @@ def computeConfInterval(data, med, iq, bootstrap):
if usermedians[i] is not None:
med = usermedians[i]

  •        if averages:
    
  •            # get average
    
  •            avg = np.average(d)
    
  •            # replace with input averages if available
    
  •            if useraverages is not None:
    
  •                if useraverages[i] is not None:
    
  •                    avg = useraverages[i]
    

This is a minor point, but would it make more sense to re-arrange this
logic so that the average is only computed if needed?


Reply to this email directly or view it on GitHubhttps://github.com//pull/2520/files#r6974227
.

Miguel Gaiowski

# get high extreme
iq = q3 - q1
hi_val = q3 + whis * iq
Expand Down Expand Up @@ -3037,6 +3072,10 @@ def computeConfInterval(data, med, iq, bootstrap):
# get y location for median
med_y = [med, med]

if averages:
# get y location for average
avg_y = [avg, avg]

# calculate 'notch' plot
if notch:
# conf. intervals from user, if available
Expand Down Expand Up @@ -3064,6 +3103,9 @@ def computeConfInterval(data, med, iq, bootstrap):
box_y = [q1, q1, q3, q3, q1]
# make our median line vectors
med_x = [box_x_min, box_x_max]
if averages:
# make our average line vectors
avg_x = [box_x_min, box_x_max]

def to_vc(xs, ys):
# convert arguments to verts and codes
Expand Down Expand Up @@ -3119,6 +3161,8 @@ def dopatch(xs, ys):
boxes.extend(doplot(box_x, box_y, 'b-'))

medians.extend(doplot(med_x, med_y, median_color + '-'))
if averages:
average_values.extend(doplot(avg_x, avg_y, 'k:'))
fliers.extend(doplot(flier_hi_x, flier_hi, sym,
flier_lo_x, flier_lo, sym))

Expand All @@ -3136,7 +3180,7 @@ def dopatch(xs, ys):
self.hold(holdStatus)

return dict(whiskers=whiskers, caps=caps, boxes=boxes,
medians=medians, fliers=fliers)
medians=medians, averages=average_values, fliers=fliers)

@docstring.dedent_interpd
def scatter(self, x, y, s=20, c='b', marker='o', cmap=None, norm=None,
Expand Down
5 changes: 3 additions & 2 deletions lib/matplotlib/pyplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -2568,7 +2568,7 @@ def broken_barh(xranges, yrange, hold=None, **kwargs):
@_autogen_docstring(Axes.boxplot)
def boxplot(x, notch=False, sym='b+', vert=True, whis=1.5, positions=None,
widths=None, patch_artist=False, bootstrap=None, usermedians=None,
conf_intervals=None, hold=None):
conf_intervals=None, hold=None, averages=False, useraverages=None):
ax = gca()
# allow callers to override the hold state by passing hold=True|False
washold = ax.ishold()
Expand All @@ -2579,7 +2579,8 @@ def boxplot(x, notch=False, sym='b+', vert=True, whis=1.5, positions=None,
ret = ax.boxplot(x, notch=notch, sym=sym, vert=vert, whis=whis,
positions=positions, widths=widths,
patch_artist=patch_artist, bootstrap=bootstrap,
usermedians=usermedians, conf_intervals=conf_intervals)
usermedians=usermedians, conf_intervals=conf_intervals,
averages=averages, useraverages=useraverages)
draw_if_interactive()
finally:
ax.hold(washold)
Expand Down