【问题标题】:Matplotlib - fixing x axis scale and autoscale y axisMatplotlib - 修复 x 轴比例和自动缩放 y 轴
【发布时间】:2015-06-10 06:43:29
【问题描述】:

我想只绘制数​​组的一部分,固定 x 部分,但让 y 部分自动缩放。我尝试了如下所示,但它不起作用。

有什么建议吗?

import numpy as np
import matplotlib.pyplot as plt

data=[np.arange(0,101,1),300-0.1*np.arange(0,101,1)]

plt.figure()

plt.scatter(data[0], data[1])
plt.xlim([50,100])
plt.autoscale(enable=True, axis='y')

plt.show()

【问题讨论】:

    标签: python pandas matplotlib


    【解决方案1】:

    自动缩放始终使用完整的数据范围,因此 y 轴按 y 数据的完整范围进行缩放,而不仅仅是 x 范围内的内容。

    如果您想显示数据的子集,那么只绘制该子集可能是最简单的:

    import numpy as np
    import matplotlib.pyplot as plt
    
    x, y = np.arange(0,101,1) ,300 - 0.1*np.arange(0,101,1)
    mask = (x >= 50) & (x <= 100)
    
    fig, ax = plt.subplots()
    ax.scatter(x[mask], y[mask])
    
    plt.show()
    

    【讨论】:

      【解决方案2】:

      虽然Joe Kington 在建议只绘制必要的数据时肯定提出了最明智的答案,但在某些情况下最好绘制所有数据并放大到某个部分。此外,最好有一个只需要轴对象的“autoscale_y”函数(即,不像答案here,它需要直接使用数据。)

      这是一个仅根据可见 x 区域中的数据重新调整 y 轴的函数:

      def autoscale_y(ax,margin=0.1):
          """This function rescales the y-axis based on the data that is visible given the current xlim of the axis.
          ax -- a matplotlib axes object
          margin -- the fraction of the total height of the y-data to pad the upper and lower ylims"""
      
          import numpy as np
      
          def get_bottom_top(line):
              xd = line.get_xdata()
              yd = line.get_ydata()
              lo,hi = ax.get_xlim()
              y_displayed = yd[((xd>lo) & (xd<hi))]
              h = np.max(y_displayed) - np.min(y_displayed)
              bot = np.min(y_displayed)-margin*h
              top = np.max(y_displayed)+margin*h
              return bot,top
      
          lines = ax.get_lines()
          bot,top = np.inf, -np.inf
      
          for line in lines:
              new_bot, new_top = get_bottom_top(line)
              if new_bot < bot: bot = new_bot
              if new_top > top: top = new_top
      
          ax.set_ylim(bot,top)
      

      这有点像 hack,在很多情况下可能都行不通,但对于一个简单的情节来说,它运行良好。

      下面是一个使用这个函数的简单例子:

      import numpy as np
      import matplotlib.pyplot as plt
      
      x = np.linspace(-100,100,1000)
      y = x**2 + np.cos(x)*100
      
      fig,axs = plt.subplots(1,2,figsize=(8,5))
      
      for ax in axs:
          ax.plot(x,y)
          ax.plot(x,y*2)
          ax.plot(x,y*10)
          ax.set_xlim(-10,10)
      
      autoscale_y(axs[1])
      
      axs[0].set_title('Rescaled x-axis')
      axs[1].set_title('Rescaled x-axis\nand used "autoscale_y"')
      
      plt.show()
      

      【讨论】:

      • 这很好,但如果情节中有 axhline()s,它就会失败。我会尝试调整它,因为这正是我想要的。
      • 我将 y_displayed = yd[((xd>=lo) & (xd=lo) & (xd
      • 是的,可惜没有autoscale_axis,可能在最近更新的matplotlib中已经实现了。感谢您的贡献,我使用它并且效果很好!但是,即使您可能想要绘制整个值范围然后放大,@Joe Kinton 的解决方案也更简单,方法是在左侧面板上绘制所有范围,在右侧面板上绘制屏蔽值。
      • 非常好的解决方案!如果 x 轴是 datetime,则只需稍作修改:xd = [dt.toordinal() for dt in line.get_xdata()]
      • 对任何有兴趣的人来说,我已经将此功能调整为适用于任何轴:gist.github.com/ArcturusB/613eaba080a50385fa29e2eff8fe203f。
      【解决方案3】:

      我以@DanHickstein 的回答为基础,涵盖了绘图、散点图和 axhline/axvline 的情况,用于缩放 x 或 y 轴。可以像 autoscale() 一样简单地调用它来处理最新的轴。如果您想编辑它,请fork it on gist。

      def autoscale(ax=None, axis='y', margin=0.1):
          '''Autoscales the x or y axis of a given matplotlib ax object
          to fit the margins set by manually limits of the other axis,
          with margins in fraction of the width of the plot
      
          Defaults to current axes object if not specified.
          '''
          import matplotlib.pyplot as plt
          import numpy as np
          if ax is None:
              ax = plt.gca()
          newlow, newhigh = np.inf, -np.inf
      
          for artist in ax.collections + ax.lines:
              x,y = get_xy(artist)
              if axis == 'y':
                  setlim = ax.set_ylim
                  lim = ax.get_xlim()
                  fixed, dependent = x, y
              else:
                  setlim = ax.set_xlim
                  lim = ax.get_ylim()
                  fixed, dependent = y, x
      
              low, high = calculate_new_limit(fixed, dependent, lim)
              newlow = low if low < newlow else newlow
              newhigh = high if high > newhigh else newhigh
      
          margin = margin*(newhigh - newlow)
      
          setlim(newlow-margin, newhigh+margin)
      
      def calculate_new_limit(fixed, dependent, limit):
          '''Calculates the min/max of the dependent axis given 
          a fixed axis with limits
          '''
          if len(fixed) > 2:
              mask = (fixed>limit[0]) & (fixed < limit[1])
              window = dependent[mask]
              low, high = window.min(), window.max()
          else:
              low = dependent[0]
              high = dependent[-1]
              if low == 0.0 and high == 1.0:
                  # This is a axhline in the autoscale direction
                  low = np.inf
                  high = -np.inf
          return low, high
      
      def get_xy(artist):
          '''Gets the xy coordinates of a given artist
          '''
          if "Collection" in str(artist):
              x, y = artist.get_offsets().T
          elif "Line" in str(artist):
              x, y = artist.get_xdata(), artist.get_ydata()
          else:
              raise ValueError("This type of object isn't implemented yet")
          return x, y
      

      与其前身一样,它有点 hacky,但这是必要的,因为集合和线条有不同的方法来返回 xy 坐标,并且因为 axhline/axvline 只有两个数据点,所以使用起来很棘手。

      它在行动:

      fig, axes = plt.subplots(ncols = 4, figsize=(12,3))
      (ax1, ax2, ax3, ax4) = axes
      
      x = np.linspace(0,100,300)
      noise = np.random.normal(scale=0.1, size=x.shape)
      y = 2*x + 3 + noise
      
      for ax in axes:
          ax.plot(x, y)
          ax.scatter(x,y, color='red')
          ax.axhline(50., ls='--', color='green')
      for ax in axes[1:]:
          ax.set_xlim(20,21)
          ax.set_ylim(40,45)
      
      autoscale(ax3, 'y', margin=0.1)
      autoscale(ax4, 'x', margin=0.1)
      
      ax1.set_title('Raw data')
      ax2.set_title('Specificed limits')
      ax3.set_title('Autoscale y')
      ax4.set_title('Autoscale x')
      plt.tight_layout()
      

      【讨论】:

        【解决方案4】:

        我想补充一下@TomNorway 的出色答案(这为我节省了很多时间),以处理一些艺术家部分由完全由 NaN 组成的情况。

        我所做的所有更改都在里面

        if len(fixed) > 2:
        

        干杯!

        def autoscale(ax=None, axis='y', margin=0.1):
            '''Autoscales the x or y axis of a given matplotlib ax object
            to fit the margins set by manually limits of the other axis,
            with margins in fraction of the width of the plot
        
            Defaults to current axes object if not specified.
            '''
        
            
            if ax is None:
                ax = plt.gca()
            newlow, newhigh = np.inf, -np.inf
        
            for artist in ax.collections + ax.lines:
                x,y = get_xy(artist)
                if axis == 'y':
                    setlim = ax.set_ylim
                    lim = ax.get_xlim()
                    fixed, dependent = x, y
                else:
                    setlim = ax.set_xlim
                    lim = ax.get_ylim()
                    fixed, dependent = y, x
        
                low, high = calculate_new_limit(fixed, dependent, lim)
                newlow = low if low < newlow else newlow
                newhigh = high if high > newhigh else newhigh
        
            margin = margin*(newhigh - newlow)
        
            setlim(newlow-margin, newhigh+margin)
        
        def calculate_new_limit(fixed, dependent, limit):
            '''Calculates the min/max of the dependent axis given 
            a fixed axis with limits
            '''
            if len(fixed) > 2:
                mask = (fixed>limit[0]) & (fixed < limit[1]) & (~np.isnan(dependent)) & (~np.isnan(fixed))
                window = dependent[mask]
                try:
                    low, high = window.min(), window.max()
                except ValueError:  # Will throw ValueError if `window` has zero elements
                    low, high = np.inf, -np.inf
            else:
                low = dependent[0]
                high = dependent[-1]
                if low == 0.0 and high == 1.0:
                    # This is a axhline in the autoscale direction
                    low = np.inf
                    high = -np.inf
            return low, high
        
        def get_xy(artist):
            '''Gets the xy coordinates of a given artist
            '''
            if "Collection" in str(artist):
                x, y = artist.get_offsets().T
            elif "Line" in str(artist):
                x, y = artist.get_xdata(), artist.get_ydata()
            else:
                raise ValueError("This type of object isn't implemented yet")
            return x, y
        

        【讨论】:

          【解决方案5】:
          import numpy as np  # for the test data
          import pandas as pd
          
          # load the data into the dataframe; there are many ways to do this
          df = pd.DataFrame({'x': np.arange(0,101,1), 'y': 300-0.1*np.arange(0,101,1)})
          
          # select and plot the data
          ax = df[df.x.between(50, 100)].plot(x='x', y='y', kind='scatter', figsize=(5, 4))
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-04-06
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多