a break in the y-axis

Is it possible to create a "break" in the y-axis so that it has ticks
for value 0-.2, then ticks for values .8-1.0, but devotes only a token
amount of space to the area 0.2-0.8?
I have a dataset with most datapoints in 0-.2 and a couple in .8-1.0,
and none in .2-.8 . The default scaling wastes a lot of space and
compresses the data in the 0-.2 range
such that it is hard to distinguish.

Thanks for any help!

Ilya

p.s. I know I could use two y-axes with different scales; but this
would require splitting the data into two different datasets as well,
and would not allow connecting all points
with one line.

Ilya Shlyakhter, on 2011-01-22 19:06, wrote:

Is it possible to create a "break" in the y-axis so that it has ticks
for value 0-.2, then ticks for values .8-1.0, but devotes only a token
amount of space to the area 0.2-0.8?
I have a dataset with most datapoints in 0-.2 and a couple in .8-1.0,
and none in .2-.8 . The default scaling wastes a lot of space and
compresses the data in the 0-.2 range
such that it is hard to distinguish.

Hi Ilya,

this...

p.s. I know I could use two y-axes with different scales; but this
would require splitting the data into two different datasets as well,
and would not allow connecting all points
with one line.

... is the way I'd proceed, because it's clean, and requires the
least amount of work. Connecting your lines across such breaks
is misleading - since the magnitude of the slope of the
connecting line segment arbitrary relative to all other line
segments. You don't actually have to divide your data, you can
just replot *all* data on the secondary plot, and then set the x
and y lims to break up your views on the data. I'm attaching a
quick sketch of what that would look like. (Note how different
the outlier line segments would look if we connected them in the
same manner that all other points are connected).

  import numpy as np
  import matplotlib.pylab as plt
  pts = np.random.rand(30)*.2
  pts[[7,11]] += .8
  f,(ax,ax2) = plt.subplots(2,1,sharex=True)
  
  ax.plot(pts)
  ax2.plot(pts)
  ax.set_ylim(.78,1.)
  ax2.set_ylim(0,.22)
  
  ax.xaxis.tick_top()
  ax.spines['bottom'].set_visible(False)
  ax.tick_params(labeltop='off')
  ax2.xaxis.tick_bottom()
  ax2.spines['top'].set_visible(False)

If this is something you really want, though, you can achieve it
by making your own projection/scale:
http://matplotlib.sourceforge.net/devel/add_new_projection.html

