It's my understanding that there is no built-in method for generating a

"broken axis" (where you skip over some range of values, indicating this

with some graphical mark). I wanted to do this, so I've put together a

function which seems to be fairly robust, and I thought I might propose it

as a starting point if there's interest in having a built-in facility for

broken axes.

Please let me know if this is not the appropriate place to be submitting

this suggestion.

The basic idea of the below function is that you feed is an axes object

along with an iterable giving the bounds of each section of the y-axis. It

then returns new upper and lower axes objects, after masking spines,

calculating distances, etc. The only real problems here is that you need to

explicitly plot things on both the upper and lower axes, and then I haven't

figured out how to push out the y-axis label of the main axes object so it

doesn't overlap with the tick labels of the upper and lower axes. So, I

instead moved the y-labels of the upper and lower axes so that they appear

at the center of the axis, but this is problematic. Any thoughts on how to

do that part better?

http://old.nabble.com/file/p27909750/broken.png

## ···

----------

from matplotlib import pyplot as plt

def axes_broken_y(axes, upper_frac=0.5, break_frac=0.02, ybounds=None,

xlabel=None, ylabel=None):

"""

Replace the current axes with a set of upper and lower axes.

The new axes will be transparent, with a breakmark drawn between them.

They

share the x-axis. Returns (upper_axes, lower_axes).

If ybounds=[ymin_lower, ymax_lower, ymin_upper, ymax_upper] is defined,

upper_frac will be ignored, and the y-axis bounds will be fixed with the

specified values.

"""

def breakmarks(axes, y_min, y_max, xwidth=0.008):

x1, y1, x2, y2 = axes.get_position().get_points().flatten().tolist()

segment_height = (y_max - y_min) / 3.

xoffsets = [0, +xwidth, -xwidth, 0]

yvalues = [y_min + (i * segment_height) for i in range(4)]

# Get color of y-axis

for loc, spine in axes.spines.iteritems():

if loc == 'left':

color = spine.get_edgecolor()

for x_position in [x1, x2]:

line = matplotlib.lines.Line2D(

[x_position + offset for offset in xoffsets], yvalues,

transform=plt.gcf().transFigure, clip_on=False,

color=color)

axes.add_line(line)

# Readjust upper_frac if ybounds are defined

if ybounds:

if len(ybounds) != 4:

print "len(ybounds) != 4; aborting..."

return

ymin1, ymax1, ymin2, ymax2 = [float(value) for value in ybounds]

data_height1, data_height2 = (ymax1 - ymin1), (ymax2 - ymin2)

upper_frac = data_height2 / (data_height1 + data_height2)

x1, y1, x2, y2 = axes.get_position().get_points().flatten().tolist()

width = x2 - x1

lower_height = (y2 - y1) * ((1 - upper_frac) - 0.5 * break_frac)

upper_height = (y2 - y1) * (upper_frac - 0.5 * break_frac)

upper_bottom = (y2 - y1) - upper_height + y1

lower_axes = plt.axes([x1, y1, width, lower_height], axisbg='None')

upper_axes = plt.axes([x1, upper_bottom, width, upper_height],

axisbg='None', sharex=lower_axes)

# Erase the edges between the axes

for loc, spine in upper_axes.spines.iteritems():

if loc == 'bottom':

spine.set_color('none')

for loc, spine in lower_axes.spines.iteritems():

if loc == 'top':

spine.set_color('none')

upper_axes.get_xaxis().set_ticks_position('top')

lower_axes.get_xaxis().set_ticks_position('bottom')

plt.setp(upper_axes.get_xticklabels(), visible=False)

breakmarks(upper_axes, y1 + lower_height, upper_bottom)

# Set ylims if ybounds are defined

if ybounds:

lower_axes.set_ylim(ymin1, ymax1)

upper_axes.set_ylim(ymin2, ymax2)

lower_axes.set_autoscaley_on(False)

upper_axes.set_autoscaley_on(False)

upper_axes.yaxis.get_label().set_position((0, 1 - (0.5 /

(upper_frac/(1+break_frac)))))

lower_axes.yaxis.get_label().set_position((0, 0.5 / ((1 -

upper_frac)/(1+break_frac))))

# Make original axes invisible

axes.set_xticks([])

axes.set_yticks([])

print upper_axes.yaxis.get_label().get_position()

print lower_axes.yaxis.get_label().get_position()

print axes.yaxis.get_label().get_position()

print axes.yaxis.labelpad

for loc, spine in axes.spines.iteritems():

spine.set_color('none')

return upper_axes, lower_axes

def prepare_efficiency(axes, lower_bound=0.69):

"""

Set up an efficiency figure with breakmarks to indicate a suppressed

zero.

The y-axis limits are set to (lower_bound, 1.0), as appropriate for an

efficiency plot, and autoscaling is turned off.

"""

upper_axes, lower_axes = axes_broken_y(axes, upper_frac=0.97)

lower_axes.set_yticks([])

upper_axes.set_ylim(lower_bound, 1.)

upper_axes.set_autoscaley_on(False)

return upper_axes, lower_axes

# test these

ax = plt.axes()

upper, lower = axes_broken_y(ax, ybounds=[-2., 2.9, 22.1, 30.])

upper.plot(range(30), range(30))

lower.plot(range(30), range(30))

upper.set_ylabel('Data')

plt.savefig('test')

--

View this message in context: http://old.nabble.com/Proposal-for-Broken-Axes-tp27909750p27909750.html

Sent from the matplotlib - devel mailing list archive at Nabble.com.