I want to plot multiple lines in a 3d axis plot.
I'm plotting the trajectory of a light ray, each line should be colored depending on the time of propagation for each path.
I want to plot them independently, since I saved them in a binary three structure in which any ray may follow two different trajectories.
I used Line3DCollection, but it doesn't seem to be the right way.
In few words,
import numpy as np
x = np.linspace(0,1,100)
y = x
z = x
t = np.linspace(0,1,100)
#here I need to plot these points coloring them in function of t
x1 = np.linspace(1,2,100)
y1 = x
z1 = x
t1 = np.linspace(1,2,100)
#I need to plot also this used the same colorbar used for former values
x2 = -np.linspace(1,2,100)
y2 = x
z2 = x
t2 = np.linspace(1,2,100)
#idem
having all the lines colored with the same colorbar scale, like this
plot.
I tried with the following but the output is not like I expected
import numpy as np
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d.art3d import Line3DCollection
x = np.linspace(0,1,100)
y = x
z = x
t = np.linspace(0,1,100)
points = np.array([x,y,z]).transpose().reshape(-1,1,3)
segs = np.concatenate([points[:-1],points[1:]],axis=1)
lc = Line3DCollection(segs, cmap=plt.get_cmap('jet'))
lc.set_array(t)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.add_collection3d(lc)
x1 = np.linspace(1,2,100)
y1 = x1
z1 = x1
t1 = np.linspace(1,2,100)
points1 = np.array([x1,y1,z1]).transpose().reshape(-1,1,3)
segs1 = np.concatenate([points1[:-1],points1[1:]],axis=1)
lc = Line3DCollection(segs1, cmap=plt.get_cmap('jet'))
lc.set_array(t1)
lc.update_scalarmappable()
ax.add_collection3d(lc)
x2 = np.linspace(1,2,100)
y2 = -x2+2
z2 = -x2+2
t2 = np.linspace(1,2,100)
points2 = np.array([x2,y2,z2]).transpose().reshape(-1,1,3)
segs2 = np.concatenate([points2[:-1],points2[1:]],axis=1)
lc = Line3DCollection(segs2, cmap=plt.get_cmap('jet'))
lc.set_array(t1)
lc.update_scalarmappable()
ax.add_collection3d(lc)
ax.set_xlim(0, 2)
ax.set_ylim(0, 2)
ax.set_zlim(0, 2)
fig.colorbar(lc)
plt.show()
plotting this plot
Thanks in advance
Related
I am trying to Euclidean transform one plane to other plane. I have two equation of planes and they have different sizes. How can I align two planes in one coordinate system?
My two planes in form of ax+by+cz+d=0.
first plane => a = -5.297742252442251, b = 21.751836101364013, c = -2.470896764133499, d = -0.5601826186620921
Second plane => a = 45.42557999642176, b = -16.9433283673388, c = 2.5117971500097287, d = -8.528560240570203]
For plotting on the matplotlib, I used following code using matplotlib
import numpy as np
import pandas as pd
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
from skimage import measure
import pandas as pd
# Data points 1 and 2
data1 = [[0.190133571624755, 0.146549582481384, 0.391435742378234, 'near'],
[0.0154470205307006, 0.0959569215774536, 0.484999418258667, 'near'],
[-0.119875073432922, 0.0414541959762573, 0.542818903923034, 'near'],
[0.104917883872985, 0.058539867401123, 0.171926498413085, 'far'],
[0.177520513534545, 0.130982756614685, 0.0330302715301513, 'far'],
[0.246979117393493, 0.173633933067321, 0.373323440551757, 'far']]
data2 = [[0.334545135498046, -0.0318257808685302, 0.282101511955261, 'near'],
[0.411889553070068, 0.0223467350006103, 0.183727979660034, 'near'],
[0.330880641937255, -0.00959080457687378, 0.178299665451049, 'near'],
[-0.00756144523620605, -0.07442307472229, -0.227764248847961, 'far'],
[-0.268512785434722, -0.309048891067504, 0.456292867660522, 'far'],
[-0.305409669876098, -0.304299354553222, 0.281461238861084, 'far']]
# Create the pandas DataFrame
df1 = pd.DataFrame(data1, columns=['A', 'B', 'C', 'NearOrFar'])
df2 = pd.DataFrame(data2, columns=['A', 'B', 'C', 'NearOrFar'])
# Data - 1
# Divide into X and y
X1 = df1.iloc[:,0:3]
Y1 = df1.iloc[:,3]
# Create scatter plot of data points for data 1
fig = plt.figure(figsize=(10, 10))
ax = plt.axes(projection='3d')
for grp_name, grp_idx in df1.groupby('NearOrFar').groups.items():
x = df1.iloc[grp_idx, 0]
y = df1.iloc[grp_idx, 1]
z = df1.iloc[grp_idx, 2]
if (grp_name == 'near'):
ax.scatter(x, y, z, label=grp_name, c = 'red')
else:
ax.scatter(x, y, z, label=grp_name, c = 'blue')
# Train LDA model for data 1
lda_clf_1 = LDA(store_covariance=True)
lda_clf_1.fit(X1, Y1)
# Decision boundary Coefficient
a,b,c,d = lda_clf_1.coef_[0][0],lda_clf_1.coef_[0] [1],lda_clf_1.coef_[0][2],lda_clf_1.intercept_
# Find limit of each coordinates
xlim = ax.get_xlim()
ylim = ax.get_ylim()
# Create meshgrid in xyz
xx = np.linspace(xlim[0], xlim[1], 50)
yy = np.linspace(ylim[0], ylim[1], 50)
X,Y = np.meshgrid(xx,yy)
Z = (-d - a*X - b*Y) / c
# plot decision boundary hyperplane
ax.plot_surface(X, Y, Z, alpha=0.45)
plt.show()
# Data - 2
# Divide into X and y
X2 = df2.iloc[:,0:3]
Y2 = df2.iloc[:,3]
# Create scatter plot of data points for data 2
fig = plt.figure(figsize=(10, 10))
ax = plt.axes(projection='3d')
for grp_name, grp_idx in df2.groupby('NearOrFar').groups.items():
x = df2.iloc[grp_idx, 0]
y = df2.iloc[grp_idx, 1]
z = df2.iloc[grp_idx, 2]
if (grp_name == 'near'):
ax.scatter(x, y, z, label=grp_name, c = 'red')
else:
ax.scatter(x, y, z, label=grp_name, c = 'blue')
# Train LDA model for data 2
lda_clf_2 = LDA(store_covariance=True)
lda_clf_2.fit(X2, Y2)
# Decision boundary Coefficient
a,b,c,d = lda_clf_2.coef_[0][0],lda_clf_2.coef_[0][1],lda_clf_2.coef_[0][2],lda_clf_2.intercept_
# Find limit of each coordinates
xlim = ax.get_xlim()
ylim = ax.get_ylim()
# Create meshgrid in xyz
xx = np.linspace(xlim[0], xlim[1], 50)
yy = np.linspace(ylim[0], ylim[1], 50)
X,Y = np.meshgrid(xx,yy)
Z = (-d - a*X - b*Y) / c
# plot decision boundary hyperplane
ax.plot_surface(X, Y, Z, alpha=0.45)
plt.show()
How can I align two planes and create 3d plot of two align planes in one graph with data points?
At the end, I want to transform(mostly roatation I think?) all the data points on data2 to coordinate system of data1 as data2 hyperplane align with data1 hyperplane
Each datapoints with hyperplane should looks like following
Data 1 =
enter image description here
and
Data 2 = enter image description here
If you simply want to plot the two planes on the same 3d axes, then you just need to compute two different values for Z and plot the two Zs relative to the same Xs and Ys, like so:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
# first plane:
a1 = -5.297742252442251
b1 = 21.751836101364013
c1 = -2.470896764133499
d1 = -0.5601826186620921
# second plane:
a2 = 45.42557999642176
b2 = -16.9433283673388
c2 = 2.5117971500097287
d2 = -8.528560240570203
x = np.linspace(-1, 1, 100)
y = np.linspace(-1, 1, 100)
X, Y = np.meshgrid(x, y)
Z1 = (-d1 - a1 * X - b1 * Y) / c1
Z2 = (-d2 - a2 * X - b2 * Y) / c2
fig = plt.figure(figsize=(10, 10))
ax = plt.axes(projection="3d")
ax.plot_surface(X, Y, Z1, alpha=0.45)
ax.plot_surface(X, Y, Z2, alpha=0.45)
plt.show()
This produces the following figure:
As to "aligning" the two planes, it's not clear to me what you're asking...? Two planes will be coplanar if they have the same values of a, b, c and d in the equation you gave above - if they have different values of a, b, c and d, they won't be coplanar. They may intersect, they may not - and to ensure you graph them where they intersect (should they do so), you'd need to determine the values of x and y where they have equal values of z and set your xrange and yrange accordingly. If you can explain the meaning of "align" for your particular purposes, perhaps I could expand on this answer to accommodate.
I'm plotting two surface plots in python obtained from np.meshgrid, which I want to append to create only one surface plot. For instance:
fig = plt.figure(figsize=(8,4))
ax = fig.add_subplot(projection='3d')
# First surface:
x1 = np.linspace(0,1,100)
y1 = np.linspace(0,1,100)
X1,Y1 = np.meshgrid(x1,y1)
Z1 = 2*Y1
solid = ax.plot_surface(X1,Y1,Z1,cmap=cm.coolwarm,linewidth=0, antialiased=True)
# Second surface:
x2 = np.linspace(0,1,100)
y2 = np.linspace(1,2,100)
X2,Y2 = np.meshgrid(x2,y2)
Z2 = 2*Y2
solid = ax.plot_surface(X2,Y2,Z2,cmap=cm.coolwarm,linewidth=0, antialiased=True)
My first idea was to append them with np.append, such that:
X = np.append(X1,X2)
Y = np.append(Y1,Y2)
Z = np.append(Z1,Z2)
solid = ax.plot_surface(X,Y,Z,cmap=cm.coolwarm,linewidth=0, antialiased=True)
But as expected, I got an error since Z was flattened and, moreover, I'm not sure if X and Y are right. In the example above it's trivial how could I create only one surface since both of them have the same angle, but I want to solve for the general case where they could have different inclinations. How can I overcome this problem? Thank you in advance!
I would simply fill only one Z array, for example:
fig = plt.figure(figsize=(8,4))
ax = fig.add_subplot(projection='3d')
x = np.linspace(0,1,100)
y = np.linspace(0,2,100)
X, Y = np.meshgrid(x, y)
Z = np.empty_like(Y)
Z[Y < 1] = 2 * Y[Y < 1]
Z[Y >= 1] = 10 * Y[Y >= 1] - 8 # different slope
solid = ax.plot_surface(X , Y, Z, cmap=plt.cm.coolwarm, linewidth=0, antialiased=True)
I would like to find the intersection of two graphs. It took me 674 points to plot the first graph and only 14 points to plot the second graph.
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
df = pd.read_csv("test1.csv",,skiprows=range(9),names=['A', 'B', 'C','D'])
df2 = pd.read_csv("test2.csv",skiprows=range(1),names=['X','Y'])
x1 = df['A'].tolist()
x1 = np.array(x1)
y1 = df['D'].tolist()
y1 = np.array(y1)
x2 = df2['X'].tolist()
x2 = np.array(x2)
y2 = df2['Y'].tolist()
y2 = np.array(y2)
idx = np.argwhere(np.diff(np.sign(y1 - y2))).flatten()
fig, ax = plt.subplots()
ax.plot(x1, y1, 'blue')
ax.plot(x2, y2, 'red')
plt.show()
However, I am getting this error from the code above due to the different sizes of numpy. Any ways I can solve this?
operands could not be broadcast together with shapes (674,) (14,)
You should compute interpolations of both curves with scipy.interpolate.interp1d, then you can calculate indeces of intersection points with the method you used.
I assume you have two curves with x1, x2, y1 and y2 coordinates, with different lengths and x axis limits:
x1 = np.linspace(-2, 12, 674)
x2 = np.linspace(0, 8, 14)
y1 = np.sin(x1)
y2 = np.cos(x2) + 1
So, you have to compute interpolation functions:
f1 = interp1d(x1, y1, kind = 'linear')
f2 = interp1d(x2, y2, kind = 'linear')
Then, you need to evaluate new curves on a common x axis, so new curves will have the same length:
xx = np.linspace(max(x1[0], x2[0]), min(x1[-1], x2[-1]), 1000)
y1_interp = f1(xx)
y2_interp = f2(xx)
Finally, you can compute indices of interpolation points as you already did:
idx = np.argwhere(np.diff(np.sign(y1_interp - y2_interp))).flatten()
Complete Code
import numpy as np
import matplotlib.pyplot as plt
from scipy.interpolate import interp1d
x1 = np.linspace(-2, 12, 674)
x2 = np.linspace(0, 8, 14)
y1 = np.sin(x1)
y2 = np.cos(x2) + 1
f1 = interp1d(x1, y1, kind = 'linear')
f2 = interp1d(x2, y2, kind = 'linear')
xx = np.linspace(max(x1[0], x2[0]), min(x1[-1], x2[-1]), 1000)
y1_interp = f1(xx)
y2_interp = f2(xx)
idx = np.argwhere(np.diff(np.sign(y1_interp - y2_interp))).flatten()
fig, ax = plt.subplots()
ax.plot(x1, y1, 'blue', label = 'y1')
ax.plot(x2, y2, 'red', label = 'y2')
for index in idx:
ax.plot(xx[index], y1_interp[index], marker = 'o', markerfacecolor = 'black', markeredgecolor = 'black')
plt.show()
Plot
here is the code im using and I've also attached the output. I'd like to plot a two dimensional lognorm function as a 3d surface, the above code is supposed to do this however the output results in the entire plane being skewed rather than just the z values. any help or suggestions would be greatly appreciated.
dx = 90 - (-90)
dy = 90 - (-90)
c = [dx + dx/2.0, dy+dy/2.0]
z = np.zeros((400, 400))
x = np.linspace(-90, 90, 400)
y = x.copy()
for i in range(len(x)):
for j in range(len(y)):
p =[x[i], y[j]]
d = math.sqrt((p[0]-c[0])**2 + (p[1]-c[1])**2)
t = d
z[i][j] = lognorm.pdf(t, 1.2)
fig = plt.figure()
ax = fig.add_subplot(111, projection = '3d')
ax.plot_surface(x,y, z, cmap = 'viridis')
plt.show()
output of the provided code
ideally I'd like for it to look something like this.
this is the image here
I think you wanted to plot a 3D surface and here is an example:
#!/usr/bin/python3
# 2018/10/25 14:44 (+0800)
# Plot a 3D surface
from scipy.stats import norm, lognorm
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
xy = np.linspace(-5, 5, 400)
xx, yy = np.meshgrid(xy)
t = np.sqrt(xx**2 + yy**2)
zz = lognorm.pdf(t, 1.2)
fig = plt.figure()
ax = fig.add_subplot(111, projection = '3d')
ax.plot_surface(xx,yy, zz, cmap = 'viridis')
plt.show()
The figure above is a great artwork showing the wind speed, wind direction and temperature simultaneously. detailedly:
The X axes represent the date
The Y axes shows the wind direction(Southern, western, etc)
The variant widths of the line were stand for the wind speed through timeseries
The variant colors of the line were stand for the atmospheric temperature
This simple figure visualized 3 different attribute without redundancy.
So, I really want to reproduce similar plot in matplotlib.
My attempt now
## Reference 1 http://stackoverflow.com/questions/19390895/matplotlib-plot-with-variable-line-width
## Reference 2 http://stackoverflow.com/questions/17240694/python-how-to-plot-one-line-in-different-colors
def plot_colourline(x,y,c):
c = plt.cm.jet((c-np.min(c))/(np.max(c)-np.min(c)))
lwidths=1+x[:-1]
ax = plt.gca()
for i in np.arange(len(x)-1):
ax.plot([x[i],x[i+1]], [y[i],y[i+1]], c=c[i],linewidth = lwidths[i])# = lwidths[i])
return
x=np.linspace(0,4*math.pi,100)
y=np.cos(x)
lwidths=1+x[:-1]
fig = plt.figure(1, figsize=(5,5))
ax = fig.add_subplot(111)
plot_colourline(x,y,prop)
ax.set_xlim(0,4*math.pi)
ax.set_ylim(-1.1,1.1)
Does someone has a more interested way to achieve this? Any advice would be appreciate!
Using as inspiration another question.
One option would be to use fill_between. But perhaps not in the way it was intended. Instead of using it to create your line, use it to mask everything that is not the line. Under it you can have a pcolormesh or contourf (for example) to map color any way you want.
Look, for instance, at this example:
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import interp1d
def windline(x,y,deviation,color):
y1 = y-deviation/2
y2 = y+deviation/2
tol = (y2.max()-y1.min())*0.05
X, Y = np.meshgrid(np.linspace(x.min(), x.max(), 100), np.linspace(y1.min()-tol, y2.max()+tol, 100))
Z = X.copy()
for i in range(Z.shape[0]):
Z[i,:] = c
#plt.pcolormesh(X, Y, Z)
plt.contourf(X, Y, Z, cmap='seismic')
plt.fill_between(x, y2, y2=np.ones(x.shape)*(y2.max()+tol), color='w')
plt.fill_between(x, np.ones(x.shape) * (y1.min() - tol), y2=y1, color='w')
plt.xlim(x.min(), x.max())
plt.ylim(y1.min()-tol, y2.max()+tol)
plt.show()
x = np.arange(100)
yo = np.random.randint(20, 60, 21)
y = interp1d(np.arange(0, 101, 5), yo, kind='cubic')(x)
dv = np.random.randint(2, 10, 21)
d = interp1d(np.arange(0, 101, 5), dv, kind='cubic')(x)
co = np.random.randint(20, 60, 21)
c = interp1d(np.arange(0, 101, 5), co, kind='cubic')(x)
windline(x, y, d, c)
, which results in this:
The function windline accepts as arguments numpy arrays with x, y , a deviation (like a thickness value per x value), and color array for color mapping. I think it can be greatly improved by messing around with other details but the principle, although not perfect, should be solid.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
x = np.linspace(0,4*np.pi,10000) # x data
y = np.cos(x) # y data
r = np.piecewise(x, [x < 2*np.pi, x >= 2*np.pi], [lambda x: 1-x/(2*np.pi), 0]) # red
g = np.piecewise(x, [x < 2*np.pi, x >= 2*np.pi], [lambda x: x/(2*np.pi), lambda x: -x/(2*np.pi)+2]) # green
b = np.piecewise(x, [x < 2*np.pi, x >= 2*np.pi], [0, lambda x: x/(2*np.pi)-1]) # blue
a = np.ones(10000) # alpha
w = x # width
fig, ax = plt.subplots(2)
ax[0].plot(x, r, color='r')
ax[0].plot(x, g, color='g')
ax[0].plot(x, b, color='b')
# mysterious parts
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
# mysterious parts
rgba = list(zip(r,g,b,a))
lc = LineCollection(segments, linewidths=w, colors=rgba)
ax[1].add_collection(lc)
ax[1].set_xlim(0,4*np.pi)
ax[1].set_ylim(-1.1,1.1)
fig.show()
I notice this is what I suffered.