能否绘制隐式方程?

40
我想在Matplotlib中绘制隐式方程(形如f(x,y)= g(x,y),例如X ^ y = y ^ x)。这是可能的吗?

在Matplotlib中绘制圆锥曲线,Sage Math。 - Trenton McKinney
6个回答

36

我不认为有很好的支持,但你可以尝试类似这样的东西

import matplotlib.pyplot
from numpy import arange
from numpy import meshgrid

delta = 0.025
xrange = arange(-5.0, 20.0, delta)
yrange = arange(-5.0, 20.0, delta)
X, Y = meshgrid(xrange,yrange)

# F is one side of the equation, G is the other
F = Y**X
G = X**Y

matplotlib.pyplot.contour(X, Y, (F - G), [0])
matplotlib.pyplot.show()

请参考API文档中的contour部分:如果第四个参数是一个序列,则指定需要绘制哪些等高线。但是,绘图的质量取决于范围的分辨率,并且有些特征可能永远无法正确显示,通常会在自交点处发生。


这是一个不错的解决方案。我的解决方案更加手动,使用相同的基本概念来获取相同的信息:将隐式方程设置为 f(x, y),使得 f(x, y) = 0 等价于原始的隐式方程,并且分离其零等值线。 - Mike Graham

36

7
新的语法是 plot_implicit(Eq(x**5 + y**5, 1)),这是新文档链接 在此 - Matt Hancock

9

Matplotlib无法绘制方程,它只能绘制一系列点。您可以使用scipy.optimize等工具从隐式方程中数值计算y值和x值(或反之亦然),或者使用其他适当的工具。


例如,这里是一个示例,我在某个区域绘制了隐式方程x ** 2 + x * y + y ** 2 = 10

from functools import partial

import numpy
import scipy.optimize
import matplotlib.pyplot as pp

def z(x, y):
    return x ** 2 + x * y + y ** 2 - 10

x_window = 0, 5
y_window = 0, 5

xs = []
ys = []
for x in numpy.linspace(*x_window, num=200):
    try:
        # A more efficient technique would use the last-found-y-value as a 
        # starting point
        y = scipy.optimize.brentq(partial(z, x), *y_window)
    except ValueError:
        # Should we not be able to find a solution in this window.
        pass
    else:
        xs.append(x)
        ys.append(y)

pp.plot(xs, ys)
pp.xlim(*x_window)
pp.ylim(*y_window)
pp.show()

我认为这不是一个明智的方法。如果变量在x中突然改变,这种方法将在两个相邻点之间产生一条长直线,可能跨越整个图形高度。像 f(x, y) = xg(x, y) = a 这样微不足道的东西将完全失败。(确切的失败模式取决于插值点是否碰到该线或未碰到。) - KeithWM
2
这是一个好的方法——它比其他方法的粗略离散化更准确、更高效地找到零点的位置。 - tom10

6
在sympy中有一个隐式方程(和不等式)绘图器。它是GSoC的一部分创建的,并且它生成作为matplotlib图形实例的绘图。
文档位于http://docs.sympy.org/latest/modules/plotting.html#sympy.plotting.plot_implicit.plot_implicit 自sympy版本0.7.2以来,它可用于:
>>> from sympy.plotting import plot_implicit
>>> p = plot_implicit(x < sin(x)) # also creates a window with the plot
>>> the_matplotlib_axes_instance = p._backend._ax

看起来现在已经发布了。 :) - Andrea Lazzarotto
你的 the_matplotlib_axes_instance 是从哪里来的? - theV0ID
p 是您创建的图表。p._backend._ax 将是轴实例,如果您希望,可以将其引用到一个新变量中,并将其用于任何您想使用 matplotlib 轴实例的地方。 - Krastanov
1
你能否更新这个答案,删除对未来版本(现在已经过时)的引用,我会删除我的过时答案。谢谢。 - Gary Kerr

3

编辑: 如果使用plt.plot()绘制双曲线,则会得到不期望的分支效果。可使用plt.scatter()代替,这仍然有效。然后就没有必要颠倒负值或正值的顺序了,但如果您因某种原因想要使用此代码(而不是使用来自scipy的轮廓图),则使用plt.scatter()仍将起作用

在二维中的隐式函数通常可以写成:

f(x,y)=0

由于我们无法将其写成f(x) = y的形式,因此我们无法从易于编程的离散x集合计算y。但是,可以看出从网格生成的点与真实函数相差多远。

因此,将x和y的网格映射到自定义点密度,并查看每个点满足方程的接近程度。

