-
-
Notifications
You must be signed in to change notification settings - Fork 7.9k
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
Changes from all commits
de5e40a
d6bc727
02bd4ad
42e8614
c75c67a
d79c099
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -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 | ||
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. | ||
|
@@ -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. | ||
|
@@ -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'): | ||
|
@@ -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" | ||
|
@@ -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) | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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]
Miguel Gaiowski |
||
# get high extreme | ||
iq = q3 - q1 | ||
hi_val = q3 + whis * iq | ||
|
@@ -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 | ||
|
@@ -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 | ||
|
@@ -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)) | ||
|
||
|
@@ -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, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch =)