views:

337

answers:

5

Hello all - many thanks again to people who have kindly offered help (especially to Mark for his outstanding response to my previous question).

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?

All the best, Geddes

+4  A: 

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

X,Y=meshgrid(xrange,yrange)
F=X**Y
G=Y**X
contour(X,Y,(F-G),[0])

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.

Steve
This is a good solution. My solution is a more manual way of getting at the same piece of information using the same underlying concept: setting up the implicit equation as f(x, y) such that f(x, y) = 0 is equivalent to the original implicit equation and isolating its zero contour.
Mike Graham
+2  A: 

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()
Mike Graham
+1  A: 

If you're willing to use something other than matplotlib (but still python), there's sage:

An example: http://sagenb.org/home/pub/1806

Documentation for implicit_plot

The Sage Homepage

Alex
A: 

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()
Geddes
Mark
+1  A: 

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

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

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