Plotting cross section through 3d scatter plot in python - python

I have a simple script, that plots lat,lon and depth of points in 3D.
Is there a python module or some simple solution I could use to get a cross section over this points?
Cross section should be a vertical plane, for which I would set two coordinates (in plane view), depth and thickness (how many km in each direction perpendicular to the cross sections I want the data).
So something like point1(lat1,lon1), point2(lat2,lon2), depthmin, depthmax, thicknes
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
lats=[]
lons=[]
depts=[]
file_open=open("catZMAPVpVs")
for fo in file_open:
element=fo.split("\t")
lons.append(float(element[0]))
lats.append(float(element[1]))
depts.append(-float(element[6]))
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(lons,lats, depts, c='r', marker='o')
ax.set_xlabel('lon')
ax.set_ylabel('lat')
ax.set_zlabel('depth')
plt.show()
thanks!

Related

How to change spines linewidth in 3D plot?

When you have a 2D plot in matplolib you can change the line width of spines (the containing box) as follows:
fig, ax = plt.subplots()
ax.plot([1,2,3])
spines = ax.spines
[i.set_linewidth(5) for i in spines.values()]
Figure with thick spines:
However this same methodology does not work for 3D plots.
How could I change the axes line thickness for a 3D plot?
You can do this using the following code. I adapted the idea of accessing the axes in 3D from this answer. If you want to change the thickness of all the grid lines as well, refer to this answer by ImportanceOfBeingErnest
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for axis in [ax.w_xaxis, ax.w_yaxis, ax.w_zaxis]:
axis.line.set_linewidth(5)

How to do a 3D circle in Matplotlib

