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()
问题分析
- 切线长度计算逻辑错误:原代码通过三角函数推导x方向半增量,混淆了"切线线段长度"和"x方向增量"的概念。切线半长
l是线段的几何长度,正确计算应使用勾股定理:已知斜率a=dy/dx,则dx² + (a·dx)² = l²,推导得dx = l / sqrt(1+a²)。 - 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
相关产品推荐
相关产品推荐