换句话说,如果我们无法获得f(x,y) = 0,则可以接近0。不是寻找f(x,y) = 0而是寻找f(x,y)>-\epsilon和f(x,y)<\epsilon。

\epsilon 是您的容差,如果此条件符合您的0容差并适当调整网格,则可以绘制函数。

以下代码对于半径为1的圆(f(x,y)= x^2 + y^2 -1 = 0)就是那样做的。我使用符号dr表示\epsilon。

此外,为了确保plt.plot函数按正确顺序连接线条,我对负y值的x值使用了反转版本。这样,f(x,y)的计算将在顺时针循环中完成,以便最近的值一个接一个地出现。否则,来自函数相反两侧的线条将连接在一起,并且看起来稍微填充。

import numpy as np
import matplotlib.pyplot as plt
r = 1 #arbitrary radius to set up the span of points
points = 250 
dr = r/points #epsilon window 

x=list(np.linspace(-5*r,5*r,5*points+1)) #setting up the x,y grid
y=x

xreversed = reversed(x) #reversing the array

x_0=[] #placeholder arrays
y_0=[]

for i in x:
    for j in y:
        if i**2 + j**2 -1 < dr and i**2+j**2 -1 > -dr  and j >= 0: #positive values of y
            x_0.append(i)
            y_0.append(j)
for i in xreversed:            
    for j in y:
        if i**2+j**2 -1 < dr and i**2+j**2 -1 > -dr  and j < 0: #negative values of y, using x reversed
            x_0.append(i)
            y_0.append(j)

plt.plot(x_0,y_0)
plt.show()


1
非常感谢Steve、Mike和Alex。我已经采用了Steve的解决方案(请参见下面的代码)。我唯一剩下的问题是等高线图出现在我的网格线后面,而不是像普通图表那样可以通过zorder强制置于前面。如果有更多帮助,将不胜感激。
祝好, Geddes
import matplotlib.pyplot as plt 
from matplotlib.ticker import MultipleLocator, FormatStrFormatter
import numpy as np 

fig = plt.figure(1) 
ax = fig.add_subplot(111) 

# set up axis 
ax.spines['left'].set_position('zero') 
ax.spines['right'].set_color('none') 
ax.spines['bottom'].set_position('zero') 
ax.spines['top'].set_color('none') 
ax.xaxis.set_ticks_position('bottom') 
ax.yaxis.set_ticks_position('left') 

# setup x and y ranges and precision
x = np.arange(-0.5,5.5,0.01) 
y = np.arange(-0.5,5.5,0.01)

# draw a curve 
line, = ax.plot(x, x**2,zorder=100) 

# draw a contour
X,Y=np.meshgrid(x,y)
F=X**Y
G=Y**X
ax.contour(X,Y,(F-G),[0],zorder=100)

#set bounds 
ax.set_xbound(-1,7)
ax.set_ybound(-1,7) 

#produce gridlines of different colors/widths
ax.xaxis.set_minor_locator(MultipleLocator(0.2)) 
ax.yaxis.set_minor_locator(MultipleLocator(0.2)) 
ax.xaxis.grid(True,'minor',linestyle='-')
ax.yaxis.grid(True,'minor',linestyle='-') 

minor_grid_lines = [tick.gridline for tick in ax.xaxis.get_minor_ticks()] 
for idx,loc in enumerate(ax.xaxis.get_minorticklocs()): 
    if loc % 2.0 == 0:
        minor_grid_lines[idx].set_color('0.3')
        minor_grid_lines[idx].set_linewidth(2)
    elif loc % 1.0 == 0:
        minor_grid_lines[idx].set_c('0.5')
        minor_grid_lines[idx].set_linewidth(1)
    else:
        minor_grid_lines[idx].set_c('0.7')
        minor_grid_lines[idx].set_linewidth(1)

minor_grid_lines = [tick.gridline for tick in ax.yaxis.get_minor_ticks()] 
for idx,loc in enumerate(ax.yaxis.get_minorticklocs()): 
    if loc % 2.0 == 0:
        minor_grid_lines[idx].set_color('0.3')
        minor_grid_lines[idx].set_linewidth(2)
    elif loc % 1.0 == 0:
        minor_grid_lines[idx].set_c('0.5')
        minor_grid_lines[idx].set_linewidth(1)
    else:
        minor_grid_lines[idx].set_c('0.7')
        minor_grid_lines[idx].set_linewidth(1)

plt.show()

@Geddes,看起来对于轮廓符合zorder的支持最近才被添加到matplotlib源代码中。从他们的SVN trunk上:http://matplotlib.svn.sourceforge.net/viewvc/matplotlib?view=rev&revision=8098 - Mark

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接