Plot points overlapping when rotating points on xy-plane - python

The point of the spot marked (comment says "marked spot") is to rotate the existing points in a 3D plot by one spot. Basically I'm moving all points 60 degrees. For some reason when moving the points I am having two points overlap. I have tried changing my if statement, and have messed around with he indices, but have not been successful. The only reason I included the entire function is so you can plot and see the problem I am having. Here it is; let me know if you have any questions:
def transform_3d_trig(a,b,c):
q=2*math.pi/(360)
d=c*q
a2 = a+b
n = 12
sin=math.sin
cos=math.cos
sqrt=math.sqrt
x = []
y = []
z = []
for i in range(n):
if i <= 5:
x.append(a*np.cos(q*(60*(i-1))))
y.append(a*np.sin(q*(60*(i-1))))
z.append(0)
else:
x.append(a2*np.cos(q*(60*(i-1))))
y.append(a2*np.sin(q*(60*(i-1))))
z.append(0)
x_new = x #new lists
y_new = y
z_new = z
for i in range(n):
y_new[i] = y[i]*cos(d)
z_new[i] = y_new[i]*np.tan(d)
# plot points at this stage (before rotation); no overlapping points
fig = plt.figure(figsize=(12,12))
ax3 = fig.add_subplot(211, projection='3d')
bond2 = [x_new[4],x_new[10],y_new[4],y_new[10],z_new[4],z_new[10]]
ax3.plot(bond2[:2],bond2[2:4],bond2[4:6], color='r')
ax3.scatter(x_new, y_new, z_new)
ax3.set_xlabel('\nX')
ax3.set_ylabel('\nY')
ax3.set_zlabel('\nZ')
x_dummy = x_new #dummy variables to not screw up list
y_dummy = y_new
for i in range(n): #marked spot
if (i == 5 or i == 11):
x_new[i] = x_dummy[i-5]
y_new[i] = y_dummy[i-5]
else:
x_new[i] = x_dummy[i+1]
y_new[i] = y_dummy[i+1]
print(x_new[i], y_new[i]) #to track what point are overlapping
# plot points at this stage (after rotation); overlapping points
ax3 = fig.add_subplot(212, projection='3d')
bond2 = [x_new[4],x_new[10],y_new[4],y_new[10],z_new[4],z_new[10]]
ax3.plot(bond2[:2],bond2[2:4],bond2[4:6], color='r')
ax3.scatter(x_new, y_new, z_new)
ax3.set_xlabel('\nX')
ax3.set_ylabel('\nY')
ax3.set_zlabel('\nZ')
plt.show()
return x, y, z, x_new, y_new, z_new

The problem is probably that these lines don't do what you expect:
x_dummy = x_new
y_dummy = y_new
They don't create copies of your arrays but just duplicate the references. As a result you are actually 'screwing up' your original _new arrays when modifying the _dummy 'versions' because they are (references to) the same object.
To achieve the behaviour you are looking for (I assume), you would need to force the actual copy of the data:
x_dummy = x_new.copy()
y_dummy = y_new.copy()

Related

How to plot multiple lines from a loop on one 3d plot in Python?

Basically, I am looping generation of rays in Python and I'm trying to plot them all on the same graph. They should all be on a circle of radius 0.1. Each ray should be at a position on the circle that is varied by the arg which is in this case the theta. Also, just to mention (although I don't think it's that relevant) I am doing OOP here.
I get correct rays but I can't get them on the same 3d graph and I'm not sure how I'm supposed to do it. I thought using plt.show() would give me a graph with all 24 rays but it just plots 24 graphs.
Here is the relevant bit of code for reference:
r = 0.1
arg = 0
for i in range (0,24):
arg += np.pi/12
x = r*np.sin(arg)
y = r*np.cos(arg)
l = ray.Ray(r=np.array([x,y,0]),v=np.array([0.5,0,5]))
c = ray.SphericalRefraction(z0 = 100, curv = 0.0009, n1 = 1.0, n2 = 1.5, ar = 5)
c.propagate_ray(l)
o = ray.OutputPlane(250)
o.outputintercept(l)
points = np.array(l.vertices())
fig = plt.figure()
ax = plt.axes(projection='3d')
#ax = fig.add_subplot(1,2,1,projection='3d')
#plt.plot(points[:,2],points[:,0])
ax.plot3D(points[:,0],points[:,1],points[:,2])
plt.show()
Expanding on the comment by Mercury, the figure and also axes object must be created outside the loop.
import matplotlib.pyplot as plt
import numpy as np
r = 0.1
arg = 0
fig = plt.figure()
ax = plt.axes(projection='3d')
for i in range(0,24):
arg += np.pi/12 * i
v1 = r*np.sin(arg)
v2 = r*np.cos(arg)
# ...
# using sample data
x = []
y = []
z = []
for j in range(2):
x.append(j*v1)
y.append(j*v2)
z.append(j)
# add vertex to the axes object
ax.plot3D(x, y, z)
plt.show()

