Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Draw 3D plot with minimal interpolation

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 3d_scatter_plot

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).

contourf3d_demo2

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
like image 446
Agostino Avatar asked Nov 09 '22 17:11

Agostino


1 Answers

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: enter image description here

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.

like image 95
Konstantin Schubert Avatar answered Nov 14 '22 21:11

Konstantin Schubert