I am a beginner in Python. I'm trying to plot a circle using matplotlib that has tangent to Z axis. I know how to draw a sphere in 3D but don't know how to draw a circle/ring in 3D plot. Can someone help me with the code? Thanks in advance!
You need the usual imports, plus the 3D toolkit
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
You need a 3D enabled axes object
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
You need a circle, contained in the plane y-z
theta = np.linspace(0, 2 * np.pi, 201)
y = 10*np.cos(theta)
z = 10*np.sin(theta)
now we can plot the original circle and, as an example, a number of circles rotated about the z-axis and whose centers are also placed at a fixed distance (equal to the c ircles'radius) from the z-axis, so that they are tangent to it
for i in range(18):
phi = i*np.pi/9
ax.plot(y*np.sin(phi)+10*np.sin(phi),
y*np.cos(phi)+10*np.cos(phi), z)
eventually we place a vertical axis and a legend
ax.plot((0,0),(0,0), (-10,10), '-k', label='z-axis')
ax.legend()
It's time to see what we got
plt.show()
mpl_toolkits.mplot3d.art3d
https://matplotlib.org/3.2.1/gallery/mplot3d/pathpatch3d.html was mentioned
in a comment, the example can be minimized to:
#!/usr/bin/env python3
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
import mpl_toolkits.mplot3d.art3d as art3d
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# Draw a circle on the x=0 'wall'
p = Circle((5, 5), 3)
ax.add_patch(p)
art3d.pathpatch_2d_to_3d(p, z=0, zdir="x")
ax.set_xlim(0, 10)
ax.set_ylim(0, 10)
ax.set_zlim(0, 10)
plt.show()
which gives:
This is a bit nicer than https://stackoverflow.com/a/56871467/895245 as it uses a higher level Circle object directly, instead of requiring you to explicitly plot the lines.
Unfortunately, 3D support in matplotlib is a bit limited as mentioned in the documentation itself, and you have to do some extra work to plot on planes not parallel to the main coordinate plane: How can matplotlib 2D patches be transformed to 3D with arbitrary normals?
Tested on matplotlib==3.2.2.

What is the most descriptive way to plot Lorenz System?

I am (numerically) solving the Lorenz System by using different methods. I am plotting it using matplotlib but I would like a way to distinguish better the points.
For example:
Let's assume the points to be plotted are stored in the array a which has the form
array([[ 0.5 , 0.5 , 0.5 ],
[ 0.50640425, 0.6324552 , 0.48965064]])
#...
Now these lines of code
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(a[:,0],a[:,1],a[:,2])
plt.show()
produce:
Not very descriptive, is it? So I thought plotting discrete points would work better. So these ones:
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(a[:,0],a[:,1],a[:,2], s=0.2)
plt.show()
produce:
But it is not as descriptive as I want. I want to know what is the most descriptive way to plot the Lorenz system.
Consider making your scatter points transparent. You can do this by passing an alpha keyword to plt.scatter. Here's an example, modified from mplot3d example gallery, with alpha = 1.0, which is the default value:
ax.scatter(xs, ys, zs, alpha=1.0, s=0.2)
And here is the same scatter point cloud drawn with alpha = 0.1:
ax.scatter(xs, ys, zs, alpha=0.1, s=0.2)
Note that while this appears to be a good visualization, the interactive part of it is quite slow for a large number of points. If you really need fast performance, consider an alternative approach - splitting the lines in segments and coloring them by index, similarly to what's being done here.

mplot3D fill_between extends over axis limits

I have questions related to creating a simple lineplot in Python with mplot3D where the area under the plot is filled. I am using Python 2.7.5 on RedHatEnterprise 7.2, matplotlib 1.2.0 and numpy 1.7.2.
Using the code below, I am able to generate a line plot. This is displayed as expected with the beginning / end of the plot set by the limits of the imported data set.
I am then trying to fill the area between the line plot and -0.1 using the answer given by Bart from Plotting a series of 2D plots projected in 3D in a perspectival way. This works, however, the filled area is continued beyond the limits of the data set. This is also the case when running the example from the link.
This screen shot shows the plot generated with filled area extending beyond the set axis limits.
How do I achieve that the filled area is only the range of the data set or the axis limits whichever is smaller?
How do I add a legend for those plots onto the figure?
Code as follows:
from numpy import *
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d import Axes3D
x,y = genfromtxt("data.dat",unpack=True)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.add_collection3d(plt.fill_between(x,y,-0.1, color='orange', alpha=0.3,label="filled plot"),1, zdir='y')
ax.plot(x,y,1,zdir="y",label="line plot")
ax.legend()
ax.set_xlim3d(852.353,852.359)
ax.set_zlim3d(-0.1,5)
ax.set_ylim3d(0,2)
ax.get_xaxis().get_major_formatter().set_useOffset(False)
plt.show()
I don't know how to put fill_between working the way you want it to, but I can provide an alternative using a 3D polygon:
from numpy import *
import matplotlib.pylab as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection # New import
#x,y = genfromtxt("data.dat",unpack=True)
# Generated some random data
w = 3
x,y = np.arange(100), np.random.randint(0,100+w,100)
y = np.array([y[i-w:i+w].mean() for i in range(3,100+w)])
z = np.zeros(x.shape)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
#ax.add_collection3d(plt.fill_between(x,y,-0.1, color='orange', alpha=0.3,label="filled plot"),1, zdir='y')
verts = [(x[i],z[i],y[i]) for i in range(len(x))] + [(x.max(),0,0),(x.min(),0,0)]
ax.add_collection3d(Poly3DCollection([verts],color='orange')) # Add a polygon instead of fill_between
ax.plot(x,z,y,label="line plot")
ax.legend()
ax.set_ylim(-1,1)
plt.show()
The code above generates some random data. Builds vertices from it and plots a polygon with those vertices. This will give you the plot you wish (but does not use fill_between). The result is:

Adding Radial Axis Label in matplotlib

I am making a polar scatter plot for a college project with matplotlib and I can't find out how to add a label to the radial axis. Here is my code ( I left out the data because it was read out of a csv)
import matplotlib.pyplot as plt
ax = plt.subplot(111, polar=True)
ax.set_rmax(1)
c = plt.scatter(theta, radii)
ax.set_title("Spread of Abell Cluster Supernova Events as a Function of Fractional Radius", va='bottom')
ax.legend(['Supernova'])
plt.show()
(My plot looks like this. I can't seem to find any straight forward method to do it. Has anyone dealt with this before and have any suggestions?
I don't know of a built in way to do it, but you could use ax.text to make your own. You can get the position of the radial tick labels using ax.get_rlabel_position(), and the mid point of the radial axis using ax.get_rmax()/2.
For example, here's your code (with some random data):
import matplotlib.pyplot as plt
import numpy as np
theta=np.random.rand(40)*np.pi*2.
radii=np.random.rand(40)
ax = plt.subplot(111, polar=True)
ax.set_rmax(1)
c = plt.scatter(theta, radii)
ax.set_title("Spread of Abell Cluster Supernova Events as a Function of Fractional Radius", va='bottom')
ax.legend(['Supernova'])
label_position=ax.get_rlabel_position()
ax.text(np.radians(label_position+10),ax.get_rmax()/2.,'My label',
rotation=label_position,ha='center',va='center')
plt.show()
And here's the output:
I'd be interested to see if there's a more elegant solution, but hopefully this helps you.
from pylab import *
N = 150
r = 2*rand(N)
theta = 2*pi*rand(N)
area = 200*r**2*rand(N)
colors = theta
ax = subplot(111, polar=True)
c = scatter(theta, r, c=colors, s=area, cmap=cm.hsv)
c.set_alpha(0.75)
ax.set_ylabel('Radius', rotation=45, size=11)
show()
A slightly different method from #tom. This uses directly the plt.legend option.
Example:
import matplotlib.pyplot as plt
import numpy as np
theta=np.random.rand(40)*np.pi*2.
radii=np.random.rand(40)
ax = plt.subplot(111, polar=True)
ax.set_rmax(1)
c = plt.scatter(theta, radii,label='Supernova')
ax.set_title("Spread of Abell Cluster Supernova Events as a Function of Fractional Radius", va='bottom')
ax.legend(loc='lower right', scatterpoints=1)
plt.show()
You can change lower right to upper right or even to best to leave the alignment of the legend to matplotlib.

Categories