I'm plotting a 3D scatter plot reading my values from a file. Each line of this file has 3 coordinates and a standard deviation. Let's keep the errors aside for the moment.
import os
import numpy as np
import matplotlib.pyplot as plt
input_file = os.path.normpath('C:/Users/sturaroa/Documents/my_file.tsv')
# read data from file
my_data = np.genfromtxt(input_file, delimiter='\t', skiprows=0)
X = my_data[:, 0] # 1st column
Y = my_data[:, 1] # 2nd column
Z = my_data[:, 2] # 3rd column
errors = my_data[:, 3] # 4th column (errors)
# draw 3D scatter graph
fig = plt.figure()
ax = fig.gca(projection='3d')
ax.scatter(X, Y, Z)
I get this
There is a nice example in the gallery that draws a surface and the projection of contours (image below). I need to have a similar representation while elaborating my data as little as possible (to prevent distortions).
I'm aware of this question explaining how to get a 3D surface out of irregular 3D data. However, it "smooths" the curve, and interpolates to a regular set of points. I've read the doc about griddata, it says it returns a
2d float array - Array of values interpolated at (xi, yi) points.
Not what I want. I was told by some people that I absolutely need to interpolate to find a surface. Ant it may be true. I was also told by some other people that interpolation is bad, because it forces a shape. And this is probably also true (for large values of "interpolation").
How can get a decent 3D graph with minimal interpolation? Is there something like just linking the closest 3D points together?
By the way, my data is fairly regular, like they are organized as a set of 2D planes, or "slices", but I'd like to know if this is possible without making that assumption.
Here's an example file, it's the same used for the scatter plot. It's simple and regular, I would suggest testing on a more general one, if possible.
2 1 2.0 0.0
2 2 82.666664 35.30187
2 3 100.0 0.0
2 4 98.0 4.472136
2 7 100.0 0.0
2 12 100.0 0.0
2 15 100.0 0.0
2 17 100.0 0.0
2 21 100.0 0.0
2 24 100.0 0.0
3 1 2.0 0.0
3 2 4.0 0.0
3 3 6.0 0.0
3 4 8.181818 0.60302263
3 7 15.090909 1.8683975
3 12 53.454544 33.6344
3 15 97.09091 3.9358494
3 17 97.09091 3.9358494
3 21 97.09091 3.3898242
3 24 97.09091 3.5058389
4 1 2.0 0.0
4 2 4.0 0.0
4 3 6.0 0.0
4 4 8.0 0.0
4 7 14.0 0.0
4 12 24.0 0.0
4 15 30.333334 0.74535596
4 17 37.666668 2.1343749
4 21 48.0 5.1639776
4 24 92.0 11.075499
Longer example input. The first 2 columns are supposed to be int
and the last 2 ones are float
.
Here's an improved loading, just in case
# tell numpy the first 2 columns are int and the last 2 are floats
my_data = np.genfromtxt(infile, dtype=[('a', '<i8'), ('b', '<i8'), ('x', '<f8'), ('d', '<f8')])
# access columns by name
print(my_data["b"]) # column 1
You want to draw a surface that goes exactly though all your data points and that does so "directly" without smoothing out.
I recently created this plot with matplotlib:
I wouldn't want to claim that it has minimal "smoothness", but at least it passes exactly through all data points.
I used the plot_surface function from matplotlib. You might also want to use plot_wireframe
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(xGrid, yGrid, zGrid, rstride=1, cstride=1,cmap="autumn")
I think part of the trick is to set rstride=1
and cstride=1
. You probably want to verify this. Someone with a deeper insight might be able to explain this better, in the documentation it just says that the stride is the sampling length.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With