Matplotlib - Passing an equation through a parameter into a function - python

I am writing a program where the user enters a mathematical equation (e.g. x^2 + 2x + 2) and the function plotFunction will plot it out on a graph using matplotlib.
When I enter a parameter such as "x2" it returns the error:
ValueError: Illegal format string "x2"; two marker symbols
import matplotlib.pyplot as plt
import numpy as np
class Plotter:
def __init__(self):
pass
def plotFunction(self, func):
x = np.arange(-100, 100)
y = func
plt.plot(x, y)
plt.show()
p1 = Plotter()
p1.plotFunction("x**2")

This is not exactly ideal, however, it is what I came up with that works right now.
import matplotlib.pyplot as plt
def plot_func():
three = float(input("X ** 3:\n"))
two = float(input("X ** 2:\n"))
one = float(input("X:\n"))
b = float(input("B:\n"))
n = -10
x = []
y = []
while n <= 10:
x.append(n)
y.append( (three * n) ** 3 + (two * n) ** 2 + (one * n) + b)
n += 1
return x, y
x, y = plot_func()
plt.scatter(x, y)
plt.show()
I basically just broke up the different parts and it will work with any function up to x ** 3 and of course you could add more. Just make your x ** 3 equal 0 if you only have an x ** 2 function. Again, not ideal, but it will work until you can figure out something better.

Related

PyCharm Matplotlib plot parametric equation with range

I want to plot a graph in PyCharm, using Matplotlib and a parametric equation. In this equation x and y are defined by some constant variables (a, b & c) and another variable (θ) which is in a range between 0 - 10. Python doesn't seem to handle the range because it's not a string.
This is an example code with an example equation:
import math
import matplotlib.pyplot as plt
a = 2
b = 3
c = 4
θ = range(0, 10)
x = (a + b * c) / θ
y = ((a / b) + c) * θ
plt.plot(x, y)
plt.show()
I would like to have something like this (2d) and eventually 3d:
I have tried to use the for loop and updating the graph, but that resulted in a very slow PyCharm and the graph to shut down.
Does any of you know how to do this?
Found the answer:
import math
import matplotlib.pyplot as plt
a = 2
b = 3
c = 4
θ = np.linspace(0, 10) #this sets an array between 0 and 10
x = (a + b * c) / θ
y = ((a / b) + c) * θ
plt.plot(x, y)
plt.show()
For the rest as an advice: np.linspace doesn't work with math.fabs, math.cos, math.pow. You have to use np.abs, np.cos or np.power instead ;)

Find maxima for a negative parabolic equation

I have the following negative quadratic equation
-0.03402645959398278x^{2}+156.003469x-178794.025
I want to know if there is a straight way (using numpy/scipy libraries or any other) to get the value of x when the slope of the derivative is zero (the maxima). I'm aware I could:
change the sign of the equation and apply the scipy.optimize.minima method or
using the derivative of the equation so I can get the value when the slope is zero
For instance:
from scipy.optimize import minimize
quad_eq = np.poly1d([-0.03402645959398278, 156.003469, -178794.025])
############SCIPY####################
neg_quad_eq = np.poly1d(np.negative(quad_eq))
fit = minimize(neg_quad_eq, x0=15)
slope_zero_neg = fit.x[0]
maxima = np.polyval(quad_eq, slope_zero_neg)
print(maxima)
##################numpy######################
import numpy as np
first_dev = np.polyder(quad_eq)
slope_zero = first_dev.r
maxima = np.polyval(quad_eq, slope_zero)
print(maxima)
Is there any straight way to get the same result?
print(maxima)
You don't need all that code... The first derivative of a x^2 + b x + c is 2a x + b, so solving 2a x + b = 0 for x yields x = -b / (2a) that is actually the maximum you are searching for
import numpy as np
import matplotlib.pyplot as plt
def func(x, a=-0.03402645959398278, b=156.003469, c=-178794.025):
result = a * x**2 + b * x + c
return result
def func_max(a=-0.03402645959398278, b=156.003469, c=-178794.025):
maximum_x = -b / (2 * a)
maximum_y = a * maximum_x**2 + b * maximum_x + c
return maximum_x, maximum_y
x = np.linspace(-50000, 50000, 100)
y = func(x)
mx, my = func_max()
print('maximum:', mx, my)
maximum: 2292.384674478263 15.955750522436574
and verify
plt.plot(x, y)
plt.axvline(mx, color='r')
plt.axhline(my, color='r')

