Python / Matplotlib – 有没有办法使一个不连续的轴?

我正在尝试使用具有不连续X轴的pyplot创build一个图表。 通常的方式是绘制坐标轴,像这样:

(值)—- // —-(后面的值)

//表示你正在跳过(值)和(后面的值)之间的所有内容。

我一直没有find任何这样的例子,所以我想知道是否有可能。 我知道你可以通过例如财务数据的不连续性来join数据,但是我想让这个轴的跳跃更加明确。 目前我只是使用子图,但我最终希望所有的东西都在同一个图上。

保罗的答案是这样做的完美方法。

但是,如果您不想进行自定义转换,则可以使用两个子图创build相同的效果。

保罗·伊凡诺夫(Paul Ivanov)在matplotlib的例子中写了一个很好的例子(这只是在当前的git提示中,因为它只是在几个月前才提交,而不是在网页上)。 。

这只是对这个例子的一个简单的修改,它有一个不连续的x轴而不是y轴。 (这就是为什么我把这个post写成CW)

基本上,你只是做这样的事情:

import matplotlib.pylab as plt import numpy as np # If you're not familiar with np.r_, don't worry too much about this. It's just # a series with points from 0 to 1 spaced at 0.1, and 9 to 10 with the same spacing. x = np.r_[0:1:0.1, 9:10:0.1] y = np.sin(x) fig,(ax,ax2) = plt.subplots(1, 2, sharey=True) # plot the same data on both axes ax.plot(x, y, 'bo') ax2.plot(x, y, 'bo') # zoom-in / limit the view to different portions of the data ax.set_xlim(0,1) # most of the data ax2.set_xlim(9,10) # outliers only # hide the spines between ax and ax2 ax.spines['right'].set_visible(False) ax2.spines['left'].set_visible(False) ax.yaxis.tick_left() ax.tick_params(labeltop='off') # don't put tick labels at the top ax2.yaxis.tick_right() # Make the spacing between the two axes a bit smaller plt.subplots_adjust(wspace=0.15) plt.show() 

在这里输入图像说明

要添加破损的轴线//效果,我们可以做到这一点(再次,从保罗·伊万诺夫的例子中修改):

 import matplotlib.pylab as plt import numpy as np # If you're not familiar with np.r_, don't worry too much about this. It's just # a series with points from 0 to 1 spaced at 0.1, and 9 to 10 with the same spacing. x = np.r_[0:1:0.1, 9:10:0.1] y = np.sin(x) fig,(ax,ax2) = plt.subplots(1, 2, sharey=True) # plot the same data on both axes ax.plot(x, y, 'bo') ax2.plot(x, y, 'bo') # zoom-in / limit the view to different portions of the data ax.set_xlim(0,1) # most of the data ax2.set_xlim(9,10) # outliers only # hide the spines between ax and ax2 ax.spines['right'].set_visible(False) ax2.spines['left'].set_visible(False) ax.yaxis.tick_left() ax.tick_params(labeltop='off') # don't put tick labels at the top ax2.yaxis.tick_right() # Make the spacing between the two axes a bit smaller plt.subplots_adjust(wspace=0.15) # 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((1-d,1+d),(-d,+d), **kwargs) # top-left diagonal ax.plot((1-d,1+d),(1-d,1+d), **kwargs) # bottom-left diagonal kwargs.update(transform=ax2.transAxes) # switch to the bottom axes ax2.plot((-d,d),(-d,+d), **kwargs) # top-right diagonal ax2.plot((-d,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' plt.show() 

在这里输入图像说明

