You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Sympy实现同一图中曲线与切线独立设置xlim问题求助

问题:函数曲线与切线独立设置xlim及切线长度不符合预期

我需要绘制函数曲线及其在点p处的切线,希望分别设置曲线和切线的xlim参数。代码中设定切线半长为1,但实际显示效果不符合预期。

原代码

from sympy import init_printing, symbols, N, plot
from sympy import diff
from sympy import log, cos, atan

init_printing()
x = symbols('x')

# 绘制点(p_x, p_y)处长度为l的切线
def plot_line(p_x, p_y, x, a, l):
    # 计算截距b,构建切线表达式
    b = p_y - a*p_x
    t = a*x + b
    
    # 限制线段长度
    r = atan(a) # 弧度制角度
    dx = N(l*cos(r)) # x方向半范围
    lims = {'xlim': (p_x-dx, p_x+dx)}
    
    # 创建绘图对象
    t_plot = plot(t, show=False, **lims)
    return t_plot

# 目标函数
y = 2.1*log(x)

# 切点坐标
px = 7
py = y.subs(x, px)

# 绘制曲线和切点
marker = {'args': [px, py, 'bo']}
lims = {'xlim': (0,10), 'ylim': (0,5)}
plots = plot(y, markers=[marker], show=False, **lims)

# 求导并绘制切线
y_d = diff(y)
a = y_d.subs(x, px)
plots.extend(plot_line(px, py, x, a, 1))

# 调整显示并绘图
plots.aspect_ratio=(1,1)
plots.show()

问题分析

  1. 切线长度计算逻辑错误:原代码通过三角函数推导x方向半增量,混淆了"切线线段长度"和"x方向增量"的概念。切线半长l是线段的几何长度,正确计算应使用勾股定理:已知斜率a=dy/dx,则dx² + (a·dx)² = l²,推导得dx = l / sqrt(1+a²)。
  2. xlim参数被主图覆盖:SymPy的plot.extend()方法会让子图继承主图的xlim设置,导致切线的独立xlim参数失效。

修正方案

方案1:修正长度计算并指定绘图范围

修改切线长度计算逻辑,同时直接指定切线的x取值范围,避免主图参数覆盖:

from sympy import init_printing, symbols, N, plot, sqrt
from sympy import diff
from sympy import log

init_printing()
x = symbols('x')

def plot_line(p_x, p_y, x, a, l):
    b = p_y - a*p_x
    t = a*x + b
    
    # 用勾股定理计算x方向半增量
    dx = N(l / sqrt(1 + a**2))
    x_start = p_x - dx
    x_end = p_x + dx
    
    # 直接指定切线的x取值范围,而非仅设置xlim
    t_plot = plot(t, (x, x_start, x_end), show=False)
    return t_plot

# 目标函数与切点
y = 2.1*log(x)
px = 7
py = y.subs(x, px)

# 绘制曲线和切点
marker = {'args': [px, py, 'bo']}
plots = plot(y, markers=[marker], show=False, xlim=(0,10), ylim=(0,5))

# 添加切线
y_d = diff(y)
a = y_d.subs(x, px)
plots.extend(plot_line(px, py, x, a, 1))

plots.aspect_ratio=(1,1)
plots.show()

方案2:参数化绘制切线线段(更可靠)

直接计算切线的两个端点,用参数化绘图绘制线段,完全不受主图xlim影响:

from sympy import init_printing, symbols, N, plot, plot_parametric, sqrt
from sympy import diff
from sympy import log

init_printing()
x = symbols('x')

def plot_tangent_segment(p_x, p_y, a, l):
    # 计算x方向半增量
    dx = N(l / sqrt(1 + a**2))
    # 参数化线段:t从0到1时,从左端点移动到右端点
    t = symbols('t')
    seg_x = p_x + dx*(2*t - 1)
    seg_y = p_y + a*dx*(2*t - 1)
    return plot_parametric(seg_x, seg_y, (t, 0, 1), show=False, line_color='red')

# 目标函数与切点
y = 2.1*log(x)
px = 7
py = y.subs(x, px)

# 绘制曲线和切点
marker = {'args': [px, py, 'bo']}
plots = plot(y, markers=[marker], show=False, xlim=(0,10), ylim=(0,5))

# 添加切线线段
y_d = diff(y)
a = y_d.subs(x, px)
plots.extend(plot_tangent_segment(px, py, a, 1))

plots.aspect_ratio=(1,1)
plots.show()

效果说明

修正后,切线的半长将严格为1,曲线的xlim保持(0,10)不受切线范围影响,实现了两者独立设置的需求。

内容的提问来源于stack exchange,提问作者mins

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 16:15:26