Python loglog graph

In the following code I have implemented composite Simpsons Rule in python. I have tested it by integrating f = sinx over [0,pi/2] and plotting the resulting absolute error as a function of n for a suitable range of integer values for n. As shown below. Now I am trying to verify that my method is of order 4. To do this instead of plotting the error vs n I need to plot n vs n^(-4) to show that both have the same slope.
from math import pi, cos, sin
from matplotlib import pyplot as plt
def simpson(f, a, b, n):
"""Approximates the definite integral of f from a to b by the composite Simpson's rule, using 2n subintervals """
h = (b - a) / (2*n)
s = f(a) + f(b)
for i in range(1, 2*n, 2):
s += 4 * f(a + i * h)
for i in range(2, 2*n-1, 2):
s += 2 * f(a + i * h)
return s * h / 3
diffs = {}
exact = 1 - cos(pi/2)
for n in range(1, 100):
result = simpson(lambda x: sin(x), 0.0, pi/2, n)
diffs[2*n] = abs(exact - result) # use 2*n or n here, your choice.
ordered = sorted(diffs.items())
x,y = zip(*ordered)
plt.autoscale()
plt.loglog(x,y)
plt.xlabel("Intervals")
plt.ylabel("Error")
plt.show()
this results in my error graph:
You can add another line to your plot by making another call to plt.loglog(). So just before your plt.show() you can add:
n = []
n_inv_4 = []
for i in range(1,100):
n.append(2*i)
n_inv_4.append(1.0 / ((2*i)**4))
plt.loglog(n, n_inv_4)
Note that the above calculations use (2*i) to match the (2*n) used in the simpson method.

Fastest way to add values to the surface of a sphere in Python?

I have an numpy array that represents my voxelgrid.. Now i want to add values to the surface of a sphere for a given radius. What is the fastest way?
My solution:
def spheric Surface (x, y, z, r, value):
while phi <= (2*math.pi):
eta = math.pi * 2 / 3
while eta <= math.pi:
xx = x + r * math.sin(eta) * math.cos(phi)
yy = y + r * math.sin(eta) * math.sin(phi)
zz = z + r * math.cos(eta)
xx = int(xx*resoultion+0.5)
yy = int(yy*resolution+0.5)
zz = int(zz*resolution+0.5)
voxelGrid[xx][yy][zz] += value
eta += 1/10 * math.pi
phi += 1/10 * math.pi
This is my first Idea: It ist not very fast and not very accurate because with bigger r, i need more angle to calculate.., not just adding 1/10pi for example but 1/5pi, but this makes the code even slower...
Resolution is the resolution of my voxelgrid.. so with Resolution 3, x=2mm would become xx= 6 in the array..
And yes i dont want the whole surface of the sphere, just from 2/3pi to pi...
Is there any better and faster way?
I tried the way with the mask like this, but it is even slower:
def sphericSurface(x, y, z, r, value):
tol = 0.6
grenz = math.pi * 2 / 3
mask = (np.logical_and(np.logical_and((sx[:, None, None] - x) ** 2 + (sy[None, :, None] - y) ** 2 + (sz[None, None, :] - z) ** 2 <= (r + tol)**2,
(sx[:, None, None] - x) ** 2 + (sy[None, :, None] - y) ** 2 + (sz[None, None, :] - z) ** 2 >= (r - tol)**2),
(sz[None, None, :] - z) <= (r*math.cos(grenz))))
x, y, z = np.where(mask==True)
z *= 2
voxelGrid[x,y,z] += value
You can select all of the elements that require modification by generating a mask. I'm not sure how compatible this is which what you already have, but this is the way. It'll basically blow the doors off of the while loop solution speed-wise.
import numpy as np
x = np.arange(0.0,5.0,0.1)
y = np.arange(0.0,5.0,0.1)
z = np.arange(0.0,5.0,0.1)
points = np.array(np.meshgrid(x,y,z)).T
def make_mask(points,a,b,c,r,tol=1e-2):
"""generates a boolean mask of positions within tol distance of the surface of the sphere
(x-a)**2 + (y-b)**2 + (z-c)**2 = r**2"""
mask = (points[...,0]-a)**2+(points[...,1]-b)**2+(points[...,2]-c)**2 < (r+tol)**2
return mask
mask = make_mask(points,2.5,2.5,2.5,1.0,tol=0.2)
# this will tell you all of the points in voxelgrid which need modification
voxelgrid[mask] #will return them all
If you want to add a value to every point near the surface of the sphere you can do
voxelgrid[mask]+=value
provided that the voxelgrid and points coordinates coincide in the sense that voxelgrid[i,j,k] is the container associated with the point points[i,j,k].. you will have to use your resolution parameter to make the x,y,z so that this is true.
Here's a lame plot showing that it works for me:
The code for this plot is
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(*points[mask].T)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
plt.savefig('works.png')
You can maybe calculate the mask more cleanly with something like:
x0 = np.array([a,b,c])
mask = np.sum((points-x0)**2,axis=-1)<(r+tol)**2
but it's a little harder to read. It may be faster ? I am not sure on this. (can anyone weigh in? )

