【问题标题】:How to add line based on slope and intercept in Matplotlib?如何在 Matplotlib 中添加基于斜率和截距的线?
【发布时间】:2011-12-17 23:31:00
【问题描述】:

在 R 中,有一个名为 abline 的函数,其中可以根据截距(第一个参数)和斜率(第二个参数)的规范在绘图上绘制一条线。例如,

plot(1:10, 1:10)
abline(0, 1)

截距为 0 且斜率为 1 的线跨越整个绘图范围。 Matplotlib中有这样的功能吗?

【问题讨论】:

  • 不,没有。这将是一个方便的功能。有 axvlineaxvspanaxhlineaxhspan,它们是类似的垂直和水平函数,但在 matplotlib 中通常的方法是在给定的斜率处绘制一条线(这意味着你最终会如果您以交互方式工作,请放大它。)。虽然框架 (matplotlib.transforms) 就在那里,但“正确”的做法(即无论您在哪里缩放,它总是跨越轴)实际上有点复杂。
  • 是的,很遗憾... Matlab 也没有这个功能。另一方面,R 的绘图是静态的(base 图形系统存在 abline)所以不用担心那里(我想这是一件好事和坏事)。

标签: python matplotlib


【解决方案1】:

我想对于 (0, 1)(intercept, slope) 的情况,可以使用和扩展以下函数以适应其他斜率和截距,但如果轴限制发生更改或重新打开自动缩放,则不会重新调整。

def abline():
    gca = plt.gca()
    gca.set_autoscale_on(False)
    gca.plot(gca.get_xlim(),gca.get_ylim())

import matplotlib.pyplot as plt
plt.scatter(range(10),range(10))
abline()
plt.draw()

【讨论】:

  • 好吧,如果你只想要一条从左下角到右上角的线,不管你如何缩放,那么你可以做plt.plot([0,1],[0,1], transform=plt.gca().transAxes)。但是,这不会代表数据坐标中 1 比 1 的斜率,并且它会总是从左下角到右上角,无论您缩放到什么...就像您说的那样,更通用的abline 替换更难交互使用...
  • 啊,这个transAxes很有趣。我可以想象我会在某个时候使用它......(我经常有很多 xlim=ylim 的情节,或者应该是)。
【解决方案2】:

如果不使用回调,我想不出办法,但这似乎工作得很好。

import numpy as np
from matplotlib import pyplot as plt


class ABLine2D(plt.Line2D):

    """
    Draw a line based on its slope and y-intercept. Additional arguments are
    passed to the <matplotlib.lines.Line2D> constructor.
    """

    def __init__(self, slope, intercept, *args, **kwargs):

        # get current axes if user has not specified them
        if not 'axes' in kwargs:
            kwargs.update({'axes':plt.gca()})
        ax = kwargs['axes']

        # if unspecified, get the current line color from the axes
        if not ('color' in kwargs or 'c' in kwargs):
            kwargs.update({'color':ax._get_lines.color_cycle.next()})

        # init the line, add it to the axes
        super(ABLine2D, self).__init__([], [], *args, **kwargs)
        self._slope = slope
        self._intercept = intercept
        ax.add_line(self)

        # cache the renderer, draw the line for the first time
        ax.figure.canvas.draw()
        self._update_lim(None)

        # connect to axis callbacks
        self.axes.callbacks.connect('xlim_changed', self._update_lim)
        self.axes.callbacks.connect('ylim_changed', self._update_lim)

    def _update_lim(self, event):
        """ called whenever axis x/y limits change """
        x = np.array(self.axes.get_xbound())
        y = (self._slope * x) + self._intercept
        self.set_data(x, y)
        self.axes.draw_artist(self)

【讨论】:

  • 小改进:交换线: ax.figure.canvas.draw() 和 self._update_lim(None) 以便实际更新绘图而无需单击窗口
  • @tal 最后在我的 matplotlib 版本(1.4.3)上,在调用self.axes.draw_artist(self) 之前至少需要渲染一次父轴,否则我会在@987654324 线上得到一个AssertionError @在Axes.draw_artist。在调用_update_lim 之后,您始终可以插入额外的抽奖。我通常从一个方便的函数内部初始化ABLine,而不是直接实例化它。
【解决方案3】:

我知道这个问题已经有几年了,但由于没有公认的答案,我会添加适合我的。

您可以只在图表中绘制值,然后为最佳拟合线的坐标生成另一组值,并将其绘制在原始图表上。例如,看下面的代码:

import matplotlib.pyplot as plt
import numpy as np

# Some dummy data
x = [1, 2, 3, 4, 5, 6, 7]
y = [1, 3, 3, 2, 5, 7, 9]

# Find the slope and intercept of the best fit line
slope, intercept = np.polyfit(x, y, 1)

# Create a list of values in the best fit line
abline_values = [slope * i + intercept for i in x]

# Plot the best fit line over the actual values
plt.plot(x, y, '--')
plt.plot(x, abline_values, 'b')
plt.title(slope)
plt.show()

【讨论】:

    【解决方案4】:
    X = np.array([1, 2, 3, 4, 5, 6, 7])
    Y = np.array([1.1,1.9,3.0,4.1,5.2,5.8,7])
    
    scatter (X,Y)
    slope, intercept = np.polyfit(X, Y, 1)
    plot(X, X*slope + intercept, 'r')
    

    【讨论】:

      【解决方案5】:

      其中许多解决方案都专注于在图中添加一条适合数据的线。这是基于斜率和截距向绘图添加任意线的简单解决方案。

      import matplotlib.pyplot as plt 
      import numpy as np    
      
      def abline(slope, intercept):
          """Plot a line from slope and intercept"""
          axes = plt.gca()
          x_vals = np.array(axes.get_xlim())
          y_vals = intercept + slope * x_vals
          plt.plot(x_vals, y_vals, '--')
      

      【讨论】:

      • 我不敢相信这个功能没有直接包含在matplotlib 中。如果制作这样一个包,这似乎是人们可能实现的第一件事。
      • @ijoseph 欢迎来到分享和关怀的世界! “每一件优秀的软件工作都始于抓住开发人员的个人之痒”——埃里克·S·雷蒙德。去向Matplotlib project发送拉取请求!
      • 只是为了完整性,它需要在脚本开头import matplotlib.pyplot as plt import numpy as np
      • 感谢您的回答。我冒昧地添加了一个答案,该答案修改了您的代码,以确保斜线不会扩大原始绘图区域。
      【解决方案6】:

      这是我想出的一个可能的解决方法:假设我将截距坐标存储为 x_intercepty_intercept,并将斜率 (m) 保存为my_slope 是通过著名的方程式 m = (y2-y1)/(x2-x1) 或您设法找到的任何方式找到的。 p>

      使用另一个著名的直线方程y = mx + q,我定义了函数find_second_point,它首先计算q (因为 mxy 已知)然后计算属于该线的另一个随机点.

      一旦我有了这两点(最初的x_intercepty_intercept 和新发现的new_xnew_y),我只需通过这两点绘制线段。代码如下:

      import numpy as np
      import matplotlib.pyplot as plt
      
      x_intercept = 3  # invented x coordinate
      y_intercept = 2  # invented y coordinate
      my_slope = 1  # invented slope value
      
      def find_second_point(slope,x0,y0):
          # this function returns a point which belongs to the line that has the slope 
          # inserted by the user and that intercepts the point (x0,y0) inserted by the user
          q = y0 - (slope*x0)  # calculate q
          new_x = x0 + 10  # generate random x adding 10 to the intersect x coordinate
          new_y = (slope*new_x) + q  # calculate new y corresponding to random new_x created
      
          return new_x, new_y  # return x and y of new point that belongs to the line
      
      # invoke function to calculate the new point
      new_x, new_y = find_second_point(my_slope , x_intercept, y_intercept)
      
      plt.figure(1)  # create new figure
      plt.plot((x_intercept, new_x),(y_intercept, new_y), c='r', label='Segment')
      plt.scatter(x_intercept, y_intercept, c='b', linewidths=3, label='Intercept')
      plt.scatter(new_x, new_y, c='g', linewidths=3, label='New Point')
      plt.legend()  # add legend to image
      
      plt.show()
      

      这是代码生成的图片:

      【讨论】:

        【解决方案7】:

        看起来这个功能将成为版本3.3.0的一部分:

        matplotlib.axes.Axes.axline

        例如,您将能够使用点(0, 0)(1, 1) 绘制一条红线

        axline((0, 0), (1, 1), linewidth=4, color='r')
        

        【讨论】:

          【解决方案8】:

          kite.com启发的简短回答:

          plt.plot(x, s*x + i) 
          

          可重现的代码:

          import numpy as np
          import matplotlib.pyplot as plt
          i=3        # intercept
          s=2        # slope
          x=np.linspace(1,10,50)      # from 1 to 10, by 50
          plt.plot(x, s*x + i)        # abline
          plt.show()
          

          【讨论】:

            【解决方案9】:

            我想扩展 David Marx 的答案,我们确保斜线不会在原始绘图区域上扩展。 由于 x 轴限制用于计算倾斜线的 y 数据,因此我们需要确保计算的 y 数据不会扩展给定的 ymin - ymax 范围。如果它确实裁剪了显示的数据。

            def abline(slope, intercept,**styles):
            """Plot a line from slope and intercept"""
            
            axes = plt.gca()
            xmin,xmax = np.array(axes.get_xlim())
            ymin,ymax = np.array(axes.get_ylim()) # get also y limits
            x_vals = np.linspace(xmin,xmax,num=1000) #increased sampling (only actually needed for large slopes)
            y_vals = intercept + slope * x_vals
            locpos = np.where(y_vals<ymax)[0] # if data extends above ymax
            locneg = np.where(y_vals>ymin)[0] # if data extends below ymin
            # select most restricitive condition 
            if len(locpos) >= len(locneg):
                loc = locneg
            else: 
                loc = locpos
            plt.plot(x_vals[loc], y_vals[loc], '--',**styles)
            return y_vals
            

            【讨论】:

              【解决方案10】:

              您可以简单地创建一个列表,其中包含从特定截距和斜率获得的直线方程。将这些值放在一个列表中,并根据您想要的任何一组数字绘制它。例如-(Lr是线性回归模型)

              te= []
              for i in range(11):
                  te.append(Lr.intercept_ + Lr.coef_*i)
              plt.plot(te, '--')
              

              完成工作。

              【讨论】:

                【解决方案11】:

                截至 2021 年,在 matplotlib 3.3.4 中,它支持绘制具有斜率值和点的线。

                fig, ax = plt.subplots()
                
                ax.axline((0, 4), slope=3., color='C0', label='by slope')
                ax.set_xlim(0, 1)
                ax.set_ylim(3, 5) 
                ax.legend()
                

                【讨论】:

                  猜你喜欢
                  • 2022-01-03
                  • 2017-06-13
                  • 2014-08-26
                  • 2018-05-08
                  • 2012-10-18
                  • 2018-01-19
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多