Yet another way would be to re-label the tick lines (e.g. make .6
label be 1.0 and subtract that offset from your two outliers.

best,

···

--
Paul Ivanov
314 address only used for lists, off-list direct email at:
http://pirsquared.org | GPG/PGP key id: 0x0F3E28F7

Paul Ivanov, on 2011-01-22 18:28, wrote:

Ilya Shlyakhter, on 2011-01-22 19:06, wrote:
> Is it possible to create a "break" in the y-axis so that it has ticks
> for value 0-.2, then ticks for values .8-1.0, but devotes only a token
> amount of space to the area 0.2-0.8?
> I have a dataset with most datapoints in 0-.2 and a couple in .8-1.0,
> and none in .2-.8 . The default scaling wastes a lot of space and
> compresses the data in the 0-.2 range
> such that it is hard to distinguish.

Hi Ilya,

this...

> p.s. I know I could use two y-axes with different scales; but this
> would require splitting the data into two different datasets as well,
> and would not allow connecting all points
> with one line.

... is the way I'd proceed, because it's clean, and requires the
least amount of work. Connecting your lines across such breaks
is misleading - since the magnitude of the slope of the
connecting line segment arbitrary relative to all other line
segments. You don't actually have to divide your data, you can
just replot *all* data on the secondary plot, and then set the x
and y lims to break up your views on the data. I'm attaching a
quick sketch of what that would look like. (Note how different
the outlier line segments would look if we connected them in the
same manner that all other points are connected).

  import numpy as np
  import matplotlib.pylab as plt
  pts = np.random.rand(30)*.2
  pts[[7,11]] += .8
  f,(ax,ax2) = plt.subplots(2,1,sharex=True)
  
  ax.plot(pts)
  ax2.plot(pts)
  ax.set_ylim(.78,1.)
  ax2.set_ylim(0,.22)
  
  ax.xaxis.tick_top()
  ax.spines['bottom'].set_visible(False)
  ax.tick_params(labeltop='off')
  ax2.xaxis.tick_bottom()
  ax2.spines['top'].set_visible(False)

If this is something you really want, though, you can achieve it
by making your own projection/scale:
http://matplotlib.sourceforge.net/devel/add_new_projection.html

Yet another way would be to re-label the tick lines (e.g. make .6
label be 1.0 and subtract that offset from your two outliers.

forgot the attachment, here it is.

snipped_ticks.png

···

--
Paul Ivanov
314 address only used for lists, off-list direct email at:
http://pirsquared.org | GPG/PGP key id: 0x0F3E28F7

Paul Ivanov, on 2011-01-22 18:28, wrote:

Ilya Shlyakhter, on 2011-01-22 19:06, wrote:

Is it possible to create a "break" in the y-axis so that it has ticks
for value 0-.2, then ticks for values .8-1.0, but devotes only a token
amount of space to the area 0.2-0.8?
I have a dataset with most datapoints in 0-.2 and a couple in .8-1.0,
and none in .2-.8 . The default scaling wastes a lot of space and
compresses the data in the 0-.2 range
such that it is hard to distinguish.

Hi Ilya,

this...

Paul,

Your example below is nice, and this question comes up quite often. If we don't already have a gallery example of this, you might want to add one. (Probably better to use deterministic fake data rather than random.)

Eric

···

On 01/22/2011 05:16 PM, Paul Ivanov wrote:

p.s. I know I could use two y-axes with different scales; but this
would require splitting the data into two different datasets as well,
and would not allow connecting all points
with one line.

... is the way I'd proceed, because it's clean, and requires the
least amount of work. Connecting your lines across such breaks
is misleading - since the magnitude of the slope of the
connecting line segment arbitrary relative to all other line
segments. You don't actually have to divide your data, you can
just replot *all* data on the secondary plot, and then set the x
and y lims to break up your views on the data. I'm attaching a
quick sketch of what that would look like. (Note how different
the outlier line segments would look if we connected them in the
same manner that all other points are connected).

   import numpy as np
   import matplotlib.pylab as plt
   pts = np.random.rand(30)*.2
   pts[[7,11]] += .8
   f,(ax,ax2) = plt.subplots(2,1,sharex=True)

   ax.plot(pts)
   ax2.plot(pts)
   ax.set_ylim(.78,1.)
   ax2.set_ylim(0,.22)

   ax.xaxis.tick_top()
   ax.spines['bottom'].set_visible(False)
   ax.tick_params(labeltop='off')
   ax2.xaxis.tick_bottom()
   ax2.spines['top'].set_visible(False)

If this is something you really want, though, you can achieve it
by making your own projection/scale:
http://matplotlib.sourceforge.net/devel/add_new_projection.html

Yet another way would be to re-label the tick lines (e.g. make .6
label be 1.0 and subtract that offset from your two outliers.

forgot the attachment, here it is.

------------------------------------------------------------------------------
Special Offer-- Download ArcSight Logger for FREE (a $49 USD value)!
Finally, a world-class log management solution at an even better price-free!
Download using promo code Free_Logger_4_Dev2Dev. Offer expires
February 28th, so secure your free ArcSight Logger TODAY!
http://p.sf.net/sfu/arcsight-sfd2d

_______________________________________________
Matplotlib-users mailing list
Matplotlib-users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-users

Eric Firing, on 2011-01-22 17:49, wrote:

>> Paul Ivanov, on 2011-01-22 18:28, wrote:

Paul,

Your example below is nice, and this question comes up quite often. If
we don't already have a gallery example of this, you might want to add
one. (Probably better to use deterministic fake data rather than random.)

>>
>> import numpy as np
>> import matplotlib.pylab as plt
>> pts = np.random.rand(30)*.2
>> pts[[7,11]] += .8
>> f,(ax,ax2) = plt.subplots(2,1,sharex=True)
>>
>> ax.plot(pts)
>> ax2.plot(pts)
>> ax.set_ylim(.78,1.)
>> ax2.set_ylim(0,.22)
>>
>> ax.xaxis.tick_top()
>> ax.spines['bottom'].set_visible(False)
>> ax.tick_params(labeltop='off')
>> ax2.xaxis.tick_bottom()
>> ax2.spines['top'].set_visible(False)

Done in r8935, see examples/pylab_examples/broken_axis.py

I documented the above, used deterministic fake data, as Eric
suggested, and added the diagonal cut lines that usually
accompany a broken axis. Here's the tail end of the script which
creates that effect (see updated attached image).

  # This looks pretty good, and was fairly painless, but you can
  # get that cut-out diagonal lines look with just a bit more
  # work. The important thing to know here is that in axes
  # coordinates, which are always between 0-1, spine endpoints
  # are at these locations (0,0), (0,1), (1,0), and (1,1). Thus,
  # we just need to put the diagonals in the appropriate corners
  # of each of our axes, and so long as we use the right
  # transform and disable clipping.
  
  d = .015 # how big to make the diagonal lines in axes coordinates
  # arguments to pass plot, just so we don't keep repeating them
  kwargs = dict(transform=ax.transAxes, color='k', clip_on=False)
  ax.plot((-d,+d),(-d,+d), **kwargs) # top-left diagonal
  ax.plot((1-d,1+d),(-d,+d), **kwargs) # top-right diagonal
  
  kwargs.update(transform=ax2.transAxes) # switch to the bottom axes
  ax2.plot((-d,+d),(1-d,1+d), **kwargs) # bottom-left diagonal
  ax2.plot((1-d,1+d),(1-d,1+d), **kwargs) # bottom-right diagonal
  
  # What's cool about this is that now if we vary the distance
  # between ax and ax2 via f.subplots_adjust(hspace=...) or
  # plt.subplot_tool(), the diagonal lines will move accordingly,
  # and stay right at the tips of the spines they are 'breaking'

best,

broken_axis.png

···

--
Paul Ivanov
314 address only used for lists, off-list direct email at:
http://pirsquared.org | GPG/PGP key id: 0x0F3E28F7

[...]

Done in r8935, see examples/pylab_examples/broken_axis.py

Thank you.

I documented the above, used deterministic fake data, as Eric
suggested, and added the diagonal cut lines that usually
accompany a broken axis. Here's the tail end of the script which
creates that effect (see updated attached image).

Beautiful!

Eric

···

On 01/23/2011 11:46 PM, Paul Ivanov wrote:

   # This looks pretty good, and was fairly painless, but you can
   # get that cut-out diagonal lines look with just a bit more
   # work. The important thing to know here is that in axes
   # coordinates, which are always between 0-1, spine endpoints
   # are at these locations (0,0), (0,1), (1,0), and (1,1). Thus,
   # we just need to put the diagonals in the appropriate corners
   # of each of our axes, and so long as we use the right
   # transform and disable clipping.

   d = .015 # how big to make the diagonal lines in axes coordinates
   # arguments to pass plot, just so we don't keep repeating them
   kwargs = dict(transform=ax.transAxes, color='k', clip_on=False)
   ax.plot((-d,+d),(-d,+d), **kwargs) # top-left diagonal
   ax.plot((1-d,1+d),(-d,+d), **kwargs) # top-right diagonal

   kwargs.update(transform=ax2.transAxes) # switch to the bottom axes
   ax2.plot((-d,+d),(1-d,1+d), **kwargs) # bottom-left diagonal
   ax2.plot((1-d,1+d),(1-d,1+d), **kwargs) # bottom-right diagonal

   # What's cool about this is that now if we vary the distance
   # between ax and ax2 via f.subplots_adjust(hspace=...) or
   # plt.subplot_tool(), the diagonal lines will move accordingly,
   # and stay right at the tips of the spines they are 'breaking'

best,