Skip to content

Instantly share code, notes, and snippets.

@dmcdougall
Created September 17, 2012 11:01
Show Gist options
  • Save dmcdougall/3736702 to your computer and use it in GitHub Desktop.
Save dmcdougall/3736702 to your computer and use it in GitHub Desktop.
Axes boxplot method
def boxplot(self, x, notch=0, sym='b+', vert=1, whis=1.5,
positions=None, widths=None, means=0, dofill=1,
linestyle='-', monochrome=0, limits=None):
"""
boxplot(x, notch=0, sym='+', vert=1, whis=1.5,
positions=None, widths=None, means=0, dofill=1,
linestyle='-', monochrome=0, limits=None)
Make a box and whisker plot for each column of x.
The box extends from the lower to upper quartile values
of the data, with a line at the median. The whiskers
extend from the box to show the range of the data. Flier
points are those past the end of the whiskers.
notch = 0 (default) produces a rectangular box plot.
notch = 1 will produce a notched box plot
sym (default 'b+') is the default symbol for flier points.
Enter an empty string ('') if you don't want to show fliers.
vert = 1 (default) makes the boxes vertical.
vert = 0 makes horizontal boxes. This seems goofy, but
that's how Matlab did it.
whis (default 1.5) defines the length of the whiskers as
a function of the inner quartile range. They extend to the
most extreme data point within ( whis*(75%-25%) ) data range.
positions (default 1,2,...,n) sets the horizontal positions of
the boxes. The ticks and limits are automatically set to match
the positions.
widths is either a scalar or a vector and sets the width of
each box. The default is 0.5, or 0.15*(distance between extreme
positions) if that is smaller.
means = 0 (default) does not indicate the mean of the data.
means = 1 plots a dashed black line in the box indicating
the mean of the data.
linestyle sets the line style of the whiskers.
monochrome = 0 (default) uses color in the plot.
monochrome = 1 uses a monochrome color scheme.
dofill = 1 (default) fills the box in white
dofill = 0 leaves the box alone
x is a Numeric array
Returns a list of the lines added
"""
if not self._hold:
self.cla()
holdStatus = self._hold
lines = []
x = asarray(x)
# if we've got a vector, reshape it
rank = len(x.shape)
if 1 == rank:
x.shape = -1, 1
row, col = x.shape
# get some plot info
if positions is None:
positions = range(1, col + 1)
if widths is None:
distance = max(1,max(positions) - min(positions))
widths = distance * min(0.15, 0.5/distance)
if isinstance(widths, float) or isinstance(widths, int):
widths = ones((col,), 'd') * widths
# loop through columns, adding each to plot
self.hold(True)
for i,pos in enumerate(positions):
d = x[:,i]
# get mean
mean = sum(d)/len(d)
# get median and quartiles
q1, med, q3 = prctile(d,[25,50,75])
# get high extreme
iq = q3 - q1
hi_val = q3 + whis*iq
wisk_hi = compress( d <= hi_val , d )
if len(wisk_hi) == 0:
wisk_hi = q3
else:
wisk_hi = max(wisk_hi)
# get low extreme
lo_val = q1 - whis*iq
wisk_lo = compress( d >= lo_val, d )
if len(wisk_lo) == 0:
wisk_lo = q1
else:
wisk_lo = min(wisk_lo)
# get fliers - if we are showing them
flier_hi = []
flier_lo = []
flier_hi_x = []
flier_lo_x = []
if len(sym) != 0:
flier_hi = compress( d > wisk_hi, d )
flier_lo = compress( d < wisk_lo, d )
flier_hi_x = ones(flier_hi.shape[0]) * pos
flier_lo_x = ones(flier_lo.shape[0]) * pos
# get x locations for fliers, whisker, whisker cap and box sides
box_x_min = pos - widths[i] * 0.5
box_x_max = pos + widths[i] * 0.5
wisk_x = ones(2) * pos
cap_x_min = pos - widths[i] * 0.25
cap_x_max = pos + widths[i] * 0.25
cap_x = [cap_x_min, cap_x_max]
# get y location for median, mean
med_y = [med, med]
mean_y = [mean, mean]
# calculate 'regular' plot
if notch == 0:
# make our box vectors
box_x = [box_x_min, box_x_max, box_x_max, box_x_min, box_x_min]
box_y = [q1, q1, q3, q3, q1 ]
# make our median, mean line vectors
med_x = [box_x_min, box_x_max]
mean_x = [box_x_min, box_x_max]
# calculate 'notch' plot
else:
notch_max = med + 1.57*iq/sqrt(row)
notch_min = med - 1.57*iq/sqrt(row)
if notch_max > q3:
notch_max = q3
if notch_min < q1:
notch_min = q1
# make our notched box vectors
box_x = [box_x_min, box_x_max, box_x_max, cap_x_max, box_x_max, box_x_max, box_x_min, box_x_min, cap_x_min, box_x_min, box_x_min]
box_y = [q1, q1, notch_min, med, notch_max, q3, q3, notch_max, med, notch_min, q1]
# make our median, mean line vectors
med_x = [cap_x_min, cap_x_max]
mean_x = [box_x_min, box_x_max] # doesn't take into account notch cap
if monochrome:
wiskcol = capcol = boxcol = medcol = 'k'
else:
wiskcol, capcol, boxcol, medcol = 'b', 'k', 'b', 'r'
# make a vertical plot . . .
if dofill:
self.fill(box_x, box_y, facecolor='w')
if 1 == vert:
l = self.plot(wisk_x, [q1, wisk_lo], wiskcol+linestyle,
wisk_x, [q3, wisk_hi], wiskcol+linestyle,
cap_x, [wisk_hi, wisk_hi], capcol+'-',
cap_x, [wisk_lo, wisk_lo], capcol+'-',
box_x, box_y, boxcol+'-',
med_x, med_y, medcol+'-',
flier_hi_x, flier_hi, sym,
flier_lo_x, flier_lo, sym )
if means:
l.append(self.plot(mean_x, mean_y, 'k:'))
lines.extend(l)
# or perhaps a horizontal plot
else:
l = self.plot([q1, wisk_lo], wisk_x, wiskcol+linestyle,
[q3, wisk_hi], wisk_x, wiskcol+linestyle,
[wisk_hi, wisk_hi], cap_x, capcol+'-',
[wisk_lo, wisk_lo], cap_x, capcol+'-',
box_y, box_x, boxcol+'-',
med_y, med_x, medcol+'-',
flier_hi, flier_hi_x, sym,
flier_lo, flier_lo_x, sym )
if means:
l.append(self.plot(mean_x, mean_y, 'k:'))
lines.extend(l)
# fix our axes/ticks up a little
if 1 == vert:
setticks, setlim = self.set_xticks, self.set_xlim
else:
setticks, setlim = self.set_yticks, self.set_ylim
if limits is None:
newlimits = min(positions)-0.5, max(positions)+0.5
setlim(newlimits)
setticks(positions)
elif limits != ():
setlim(limits)
setticks([])
# reset hold status
self.hold(holdStatus)
return lines
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment