I have an implicit function, say x**2 - y = 0 (to simplify), of which I want to obtain a plot for a certain range of x values.
sympy.plot_implicit usually gives some spreading of the lines that I am not happy with.
I would like to have access to the plotted values, and so pyplot.plot is preferable to me. Usually I use the following piece of code to get my explicit Sympy functions plotted, but I am unsure how to use something similar for exp = sym.Eq(x**2 - y, 0). Does anyone have a solutions for this?
import sympy as sym
import numpy as np
from matplotlib import pyplot as plt
x, y = sym.symbols('x y', nonnegative=True)
exp = x**2
# Plot using a numpy-ready function
x_arr = np.linspace(-2, 2, 100)
exp_func = sym.lambdify(x, exp, 'numpy')
exp_arr = exp_func(x_arr)
plt.plot(x_arr, exp_arr)
PS: my real expression is b_sim (see below) and I want the plot for the equation b_sim = -1. With sym.plot_implicit(b_sim + 1, (n,0.225,1.5), (h, -1.1, 1.1)) one can see the lines spreading I dislike. Following Oscar Benjami's tips here, I attempted the following piece of code that is giving an error for roots.
from sympy import *
h, nu = symbols('h nu', nonnegative=True)
b_sim = 1.0*cos(pi*sqrt(1 - h)/(2*nu))*cos(pi*sqrt(h + 1)/(2*nu)) - 1.0*sin(pi*sqrt(1 - h)/(2*nu))*sin(pi*sqrt(h + 1)/(2*nu))/sqrt(1 - h**2)
eq = Eq(b_sim + 1, 0)
sols = roots(eq, h)
sym.plot(*sols, (nu, 0.225, 1.5), ylim=(-1.1, 1.1))
The line spread of plot_implicit is caused by the adaptive algorithm. If you set the option adaptive=False the plotting module would use a meshgrid approach. However, due to the implementation, the figure is likely not going to be good (too much "segmentation").
This is how you can do it with Numpy and Matplotlib:
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
import numpy as np
expr = eq.rewrite(Add)
f = lambdify([nu, h], expr)
n = 2000j
nnu, hh = np.mgrid[0.225:1.5:n, -1.1:1.1:n]
res = f(nnu, hh)
plt.figure()
cmap = ListedColormap(["tab:blue", "tab:blue"])
plt.contour(nnu, hh, res, levels=[0], cmap=cmap)
plt.show()
To have access to the plotted values of a sympy function plot, in this case the coordinates of lines2d plot, is simple.
Here is the code that plots the function.
import matplotlib.pyplot as plt
from sympy import symbols
import numpy as np
import sympy
x, y = symbols('x y', nonnegative=True)
exp = x**2
# Plot using a numpy-ready function
x_arr = np.linspace(-2, 2, 10) #small number for demo
exp_func = sympy.lambdify(x, exp, 'numpy')
exp_arr = exp_func(x_arr)
plt.figure(figsize=(4, 3))
lines2d = plt.plot(x_arr, exp_arr)
In the code above, lines2d is a list of line2d objects. To get the coordinates from the 1st (only one in this case), do this:
xys = lines2d[0].get_xydata()
And you can use it to plot with this code:-
fig = plt.figure(figsize=(4, 3))
ax = fig.add_subplot()
ax.plot(xys[:,0], xys[:,1])
Related
I am trying to get the following result using (numerical) double integration of the two partial derivatives:
def deriv_y(x,y):
return -2*x*y*np.exp(-x**2-y**2)
and
def deriv_x(x,y):
return (1-2*x**2)*np.exp(-x**2-y**2)
The function is an introductory example suitable for novices. The derivatives and some properties are provided here by Wolfram Alpha.
This function is also used by this tutorial "3D Surface Plots".
Successfully I could integrate the function along the x-Axis by implementing:
from scipy import integrate
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
arr_x = np.arange(-5,5,0.5)
arr_y = np.arange(-5,5,0.5)
X,Y = np.meshgrid(arr_x, arr_y)
Z = np.zeros([arr_x.size, arr_y.size])
def deriv_y(x,y):
return 2*x*y*np.exp(-x**2-y**2)
def deriv_x(x,y):
return (1-2*x**2)*np.exp(-x**2-y**2)
def bounds_x(y):
return [y, np.inf]
def bounds_y():
return [0, np.inf]
for idx_x, x in enumerate(arr_x):
for idx_y, y in enumerate(arr_y):
m = integrate.nquad(deriv_x, [bounds_x(y), bounds_y])
Z[idx_x][idx_y] = m[0]
fig = plt.figure(figsize=(14,10))
ax1 = fig.add_subplot(111, projection='3d')
mycmap = plt.get_cmap('gist_earth')
surf1 = ax1.plot_surface(X, Y, Z, cmap=mycmap)
fig.colorbar(surf1, ax=ax1, shrink=0.5, aspect=5)
plt.show()
The output seems to be correct:
I have read the official documentation "Integration (scipy.integrate)". Then I tried to chain both functions deriv_x and deriv_y in many combinations. In any case a got some wrong plots. I would be greateful if anyone could help me, doing it right.
I need to use the streamplot function in matplotlib with coordinates x, p in the order as in the code below.
import numpy as np
import matplotlib.pyplot as plt
size = 2
x, p = np.mgrid[-size:size:100j, -size:size:100j]
x_force = p
p_force = x**3
fig = plt.figure()
ax = fig.gca()
ax.streamplot(x, p, x_force, p_force, density=[0.5, 1])
plt.show()
This produces an error: ValueError: The rows of 'x' must be equal.
Quite surprisingly, changing the order of x and p in the streamplot solves the problem.
ax.streamplot(p, x, p_force, x_force, density=[0.5, 1])
Why does this happen please? How can I make the plot with coordinates in my chosen order?
Changing the order of x and p in the meshgrid command solves the problem:
p, x = np.mgrid[-size:size:100j, -size:size:100j]
What is the best way to create a Sympy equation, do something like take the derivative, and then plot the results of that equation?
I have my symbolic equation, but can't figure out how to make an array of values for plotting. Here's my code:
from sympy import symbols
import matplotlib.pyplot as mpl
t = symbols('t')
x = 0.05*t + 0.2/((t - 5)**2 + 2)
nums = []
for i in range(1000):
nums.append(t)
t += 0.02
plotted = [x for t in nums]
mpl.plot(plotted)
mpl.ylabel("Speed")
mpl.show()
In my case I just calculated the derivative of that equation, and now I want to plot the speed x, so this is fairly simplified.
You can use numpy.linspace() to create the values of the x axis (x_vals in the code below) and lambdify().
from sympy import symbols
from numpy import linspace
from sympy import lambdify
import matplotlib.pyplot as mpl
t = symbols('t')
x = 0.05*t + 0.2/((t - 5)**2 + 2)
lam_x = lambdify(t, x, modules=['numpy'])
x_vals = linspace(0, 10, 100)
y_vals = lam_x(x_vals)
mpl.plot(x_vals, y_vals)
mpl.ylabel("Speed")
mpl.show()
(improvements suggested by asmeurer and MaxNoe)
Alternatively, you can use sympy's plot():
from sympy import symbols
from sympy import plot
t = symbols('t')
x = 0.05*t + 0.2/((t - 5)**2 + 2)
plot(x, (t, 0, 10), ylabel='Speed')
Using SymPy
You can use directly the plotting functions of SymPy:
from sympy import symbols
from sympy.plotting import plot as symplot
t = symbols('t')
x = 0.05*t + 0.2/((t - 5)**2 + 2)
symplot(x)
Most of the time it uses matplotlib as a backend.
I have numerically solved the Lorenz equations using SciPy with the script:
# Lorenz Equations SciPy solver
import numpy as np
from scipy import integrate
from math import cos
from matplotlib import pyplot as plt
a, b = 0, 100
sigma, rho, beta = 10, 28, 8/3
N = 1000000
h = (b-a) / float(N)
def solvr(Y, t):
return [sigma*(Y[1]-Y[0]), Y[0]*(rho-Y[2])-Y[1], Y[0]*Y[1]-beta*Y[2]]
t = np.arange(a, b, h)
asol = integrate.odeint(solvr, [0, 1, 1], t)
x = asol[:,0]
y = asol[:,1]
z = asol[:,2]
Now what I would like to do is plot x, y and z (which are all Numpy ndarrays, if you are unsure) against each other in a 3D line (or wireframe) plot. I think this would have to be done using matplotlib, but I am not picky, so long as you give me a solution that will plot the data in 3D I do not care what modules I need to import.
Here is the Lorenz attractor both in 3D and animated. The script is in the following link (along with many goodies) in Jake VanderPlas' Pythonic Perambulations. You can learn a lot by going line-by-line through the script - it's an elegant use of matplotlib objects.
https://jakevdp.github.io/blog/2013/02/16/animating-the-lorentz-system-in-3d/
I added these two lines just before return in the animate function, and then used ImageJ to import the "image stack" and save the "animated GIF":
fname = "Astro_Jake_" + str(i+10000)[1:]
fig.savefig(fname)
Note: For OSX it seems to be necessary to set blit = False in animation.FuncAnimation.
Here is a minimal, simplified example of plotting lines in 3D based on the above:
def lorentz_deriv((x, y, z), t0, sigma=10., beta=8./3, rho=28.0):
"""Compute the time-derivative of a Lorentz system."""
return [sigma * (y - x), x * (rho - z) - y, x * y - beta * z]
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from scipy.integrate import odeint as ODEint
x = np.linspace(0, 20, 1000)
y, z = 10.*np.cos(x), 10.*np.sin(x) # something simple
fig = plt.figure()
ax = fig.add_subplot(1,2,1,projection='3d')
ax.plot(x, y, z)
# now Lorentz
times = np.linspace(0, 4, 1000)
start_pts = 30. - 15.*np.random.random((20,3)) # 20 random xyz starting values
trajectories = []
for start_pt in start_pts:
trajectory = ODEint(lorentz_deriv, start_pt, times)
trajectories.append(trajectory)
ax = fig.add_subplot(1,2,2,projection='3d')
for trajectory in trajectories:
x, y, z = trajectory.T # transpose and unpack
# x, y, z = zip(*trajectory) # this also works!
ax.plot(x, y, z)
plt.show()
There is a brief example / tutorial on how to do wireframe plots (as well as 3d scatter) at the matplotlib site http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#wireframe-plots
I am trying to make a polar plot of 1/t. What I have so far is below (which may be wrong). How can I finish this or make it work?
from pylab import *
import matplotlib.pyplot as plt
theta = arange(0, 6 * pi, 0.01)
def f(theta):
return 1 / theta
I think the problem is that your first value of f(theta) is 1/0 = inf
theta = np.arange(0, 6*np.pi, .01)[1:]
def f(x):
return 1/x
plt.polar(theta, f(theta))
and it looks even nicer if you zoom in:
from mpl_toolkits.axes_grid.axislines import SubplotZero
from matplotlib.ticker import MultipleLocator, FuncFormatter
import matplotlib.pyplot as plt
import numpy as np
plt.ion()
fig = plt.figure(1)
ax = SubplotZero(fig, 111)
fig.add_subplot(ax)
for dir in ax.axis:
ax.axis[dir].set_visible(dir.endswith("zero"))
ax.set_xlim(-.35,.4)
ax.set_ylim(-.25,.45)
ax.set_aspect('equal')
tick_format = lambda x, i: '' if x == 0.0 else '%.1f' % x
for a in [ax.xaxis, ax.yaxis]:
a.set_minor_locator(MultipleLocator(0.02))
a.set_major_formatter(FuncFormatter(tick_format))
theta = np.arange(2*np.pi/3,6*np.pi,0.01)
r = 1 / theta
ax.plot(r*np.cos(theta), r*np.sin(theta), lw=2)
plt.show()
raw_input()
If you want a square plot like Mathematica gave you, the standard plot function just takes an array of x values and an array of y values. Here, f(theta) is the radius, and cos and sin give the x and y directions, so
plt.plot(f(theta)*cos(theta), f(theta)*sin(theta))
should do the job. This will show all of the data, rather than a cleverly chosen subset like in Mathematica, so you might want to limit it. For example:
plt.xlim((-0.35,0.43))
plt.ylim((-0.23,0.45))
gives me the ranges in your version.