Python/Matplotlib-有没有办法创建不连续轴?

Python/Matplotlib-有没有办法创建不连续轴?,python,matplotlib,Python,Matplotlib,我正在尝试使用具有不连续x轴的pyplot创建一个绘图。通常的绘制方式是,轴将具有如下内容: 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

我正在尝试使用具有不连续x轴的pyplot创建一个绘图。通常的绘制方式是,轴将具有如下内容:

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()
(值)--//-----(后面的值)

其中//表示跳过(值)和(更高的值)之间的所有内容


我还没有找到任何这样的例子,所以我想知道这是否可能。我知道你可以在不连续性上连接数据,例如,金融数据,但我想让轴上的跳跃更明确。目前,我只使用子图,但我真的希望最终所有内容都显示在同一个图表上。

我看到很多关于此功能的建议,但没有迹象表明它已经实现。这是一个暂时可行的解决办法。它将阶跃函数变换应用于x轴。它有很多代码,但它相当简单,因为其中大部分都是样板定制规模的东西。我没有添加任何图形来指示中断的位置,因为这是一个风格问题。祝你完成工作好运

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()
从matplotlib导入pyplot作为plt
从matplotlib导入比例为mscale
从matplotlib将转换导入为MTTransforms
将numpy作为np导入
def CustomScaleFactory(l,u):
类CustomScale(mscale.ScaleBase):
名称='自定义'
定义初始值(自身、轴、**kwargs):
mscale.ScaleBase.\uuuu init\uuuuu(self)
self.thresh=无#thresh
def get_变换(自):
返回self.CustomTransform(self.thresh)
def set_默认_定位器_和_格式化程序(自身、轴):
通过
类CustomTransform(mtTransforms.Transform):
输入_dims=1
输出直径=1
_可分离=真吗
下=l
上限=u
定义初始值(自,阈值):
mtransforms.Transform.\uuuuu init\uuuuu(self)
self.thresh=thresh
def转换(自我,a):
aa=a.复制()
aa[a>自下]=a[a>自下]-(自上自下)
aa[(a>self.lower)&(aself.lower]=a[a>self.lower]+(self.upper-self.lower)
返回aa
def倒置(自身):
返回CustomScale.CustomTransform(self.thresh)
返回自定义刻度
mscale.register_比例(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.绘图(x,y,'。)
ax=plt.gca()
ax.setxscale(“自定义”)
ax.set\u xticks(xticks)
plt.show()

保罗的回答是一个非常好的方法

但是,如果不想进行自定义变换,则可以使用两个子图来创建相同的效果

matplotlib示例中没有一个从头开始的示例(它只出现在当前的git提示中,因为它是几个月前才提交的。它还没有出现在网页上)

这只是对这个例子的一个简单修改,用一个不连续的x轴代替y轴。(这就是为什么我把这篇文章变成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()

要添加断开的轴线
/
效果,我们可以这样做(同样,根据Paul Ivanov的示例进行修改):


针对弗雷德里克·诺德提出的问题,在使用比率不等于1:1的gridspec时,如何使对角线“断开”线平行定向,以下基于保罗·伊万诺夫和乔·金顿建议的更改可能会有所帮助。宽度比率可以使用变量n和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()
检查包装:


对于那些感兴趣的人,我扩展了@Paul的答案,并将其添加到.it can do轴中


目前没有办法像Joe的答案那样添加表示离散跳跃的“十字”,但我计划在将来添加它。我还计划添加一个默认的“记号定位器”,根据
CutoffScale
参数设置合理的默认记号位置。

一个非常简单的方法是

  • 轴棘和轴上的散点图矩形
  • 在该位置将“/”绘制为文本
  • 这对我来说是个魅力:

    # FAKE BROKEN AXES
    # plot a white rectangle on the x-axis-spine to "break" it
    xpos = 10 # x position of the "break"
    ypos = plt.gca().get_ylim()[0] # y position of the "break"
    plt.scatter(xpos, ypos, color='white', marker='s', s=80, clip_on=False, zorder=100)
    # draw "//" on the same place as text
    plt.text(xpos, ymin-0.125, r'//', fontsize=label_size, zorder=101, horizontalalignment='center', verticalalignment='center')
    
    示例图:

    我想现在就只能这样了。这将是我第一次摆弄自定义轴,所以我们只需要看看它是如何运行的。在
    InvertedCustomTransform
    def transform
    中有一个小的打字错误,它应该读
    self.upper
    ,而不是
    upper
    。谢谢你的好例子gh!你能添加几行代码来演示如何使用你的类吗?@RuggeroTurra我的示例中都有。你可能只需要滚动到代码块的底部。这个示例在matplotlib 1.4.3上对我不起作用:。看起来这个版本只识别
    变换\u非仿射
    而不是
    变换
    。请参阅我的patc至少,我自己说得再好不过了;)只有当子数字的比例为1:1时,使
    /
    效果的方法才似乎有效。您是否知道如何使其与GridSpec(width_ratio=[n,m])引入的任何比率一起工作??太棒了。只要稍加修改,这就可以适用于任意数量的x轴截面。弗雷德里克·诺德是正确的。此外,
    /
    效果不会抑制正常的滴答声,这在美学上是不和谐的。安装后,在Pycharm社区2016.3.2中,从Brokernaxes导入Brokernaxes@本。迪克特:有只虫子。我修好了。请运行
    pip install brokenaxes==0.2
    来安装代码的固定版本。似乎与ax的交互不好。网格(True)断轴会抑制勾号吗?或者在水平方向上设置彼此更接近的轴的格式?嗨,Ben,我想删除y轴,但是,我尝试了许多命令,但与brokenaxes(注意x轴是断开的轴)组合使用时无法正常工作,thxRefer t
    # FAKE BROKEN AXES
    # plot a white rectangle on the x-axis-spine to "break" it
    xpos = 10 # x position of the "break"
    ypos = plt.gca().get_ylim()[0] # y position of the "break"
    plt.scatter(xpos, ypos, color='white', marker='s', s=80, clip_on=False, zorder=100)
    # draw "//" on the same place as text
    plt.text(xpos, ymin-0.125, r'//', fontsize=label_size, zorder=101, horizontalalignment='center', verticalalignment='center')