I have to check whether a point is within an ellipse with semiaxes a and b. I generated a list of tuples (dots) and then generated another list of tuples (dotsin) where I only keep those points that are within an ellipse.
However, when generated, some points fall out of the ellipse. Is this error accumulation trough calculations, and if so how do I improve on this so the dots do not fall out of the curve?
Do note that I am a bit rusty in python and some things are not obvious for me.
Thanks in advance!
dots=[(random.uniform(-a,a),random.uniform(-b,b)) for i in range(1000)]#;dots
dotsin=[(x,y) for x,y in dots if (x**2 + y**2)<((a*cos(atan(y/x)))**2 + (b*sin(atan(y/x)))**2)]#;dotsin
plt.scatter([x[0] for x in dotsin],[y[1] for y in dotsin])
plt.grid()
Your condition (x**2 + y**2)<((a*cos(atan(y/x)))**2 + (b*sin(atan(y/x)))**2) is equivalent to (x**2 + y**2)**2 < (a*x) ** 2 + (b*y) ** 2. This is not an equation of ellipse. An equation of ellipse is (x/a)**2 + (y/b)**2 < 1.
from numpy import random
import numpy as np
from math import sin, cos, atan
import matplotlib.pyplot as plt
%matplotlib inline
a = 3
b = 2
n = 1000
dots=[(random.uniform(-a,a),random.uniform(-b,b)) for i in range(n)]#;dots
dotsin=[(x,y) for x,y in dots if (x/a)**2 + (y/b)**2 < 1]#;dotsin
plt.scatter([x[0] for x in dotsin],[y[1] for y in dotsin])
phi = np.linspace(0, 2*np.pi, 200)
plt.plot(a*np.cos(phi), b*np.sin(phi))
plt.grid()
Related
I have a plot like this, plotting a semicircle with x and y
I want to add arrows at each point like so (ignore the horrible paint job):
Is there an easy way to add arrows perpendicular to the plot?
Current code:
import numpy as np
import matplotlib.pyplot as plt
r = 2
h = 0
k = 0
x0 = h-r
x1 = h+r
x = np.linspace(x0,x1,9)
y = k + np.sqrt(r**2 - (x-h)**2)
plt.scatter(x,y)
plt.xlim(-4,4)
plt.ylim(-4,4)
PERPENDICULAR TO THE TANGENT OF THE CURVE I'M SORRY I FORGOT TO ADD THIS
A point in space has no idea what "perpendicular" means, but assuming your y is some function of x that has a derivate, you can think of the derivate of the function at some point to be the tangent of the curve at that point, and to get a perpendicular vector you just need to rotate the vector counter-clockwise 90 degrees:
x1, y1 = -y0, x0
We know that these points come from a circle. So given three points we can easily find the center using basic geometry notions. If you need a refresher, take a look here.
For this particular case, the center is at the origin. Knowing the center coordinates, the normal at each point is just the vector from the center to the point itself. Since the center is the origin, the normals' components are just given by the coordinates of the points themselves.
import numpy as np
import matplotlib.pyplot as plt
r = 2
h = 0
k = 0
x0 = h-r
x1 = h+r
x = np.linspace(x0, x1, 9)
y = k + np.sqrt(r**2 - (x-h)**2)
center = np.array([0.0, 0.0])
plt.scatter(x, y)
plt.quiver(x, y, x, y, width=0.005)
plt.xlim(-4, 4)
plt.ylim(-4, 4)
plt.show()
If you are in a hurry and you do not have time to implement equations, you could use the scikit-spatial library in the following way:
from skspatial.objects import Circle, Vector, Points
import numpy as np
import matplotlib.pyplot as plt
r = 2
h = 0
k = 0
x0 = h-r
x1 = h+r
x = np.linspace(x0, x1, 9)
y = k + np.sqrt(r**2 - (x-h)**2)
points = Points(np.vstack((x, y)).T)
circle = Circle.best_fit(np.vstack((x, y)).T)
center = circle.point
normals = np.array([Vector.from_points(center, point) for point in points])
plt.scatter(x, y)
plt.quiver(x, y, normals[:, 0], normals[:, 1], width=0.005)
plt.xlim(-4, 4)
plt.ylim(-4, 4)
plt.show()
Postulate of blunova's and simon's answers is correct, generally speaking: points have no normal, but curve have; so you need to rely on what you know your curve is. Either, as blunova described it, by the knowledge that it is a circle, and computing those normal with ad-hoc computation from that knowledge.
Or, as I am about to describe, using the function f such as y=f(x). and using knowledge on what is the normal to such a (x,f(x)) chart.
Here is your code, written with such a function f
import numpy as np
import matplotlib.pyplot as plt
r = 2
h = 0
k = 0
x0 = h-r
x1 = h+r
x = np.linspace(x0,x1,9)
def f(x):
return k + np.sqrt(r**2 - (x-h)**2)
y=f(x)
plt.scatter(x,y)
plt.xlim(-4,4)
plt.ylim(-4,4)
So, all I did here is rewriting your line y=... in the form of a function.
From there, it is possible to compute the normal to each point of the chart (x,f(x)).
The tangent to a point (x,f(x)) is well known: it is vector (1,f'(x)), where f'(x) is the derivative of f. So, normal to that is (-f'(x), 1).
Divided by √(f'(x)²+1) to normalize this vector.
So, just use that as entry to quiver.
First compute a derivative of your function
dx=0.001
def fprime(x):
return (f(x+dx)-f(x-dx))/(2*dx)
Then, just
plt.quiver(x, f(x), -fprime(x), 1)
Or, to have all vector normalized
plt.quiver(x, f(x), -fprime(x)/np.sqrt(fprime(x)**2+1), 1/np.sqrt(fprime(x)**2+1))
(note that fprime and the normalization part are all vectorizable operation, so it works with x being a arange)
All together
import numpy as np
import matplotlib.pyplot as plt
r = 2
h = 0
k = 0
x0 = h-r
x1 = h+r
def f(x):
return k+ np.sqrt(r**2 - (x-h)**2)
dx=0.001
x = np.linspace(x0+dx,x1-dx,9)
y = f(x)
def fprime(x):
return (f(x+dx)-f(x-dx))/(2*dx)
plt.scatter(x,y)
plt.quiver(x,f(x), -fprime(x)/np.sqrt(fprime(x)**2+1), 1/np.sqrt(fprime(x)**2+1))
plt.xlim(-4,4)
plt.ylim(-4,4)
plt.show()
That is almost an exact copy of your code, but for the quiver line, and with the addition of fprime.
One other slight change, specific to your curve, is that I changed x range to ensure the computability of fprime (if first x is x0, then fprime need f(x0-dx) which does not exist because of sqrt. Likewise for x1. So, first x is x0+dx, and last is x1-dx, which is visually the same)
That is the main advantage of this solution over blunova's: it is your code, essentially. And would work if you change f, without assuming that f is a circle. All that is assume is that f is derivable (and if it were not, you could not define what those normal are anyway).
For example, if you want to do the same with a parabola instead, just change f
import numpy as np
import matplotlib.pyplot as plt
r = 2
h = 0
k = 0
x0 = h-r
x1 = h+r
def f(x):
return x**2
dx=0.001
x = np.linspace(x0+dx,x1-dx,9)
y = f(x)
def fprime(x):
return (f(x+dx)-f(x-dx))/(2*dx)
plt.scatter(x,y)
plt.quiver(x,f(x), -fprime(x)/np.sqrt(fprime(x)**2+1), 1/np.sqrt(fprime(x)**2+1))
plt.xlim(-4,4)
plt.ylim(-2,5)
plt.show()
All I changed here is the f formula. Not need for a new reasoning to compute the normal.
Last remark: an even more accurate version (not forcing the approximate computation of fprime with a dx) would be to use sympy to define f, and then compute the real, symbolic, derivative of f. But that doesn't seem necessary for your case.
I've been trying to plot a graph of the 1d, horizontal diffraction pattern and have written the following code:
import math
import cmath
import numpy as np
import matplotlib.pyplot as plt
lamda=0.2
k=(2*math.pi)/lamda
z=0.005
def expfunc(x,xp):
return cmath.exp(1j*k*((x-xp)**2)/(2*z))
def X(xp1,xp2,x,xp,expfunc,N):
h=(xp2-xp1)/N
y=0.0
for i in np.arange(1, N/2 +1): #summing odd order y terms
y+=4*expfunc(x,xp)
xp+=2*h
xp=xp1+2*h
for i in np.arange(0, N/2): #summing even order y terms
y+=2*expfunc(x,xp)
xp+=2*h
integral= (h/3)*(y+expfunc(x, xp1)+expfunc(x, xp2))
integral= (integral.real)**2
return integral
NumPoints = 90000
xmin = 0
xmax =20
dx = (xmax - xmin) / (NumPoints - 1)
xvals = [0.0] * NumPoints
yvals = np.zeros(NumPoints)
for i in range(NumPoints):
xvals[i] = xmin + i * dx
yvals[i] = X(xmin,xmax,xvals[i],0.1,expfunc,200)
plt.plot(xvals,yvals)
plt.show()
The graph is meant to be a sinc function, however the graph I get is all over the place when I vary the parameters N, the number of intervals, and z, the distance from the screen. I fail to see what is wrong with my code
Thanks
There seems to be a problem with the frequency. Are you maybe missing a minus sign in expfunc() at return cmath.exp(1j*k*... ?
I am trying to draw the phase space plot for a certain dynamical system. In effect, I have a 2d plane in which there is a starting point followed by next point and so on. I want to connect these points with lines and on top of that I want to draw some arrows so that I would be able to see the direction (starting point to the next point etc). I decided to use linetype '->' to achieve this but it doesn't give any good result and arrows actually seem to point in wrong direction many times. Also they are quite closely spaced and hence I can't see the individual lines.
My code is given below:
import numpy as np
import matplotlib.pylab as plt
from scipy.integrate import odeint
def system(vect, t):
x, y = vect
return [x - y - x * (x**2 + 5 * y**2), x + y - y * (x**2 + y**2)]
vect0 = [(-2 + 4*np.random.random(), -2 + 4*np.random.random()) for i in range(5)]
t = np.linspace(0, 100, 1000)
for v in vect0:
sol = odeint(system, v, t)
plt.plot(sol[:, 0], sol[:, 1], '->')
plt.show()
The resulting plot is shown below:
As can be seen, the arrows are not properly aligned to the lines that connect the points. Also, many arrows are "going out" and I want them to "come in" because the next point always lies towards the close loop at the middle. Moreover, plot looks too messy and I would like to plot fewer arrows so that plot would look better. Does anybody have any idea as how to do it? Thanks in advance.
I think a solution would then look like this:
Using that code:
import numpy as np
import matplotlib.pylab as plt
from scipy.integrate import odeint
from scipy.misc import derivative
def system(vect, t):
x, y = vect
return [x - y - x * (x**2 + 5 * y**2), x + y - y * (x**2 + y**2)]
vect0 = [(-2 + 4*np.random.random(), -2 + 4*np.random.random()) for i in range(5)]
t = np.linspace(0, 100, 1000)
color=['red','green','blue','yellow', 'magenta']
plot = plt.figure()
for i, v in enumerate(vect0):
sol = odeint(system, v, t)
plt.quiver(sol[:-1, 0], sol[:-1, 1], sol[1:, 0]-sol[:-1, 0], sol[1:, 1]-sol[:-1, 1], scale_units='xy', angles='xy', scale=1, color=color[i])
plt.show(plot)
[EDIT: Some explanation on indices:
A definition of quiver and its arguments can be found here: https://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.quiver
Good examples for quiver can be found here: https://www.getdatajoy.com/examples/python-plots/vector-fields
quiver requires vectors as inputs, which are defined by a start and end points (start and end points are basically points i and i+1 from the line coordinates stored in sol)
As a consequence, the length of the vector array will be one shorter than the length of the coordinate array
In order to compensate for that and to provide arrays with the same length for coordinates and vectors to quiver, we have to play with indices as follows:
sol[:-1, 0] (:-1 in first index drops the last coordinate)
sol[1:, 0] (1: in first index starts drops first coordinate)
sol[1:, 0] - sol[:-1, 0] is therefore a convenient way to create two vectors of length n-1 and subtract them in a way that the result is sol[i+1] - sol[i]
I have a trajectory formed by a sequence of (x,y) pairs. I would like to interpolate points on this trajectory using splines.
How do I do this? Using scipy.interpolate.UnivariateSpline doesn't work because neither x nor y are monotonic. I could introduce a parametrization (e.g. length d along the trajectory), but then I have two dependent variables x(d) and y(d).
Example:
import numpy as np
import matplotlib.pyplot as plt
import math
error = 0.1
x0 = 1
y0 = 1
r0 = 0.5
alpha = np.linspace(0, 2*math.pi, 40, endpoint=False)
r = r0 + error * np.random.random(len(alpha))
x = x0 + r * np.cos(alpha)
y = x0 + r * np.sin(alpha)
plt.scatter(x, y, color='blue', label='given')
# For this special case, the following code produces the
# desired results. However, I need something that depends
# only on x and y:
from scipy.interpolate import interp1d
alpha_i = np.linspace(alpha[0], alpha[-1], 100)
r_i = interp1d(alpha, r, kind=3)(alpha_i)
x_i = x0 + r_i * np.cos(alpha_i)
y_i = x0 + r_i * np.sin(alpha_i)
plt.plot(x_i, y_i, color='green', label='desired')
plt.legend()
plt.show()
Using splprep you can interpolate over curves of any geometry.
from scipy import interpolate
tck,u=interpolate.splprep([x,y],s=0.0)
x_i,y_i= interpolate.splev(np.linspace(0,1,100),tck)
Which produces a plot like the one given, but only using the x and y points and not the alpha and r paramters.
Sorry about my original answer, I misread the question.
I'm trying to write a code that plots the elliptical paths of an object using the equation for the ellipse r=a(1-e^2)/(1+e*cos(theta)). I'd also like this data to be put into an array for other use.
from numpy import *#Imports Python mathematical functions library
import matplotlib.pyplot as plt #Imports plot library
from pylab import *
a = 5
e = 0.3
theta = 0
while theta <= 2*pi:
r = (a*(1-e**2))/(1+e*cos(theta))
print("r = ",r,"theta = ",theta)
plt.polar(theta, r)
theta += pi/180
plt.show()
The code spits out correct values for r and theta, but the plot is blank. The polar plot window appears, but there is nothing plotted.
Please help. Thanks in advance.
Do not call plt.polar once for every point. Instead, call it once, with all the data as input:
import numpy as np #Imports Python mathematical functions library
import matplotlib.pyplot as plt #Imports plot library
cos = np.cos
pi = np.pi
a = 5
e = 0.3
theta = np.linspace(0,2*pi, 360)
r = (a*(1-e**2))/(1+e*cos(theta))
plt.polar(theta, r)
print(np.c_[r,theta])
plt.show()
By the way, numpy can do the calculation as a two-liner, instead of using a while-loop:
theta = np.linspace(0,2*pi, 360) # 360 equally spaced values between 0 and 2*pi
r = (a*(1-e**2))/(1+e*cos(theta))
This defines theta and r as numpy arrays (rather than single values).
I think you need to do points.append([theta,r]) then at the end plt.polar(points) ... that makes a kinda neat design too
from numpy import *#Imports Python mathematical functions library
import matplotlib.pyplot as plt #Imports plot library
from pylab import *
a = 5
e = 0.3
theta = 0
points = []
while theta <= 2*pi:
r = (a*(1-e**2))/(1+e*cos(theta))
print("r = ",r,"theta = ",theta)
points.append((theta, r))
theta += pi/180
#plt.polar(points) #this is cool but probably not what you want
plt.polar(*zip(*points))
plt.show()