Rotate a curve to a point in python

I would like to rotate a curve to a pass through a point on a 2D plot but l cant find a way of doing it .
Say l want to rotate
y=np.exp(x)+2 #pass through (x=5,y=6) point
You can subtract value of y(x_0) from y and add desired y.
For example
x = np.arange(10)
y = np.exp(x) + 2
x_ind = 5 #This is not value of x, this is index of desired x
y_desired = 6
y -= y[x[x_ind]] + y_desired
plt.plot(x, y, color='b')
plt.scatter([x[x_ind]], [y_desired], color='r')
plt.show()

Extend a 2D plot to 3D

I'm trying to show my 2D data on a 3D space.
Here is my code below:
import numpy as np
import matplotlib.pyplot as plt
i = 60
n = 1000
r = 3.8
eps = 0.7
y = np.ones((n, i))
# random numbers on the first row of array x
np.random.seed(1)
x = np.ones((n+1, i))
x[0, :] = np.random.random(i)
def logistic(r, x):
return r * x * (1 - x)
present_indi = np.arange(i)
next_indi = (present_indi + 1) % i
prev_indi = (present_indi - 1) % i
for n in range(1000):
y[n, :] = logistic(r, x[n, :])
x[n+1, :] = (1-eps)*y[n, present_indi] + 0.5*eps*(y[n, prev_indi] + y[n, next_indi])
#print(x)
# the above logic generates a 2D array 'x'. with i columns and n rows.
fig, ax = plt.subplots()
for i in range(60):
for n in range(1000):
if n>=900:
ax.plot(i,x[n,i],'*k',ms=0.9)
plt.xlabel('i')
plt.ylabel('x')
plt.title('test')
plt.show()
The above code perfectly displays i and x graph. I have plotted all the elements of 1st column of X, then all elements of second column, then the third and so on....., using the nested for loop logic (refer to the code)
Now what I need to do is, extend the plotting to 3D, i.e use Xaxis = i, Yaxis= n, Zaxis= array 'x'
I want to plot something like this:
I know I have to use mplot3D
But doing the following won't give me any result:
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for i in range(60):
for n in range(1000):
if n>=900:
ax.plot_wireframe(i,n,x[n,i],rstride=1,cstride=1)
Plotting 3d images in matplotlib is a little tricky. Generally you plot whole surfaces at once instead of plotting one line at a time. You do so by passing three 2d arrays, one for each position dimension (x, y, z). But you can't just pass any old 2d arrays either; the points themselves have to be in a precise order!
Sometimes you can do something that just works, but I find it easier to explicitly parameterize plots using u and v dimensions. Here's what I was able to get working here:
# Abstract u and v parameters describing surface coordinates
u_plt = np.arange(x.shape[1])
v_plt = np.arange(x.shape[0])
# The outer products here produce 2d arrays. We multiply by
# ones in this case for an identity transformation, but in
# general, you could use any broadcasted operation on `u`
# and `v`.
x_plt = np.outer(np.ones(np.size(v_plt)), u_plt)
y_plt = np.outer(v_plt, np.ones(np.size(u_plt)))
# In this case, our `x` array gives the `z` values directly.
z_plt = x
fig = plt.figure(figsize=(16, 10))
ax = fig.add_subplot(111, projection='3d')
ax.set_zmargin(1) # Add a bit more space around the plot.
ax.plot_wireframe(x_plt, y_plt, z_plt,
rstride=1, cstride=1, # "Resolution" of the plot
color='blue', linewidth=1.0,
alpha=0.7, antialiased=True)
# Tilt the view to match the example.
ax.view_init(elev = 45, azim = -45)
plt.xlabel('i')
plt.ylabel('x')
plt.title('test')
plt.show()
And here's the resulting image. I had to reduce n to 80 to make this comprehensible at all, and I have no idea what I am looking at, so I am not sure it's correct. But I think it looks broadly similar to the example you gave.
Just to illustrate the power of this approach, here's a nautilus shell. It uses a two-stage parameterization, which could be compressed, but which I find conceptually clearer:
n_ticks = 100
# Abstract u and v parameters describing surface coordinates
u_plt = np.arange(n_ticks // 2) * 2
v_plt = np.arange(n_ticks)
# theta is the angle along the leading edge of the shell
# phi is the angle along the spiral of the shell
# r is the distance of the edge from the origin
theta_plt = np.pi * ((u_plt / n_ticks) * 0.99 + 0.005)
phi_plt = np.pi * v_plt / (n_ticks / 5)
r_plt = v_plt / (n_ticks / 5)
# These formulas are based on the formulas for rendering
# a sphere parameterized by theta and phi. The only difference
# is that r is variable here too.
x_plt = r_plt[:, None] * np.cos(phi_plt[:, None]) * np.sin(theta_plt[None, :])
y_plt = r_plt[:, None] * np.sin(phi_plt[:, None]) * np.sin(theta_plt[None, :])
z_plt = r_plt[:, None] * \
(np.ones(np.shape(phi_plt[:, None])) * np.cos(theta_plt[None, :]))
# This varies the color along phi
colors = cm.inferno(1 - (v_plt[:, None] / max(v_plt))) * \
np.ones(np.shape(u_plt[None, :, None]))
fig = plt.figure(figsize=(8, 8))
ax = fig.add_subplot(111, projection='3d')
ax.set_zmargin(1)
ax.plot_surface(x_plt, y_plt, z_plt,
rstride=1, cstride=1,
facecolors=colors, linewidth=1.0,
alpha=0.3, antialiased=True)
ax.view_init(elev = 45, azim = -45)
plt.show()

Can't see the result of a matrix figure using python

Would you help me fix this code?
I am using Jupiter and can't see the result. Maybe there are issues with xLim or yLim.
The entire code is below, and I want to see the figure.
points = [[0.3036, 0.1960], [0.6168, 0.2977], [0.7128, 0.4169], [0.7120, 0.1960],[0.9377,0.2620],\
[0.7120,0.5680],[0.3989,0.6697],[0.3028,0.7889],[0.3036,0.5680],[0.5293,0.5020]]
theta = np.pi
a = 0.7120
b = 0.4320
shiftrotateMatrix = np.array([[np.cos(theta),-np.sin(theta),-a*np.cos(theta)+b*np.sin(theta)+a],\
[np.cos(theta),-np.sin(theta),-a*np.sin(theta)-b*np.cos(theta)+b],\
[0,0,1]])
points3d = points
for x in points3d:
x.append(1)
pointsab = []
for x in points3d:
pointsab.append(np.dot(shiftrotateMatrix,x))
finalpoints = [np.array([x[0],x[1]]) for x in pointsab]
fig = plt.figure()
finalbird = matplotlib.patches.Polygon(finalpoints, facecolor='yellow')
fig, ax = plt.subplots()
ax.set_aspect("auto")
ax.add_patch(finalbird)
ax.set_xlim(-2,2)
ax.set_ylim(-2,2)
plt.show()
Do this at the end of your code.
for point in finalpoints:
print(point[0] - point[1])
Every single (x, y) point has the property x - y = 0.56. Your Polygon is being "shown", but it's a straight line.

Numpy way to sort out a messy array for plotting

I have data of a plot on two arrays that are stored in unsorted way, so the plot jumps from one place to another discontinuously:
I have tried one example of finding the closest point in a 2D array:
import numpy as np
def distance(pt_1, pt_2):
pt_1 = np.array((pt_1[0], pt_1[1]))
pt_2 = np.array((pt_2[0], pt_2[1]))
return np.linalg.norm(pt_1-pt_2)
def closest_node(node, nodes):
nodes = np.asarray(nodes)
dist_2 = np.sum((nodes - node)**2, axis=1)
return np.argmin(dist_2)
a = []
for x in range(50000):
a.append((np.random.randint(0,1000),np.random.randint(0,1000)))
some_pt = (1, 2)
closest_node(some_pt, a)
Can I use it somehow to "clean" my data? (in the above code, a can be my data)
Exemplary data from my calculations is:
array([[ 2.08937872e+001, 1.99020033e+001, 2.28260611e+001,
6.27711094e+000, 3.30392288e+000, 1.30312878e+001,
8.80768833e+000, 1.31238275e+001, 1.57400130e+001,
5.00278061e+000, 1.70752624e+001, 1.79131456e+001,
1.50746185e+001, 2.50095731e+001, 2.15895974e+001,
1.23237801e+001, 1.14860312e+001, 1.44268222e+001,
6.37680265e+000, 7.81485403e+000],
[ -1.19702178e-001, -1.14050879e-001, -1.29711421e-001,
8.32977493e-001, 7.27437322e-001, 8.94389885e-001,
8.65931116e-001, -6.08199292e-002, -8.51922900e-002,
1.12333841e-001, -9.88131292e-324, 4.94065646e-324,
-9.88131292e-324, 4.94065646e-324, 4.94065646e-324,
0.00000000e+000, 0.00000000e+000, 0.00000000e+000,
-4.94065646e-324, 0.00000000e+000]])
After using radial_sort_line (of Joe Kington) I have received the following plot:
This is actually a problem that's tougher than you might think in general.
In your exact case, you might be able to get away with sorting by the y-values. It's hard to tell for sure from the plot.
Therefore, a better approach for somewhat circular shapes like this is to do a radial sort.
For example, let's generate some data somewhat similar to yours:
import numpy as np
import matplotlib.pyplot as plt
t = np.linspace(.2, 1.6 * np.pi)
x, y = np.cos(t), np.sin(t)
# Shuffle the points...
i = np.arange(t.size)
np.random.shuffle(i)
x, y = x[i], y[i]
fig, ax = plt.subplots()
ax.plot(x, y, color='lightblue')
ax.margins(0.05)
plt.show()
Okay, now let's try to undo that shuffle by using a radial sort. We'll use the centroid of the points as the center and calculate the angle to each point, then sort by that angle:
x0, y0 = x.mean(), y.mean()
angle = np.arctan2(y - y0, x - x0)
idx = angle.argsort()
x, y = x[idx], y[idx]
fig, ax = plt.subplots()
ax.plot(x, y, color='lightblue')
ax.margins(0.05)
plt.show()
Okay, pretty close! If we were working with a closed polygon, we'd be done.
However, we have one problem -- This closes the wrong gap. We'd rather have the angle start at the position of the largest gap in the line.
Therefore, we'll need to calculate the gap to each adjacent point on our new line and re-do the sort based on a new starting angle:
dx = np.diff(np.append(x, x[-1]))
dy = np.diff(np.append(y, y[-1]))
max_gap = np.abs(np.hypot(dx, dy)).argmax() + 1
x = np.append(x[max_gap:], x[:max_gap])
y = np.append(y[max_gap:], y[:max_gap])
Which results in:
As a complete, stand-alone example:
import numpy as np
import matplotlib.pyplot as plt
def main():
x, y = generate_data()
plot(x, y).set(title='Original data')
x, y = radial_sort_line(x, y)
plot(x, y).set(title='Sorted data')
plt.show()
def generate_data(num=50):
t = np.linspace(.2, 1.6 * np.pi, num)
x, y = np.cos(t), np.sin(t)
# Shuffle the points...
i = np.arange(t.size)
np.random.shuffle(i)
x, y = x[i], y[i]
return x, y
def radial_sort_line(x, y):
"""Sort unordered verts of an unclosed line by angle from their center."""
# Radial sort
x0, y0 = x.mean(), y.mean()
angle = np.arctan2(y - y0, x - x0)
idx = angle.argsort()
x, y = x[idx], y[idx]
# Split at opening in line
dx = np.diff(np.append(x, x[-1]))
dy = np.diff(np.append(y, y[-1]))
max_gap = np.abs(np.hypot(dx, dy)).argmax() + 1
x = np.append(x[max_gap:], x[:max_gap])
y = np.append(y[max_gap:], y[:max_gap])
return x, y
def plot(x, y):
fig, ax = plt.subplots()
ax.plot(x, y, color='lightblue')
ax.margins(0.05)
return ax
main()
Sorting the data base on their angle relative to the center as in #JoeKington 's solution might have problems with some parts of the data:
In [1]:
import scipy.spatial as ss
import matplotlib.pyplot as plt
import numpy as np
import re
%matplotlib inline
In [2]:
data=np.array([[ 2.08937872e+001, 1.99020033e+001, 2.28260611e+001,
6.27711094e+000, 3.30392288e+000, 1.30312878e+001,
8.80768833e+000, 1.31238275e+001, 1.57400130e+001,
5.00278061e+000, 1.70752624e+001, 1.79131456e+001,
1.50746185e+001, 2.50095731e+001, 2.15895974e+001,
1.23237801e+001, 1.14860312e+001, 1.44268222e+001,
6.37680265e+000, 7.81485403e+000],
[ -1.19702178e-001, -1.14050879e-001, -1.29711421e-001,
8.32977493e-001, 7.27437322e-001, 8.94389885e-001,
8.65931116e-001, -6.08199292e-002, -8.51922900e-002,
1.12333841e-001, -9.88131292e-324, 4.94065646e-324,
-9.88131292e-324, 4.94065646e-324, 4.94065646e-324,
0.00000000e+000, 0.00000000e+000, 0.00000000e+000,
-4.94065646e-324, 0.00000000e+000]])
In [3]:
plt.plot(data[0], data[1])
plt.title('Unsorted Data')
Out[3]:
<matplotlib.text.Text at 0x10a5c0550>
See x values between 15 and 20 are not sorted correctly.
In [10]:
#Calculate the angle in degrees of [0, 360]
sort_index = np.angle(np.dot((data.T-data.mean(1)), np.array([1.0, 1.0j])))
sort_index = np.where(sort_index>0, sort_index, sort_index+360)
#sorted the data by angle and plot them
sort_index = sort_index.argsort()
plt.plot(data[0][sort_index], data[1][sort_index])
plt.title('Data Sorted by angle relatively to the centroid')
plt.plot(data[0], data[1], 'r+')
Out[10]:
[<matplotlib.lines.Line2D at 0x10b009e10>]
We can sort the data based on a nearest neighbor approach, but since the x and y are of very different scale, the choice of distance metrics becomes an important issue. We will just try all the distance metrics available in scipy to get an idea:
In [7]:
def sort_dots(metrics, ax, start):
dist_m = ss.distance.squareform(ss.distance.pdist(data.T, metrics))
total_points = data.shape[1]
points_index = set(range(total_points))
sorted_index = []
target = start
ax.plot(data[0, target], data[1, target], 'o', markersize=16)
points_index.discard(target)
while len(points_index)>0:
candidate = list(points_index)
nneigbour = candidate[dist_m[target, candidate].argmin()]
points_index.discard(nneigbour)
points_index.discard(target)
#print points_index, target, nneigbour
sorted_index.append(target)
target = nneigbour
sorted_index.append(target)
ax.plot(data[0][sorted_index], data[1][sorted_index])
ax.set_title(metrics)
In [6]:
dmetrics = re.findall('pdist\(X\,\s+\'(.*)\'', ss.distance.pdist.__doc__)
In [8]:
f, axes = plt.subplots(4, 6, figsize=(16,10), sharex=True, sharey=True)
axes = axes.ravel()
for metrics, ax in zip(dmetrics, axes):
try:
sort_dots(metrics, ax, 5)
except:
ax.set_title(metrics + '(unsuitable)')
It looks like standardized euclidean and mahanalobis metrics give the best result. Note that we choose a starting point of the 6th data (index 5), it is the data point this the largest y value (use argmax to get the index, of course).
In [9]:
f, axes = plt.subplots(4, 6, figsize=(16,10), sharex=True, sharey=True)
axes = axes.ravel()
for metrics, ax in zip(dmetrics, axes):
try:
sort_dots(metrics, ax, 13)
except:
ax.set_title(metrics + '(unsuitable)')
This is what happens if you choose the starting point of max. x value (index 13). It appears that mahanalobis metrics is better than standardized euclidean as it is not affected by the starting point we choose.
If we do the assumption that the data are 2D and the x axis should be in an increasing fashion, then you could:
sort the x axis data, e.g. x_old and store the result in a different variable, e.g. x_new
for each element in the x_new find its index in the x_old array
re-order the elements in the y_axis array according to the indices that you got from previous step
I would do it with python list instead of numpy array due to list.index method been more easily manipulated than the numpy.where method.
E.g. (and assume that x_old and y_old are your previous numpy variables for x and y axis respectively)
import numpy as np
x_new_tmp = x_old.tolist()
y_new_tmp = y_old.tolist()
x_new = sorted(x_new_tmp)
y_new = [y_new_tmp[x_new_tmp.index(i)] for i in x_new]
Then you can plot x_new and y_new

Categories