是否可以绘制隐式方程?

发布于 2024-08-25 22:26:17 字数 67 浏览 11 评论 0原文

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

I would like to plot implicit equations (of the form f(x, y)=g(x, y) eg. X^y=y^x) in Matplotlib. Is this possible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

护你周全 2024-09-01 22:26:17

既然你用 sympy 标记了这个问题,我就举一个这样的例子。

来自文档: http://docs.sympy.org/latest/modules/plotting.html

from sympy import var, plot_implicit
var('x y')
plot_implicit(x*y**3 - y*x**3)

Since you've tagged this question with sympy, I will give such an example.

From the documentation: http://docs.sympy.org/latest/modules/plotting.html.

from sympy import var, plot_implicit
var('x y')
plot_implicit(x*y**3 - y*x**3)
莫言歌 2024-09-01 22:26:17

我不相信对此有很好的支持,但您可以尝试类似的方法,

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 docs for contour:如果第四个参数是一个序列,那么它指定要绘制哪些等高线。但绘图的好坏取决于范围的分辨率,并且某些特征可能永远不会正确,通常是在自交点处。

I don't believe there's very good support for this, but you could try something like

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()

See the API docs for contour: if the fourth argument is a sequence then it specifies which contour lines to plot. But the plot will only be as good as the resolution of your ranges, and there are certain features it may never get right, often at self-intersection points.

秋意浓 2024-09-01 22:26:17

matplotlib 不绘制方程;它绘制一系列点。您可以使用诸如 scipy .optimize 之类的工具,根据隐式方程的 x 值(反之亦然)以数字方式计算 y 点,或者使用任意数量的其他适当工具。


例如,下面是我在某个区域绘制隐式方程 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()

matplotlib does not plot equations; it plots serieses of points. You can use a tool like scipy​.optimize to numerically calculate y points from x values (or vice versa) of implicit equations numerically or any number of other tools as appropriate.


For example, here is an example where I plot the implicit equation x ** 2 + x * y + y ** 2 = 10 in a certain region.

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()
兮子 2024-09-01 22:26:17

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

There is an implicit equation (and inequality) plotter in sympy. It is created as a part of GSoC and it produces the plots as matplotlib figure instances.

Docs at http://docs.sympy.org/latest/modules/plotting.html#sympy.plotting.plot_implicit.plot_implicit

Since sympy version 0.7.2 it is available as:

>>> 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
卖梦商人 2024-09-01 22:26:17

编辑:如果您使用 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 函数以正确的顺序连接线条,我使用 x 值的反转版本作为负 y 值。这样,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()

Edit: If you plot a hyperbola using plt.plot() then you will get the undesired branching effect. plt.scatter() in its place should still work. Then there is no need to reverse the order of negative or positive values, but if you wanted to use this code for some reason (instead of using contour plot from scipy) it will work anyways with plt.scatter()

An implicit function in two dimensions in general can be written as:

f(x,y)=0

Since we cannot write this as f(x) = y, then we cannot compute y from an easily programmable set of discrete x. It is possible, however, to see how close a point generated from a grid is from the true function.

So make a grid of x and y to a custom point density and see how close each point is to satisfying the equation.

In other words, if we can't get f(x,y) =0, perhaps we can get close to 0. Instead of looking for f(x,y) =0 look for f(x,y) > -\epsilon and f(x,y) < \epsilon.

\epsilon is your tolerance and if this condition fits within your tolerance of 0 and tuning the grid appropriately you can get your function plotted.

The code below does just that for a circle of radius 1 (f(x,y)= x^2 + y^2 -1 = 0). I used the symbol dr for \epsilon.

Also, to make sure the plt.plot function connects the lines in the correct order, I use a reversed version of the x values for the negative y values. That way, the evaluation of f(x,y) is done in a clockwise loop so that the nearest values are one after another. Without this, lines from opposite sides of the function would connect and it would appear slightly filled in.

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()

韬韬不绝 2024-09-01 22:26:17

非常感谢史蒂夫、迈克、亚历克斯。我已经同意史蒂夫的解决方案(请参阅下面的代码)。我唯一剩下的问题是等值线图出现在网格线后面,而不是常规图,我可以使用 zorder 将其强制到前面。任何更多半非常感激。

干杯,
格德斯

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()

Many thanks Steve, Mike, Alex. I have gone along with Steve's solution (please see code below). My only remaining issue is that the contour plot appears behind my gridlines, as opposed to a regular plot, which I can force to the front with zorder. Any more halp greatly appreciated.

Cheers,
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()
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文