Python double integral taking too long to compute

I am trying to compute the fresnel integral over a grid of coordinates using dblquad. But its taking very long and finally it's not giving any result.
Below is my code. In this code I integrated only over a 10 x 10 grid but I need to integrate at least over a 500 x 500 grid.
import time
st = time.time()
import pylab
import scipy.integrate as inte
import numpy as np
print 'imhere 0'
def sinIntegrand(y,x, X , Y):
a = 0.0001
R = 2e-3
z = 10e-3
Lambda = 0.5e-6
alpha = 0.01
k = np.pi * 2 / Lambda
return np.cos(k * (((x-R)**2)*a + (R-(x**2 + y**2)) * np.tan(np.radians(alpha)) + ((x - X)**2 + (y - Y)**2) / (2 * z)))
print 'im here 1'
def cosIntegrand(y,x,X,Y):
a = 0.0001
R = 2e-3
z = 10e-3
Lambda = 0.5e-6
alpha = 0.01
k = np.pi * 2 / Lambda
return np.sin(k * (((x-R)**2)*a + (R-(x**2 + y**2)) * np.tan(np.radians(alpha)) + ((x - X)**2 + (y - Y)**2) / (2 * z)))
def y1(x,R = 2e-3):
return (R**2 - x**2)**0.5
def y2(x, R = 2e-3):
return -1*(R**2 - x**2)**0.5
points = np.linspace(-1e-3,1e-3,10)
points2 = np.linspace(1e-3,-1e-3,10)
yv,xv = np.meshgrid(points , points2)
#def integrate_on_grid(func, lo, hi,y1,y2):
# """Returns a callable that can be evaluated on a grid."""
# return np.vectorize(lambda n,m: dblquad(func, lo, hi,y1,y2,(n,m))[0])
#
#intensity = abs(integrate_on_grid(sinIntegrand,-1e-3 ,1e-3,y1, y2)(yv,xv))**2 + abs(integrate_on_grid(cosIntegrand,-1e-3 ,1e-3,y1, y2)(yv,xv))**2
Intensity = []
print 'im here2'
for i in points:
row = []
for j in points2:
print 'im here'
intensity = abs(inte.dblquad(sinIntegrand,-1e-3 ,1e-3,y1, y2,(i,j))[0])**2 + abs(inte.dblquad(cosIntegrand,-1e-3 ,1e-3,y1, y2,(i,j))[0])**2
row.append(intensity)
Intensity.append(row)
Intensity = np.asarray(Intensity)
pylab.imshow(Intensity,cmap = 'gray')
pylab.show()
print str(time.time() - st)
I would really appreciate if you could tell any better way of doing this.
Using a scipy.integrate.dblquad to calculate every pixel of your image is going to be slow in any case.
You should try rewriting your mathematical problem so you can use some classical function in scipy.special instead. For instance, scipy.special.fresnel might work, although it is 1D and your problem seems to be in 2D. Otherwise, that there is a relationship between the Fresnel integral and the incomplete Gamma function (scipy.special.gammainc), if that helps.
If none of this work, as a last resort you can spend time optimizing your code and adapting it to Cython. This it will probably give a speed up of a factor of 10 to 100 (see this answer). Though this wouldn't be sufficient to go from a grid 10x10 to a grid 500x500.

Categories