我看到这个function的很多build议,但没有表明它已经被执行。 这是一个可行的解决scheme。 它对x轴应用一个阶跃函数变换。 这是很多代码,但它很简单,因为它大部分是样板自定义比例的东西。 我没有添加任何graphics来表示rest的位置,因为这是一个风格问题。 祝好运完成工作。

 from matplotlib import pyplot as plt from matplotlib import scale as mscale from matplotlib import transforms as mtransforms import numpy as np def CustomScaleFactory(l, u): class CustomScale(mscale.ScaleBase): name = 'custom' def __init__(self, axis, **kwargs): mscale.ScaleBase.__init__(self) self.thresh = None #thresh def get_transform(self): return self.CustomTransform(self.thresh) def set_default_locators_and_formatters(self, axis): pass class CustomTransform(mtransforms.Transform): input_dims = 1 output_dims = 1 is_separable = True lower = l upper = u def __init__(self, thresh): mtransforms.Transform.__init__(self) self.thresh = thresh def transform(self, a): aa = a.copy() aa[a>self.lower] = a[a>self.lower]-(self.upper-self.lower) aa[(a>self.lower)&(a<self.upper)] = self.lower return aa def inverted(self): return CustomScale.InvertedCustomTransform(self.thresh) class InvertedCustomTransform(mtransforms.Transform): input_dims = 1 output_dims = 1 is_separable = True lower = l upper = u def __init__(self, thresh): mtransforms.Transform.__init__(self) self.thresh = thresh def transform(self, a): aa = a.copy() aa[a>self.lower] = a[a>self.lower]+(self.upper-self.lower) return aa def inverted(self): return CustomScale.CustomTransform(self.thresh) return CustomScale mscale.register_scale(CustomScaleFactory(1.12, 8.88)) x = np.concatenate((np.linspace(0,1,10), np.linspace(9,10,10))) xticks = np.concatenate((np.linspace(0,1,6), np.linspace(9,10,6))) y = np.sin(x) plt.plot(x, y, '.') ax = plt.gca() ax.set_xscale('custom') ax.set_xticks(xticks) plt.show() 

在这里输入图像说明

检查brokenaxes包:

 import matplotlib.pyplot as plt from brokenaxes import brokenaxes import numpy as np fig = plt.figure(figsize=(5,2)) bax = brokenaxes(xlims=((0, .1), (.4, .7)), ylims=((-1, .7), (.79, 1)), hspace=.05) x = np.linspace(0, 1, 100) bax.plot(x, np.sin(10 * x), label='sin') bax.plot(x, np.cos(10 * x), label='cos') bax.legend(loc=3) bax.set_xlabel('time') bax.set_ylabel('value') 

来自breakaxes的例子

对于Frederick Nord的问题,如果使用比例不等于1:1的gridspec,如何使对angular线“断开”线的平行定向成为可能,那么根据Paul Ivanov和Joe Kingtons的build议进行如下更改可能会有帮助。 宽度比可以使用variablesn和m来改变。

 import matplotlib.pylab as plt import numpy as np import matplotlib.gridspec as gridspec x = np.r_[0:1:0.1, 9:10:0.1] y = np.sin(x) n = 5; m = 1; gs = gridspec.GridSpec(1,2, width_ratios = [n,m]) plt.figure(figsize=(10,8)) ax = plt.subplot(gs[0,0]) ax2 = plt.subplot(gs[0,1], sharey = ax) plt.setp(ax2.get_yticklabels(), visible=False) plt.subplots_adjust(wspace = 0.1) ax.plot(x, y, 'bo') ax2.plot(x, y, 'bo') ax.set_xlim(0,1) ax2.set_xlim(10,8) # hide the spines between ax and ax2 ax.spines['right'].set_visible(False) ax2.spines['left'].set_visible(False) ax.yaxis.tick_left() ax.tick_params(labeltop='off') # don't put tick labels at the top ax2.yaxis.tick_right() 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) on = (n+m)/n; om = (n+m)/m; ax.plot((1-d*on,1+d*on),(-d,d), **kwargs) # bottom-left diagonal ax.plot((1-d*on,1+d*on),(1-d,1+d), **kwargs) # top-left diagonal kwargs.update(transform=ax2.transAxes) # switch to the bottom axes ax2.plot((-d*om,d*om),(-d,d), **kwargs) # bottom-right diagonal ax2.plot((-d*om,d*om),(1-d,1+d), **kwargs) # top-right diagonal plt.show()