3D plot python matplotlib - python

I would like to know how to transform data so as to obtain:
a plot of each signal from each file corresponding to an Z Value in one image.
View 3D Data with X, Y, Z .
(X,Y) from import text files and Z is a list.
X column 1 and Y column 2,
I have written some code,
import matplotlib.pyplot as plt
import glob,os
from matplotlib import pyplot
from mpl_toolkits.mplot3d.axes3d import Axes3D
from pylab import *
from mpl_toolkits.mplot3d import axes3d
for data in glob.iglob("S21/*.txt"):
data = np.loadtxt(data,skiprows=21)
fig = plt.figure()
ax = fig.gca(projection='3d')
x_data = data[:, 1]
y_data = data[:, 2]
x_array = np.array(x_data)
y_array = np.array(y_data)
z_array = np.array([0,100,200,300,400,500,600,700,800,900,1000,1100])
X,Y,Z = x_array, y_array, z_array
plt.plot(X,Y,"o",label= 'Measured Data')
ax.set_xlabel('X')
ax.set_xlim3d()
ax.set_ylabel('Y')
ax.set_ylim3d()
ax.set_zlabel('Z')
ax.set_zlim3d()
plt.show()

Related

Plot 3d points (x,y,z) in 2d plot with colorbar

I have computed a lot (~5000) of 3d points (x,y,z) in a quite complicated way so I have no function such that z = f(x,y). I can plot the 3d surface using
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
X = surface_points[:,0]
Y = surface_points[:,1]
Z = surface_points[:,2]
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
surf = ax.plot_trisurf(X, Y, Z, cmap=cm.coolwarm, vmin=np.nanmin(Z), vmax=np.nanmax(Z))
I would like to plot this also in 2d, with a colorbar indicating the z-value. I know there is a simple solution using ax.contour if my z is a matrix, but here I only have a vector.
Attaching the plot_trisurf result when rotated to xy-plane. This is what I what like to achieve without having to rotate a 3d plot. In this, my variable surface_points is an np.array with size 5024 x 3.
I had the same problems in one of my codes, I solved it this way:
import numpy as np
from scipy.interpolate import griddata
import matplotlib.pylab as plt
from matplotlib import cm
N = 10000
surface_points = np.random.rand(N,3)
X = surface_points[:,0]
Y = surface_points[:,1]
Z = surface_points[:,2]
nx = 10*int(np.sqrt(N))
xg = np.linspace(X.min(), X.max(), nx)
yg = np.linspace(Y.min(), Y.max(), nx)
xgrid, ygrid = np.meshgrid(xg, yg)
ctr_f = griddata((X, Y), Z, (xgrid, ygrid), method='linear')
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.contourf(xgrid, ygrid, ctr_f, cmap=cm.coolwarm)
plt.show()
You could use a scatter plot to display a projection of your z color onto the x-y axis.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
N = 10000
surface_points = np.random.rand(N,3)
X = surface_points[:,0]
Y = surface_points[:,1]
Z = surface_points[:,2]
# fig = plt.figure()
# ax = fig.add_subplot(projection='3d')
# surf = ax.plot_trisurf(X, Y, Z, cmap=cm.coolwarm, vmin=np.nanmin(Z), vmax=np.nanmax(Z))
fig = plt.figure()
cmap = cm.get_cmap('coolwarm')
color = cmap(Z)[..., :3]
plt.scatter(X,Y,c=color)
plt.show()
Since you seem to have a 3D shape that is hollow, you could split the projection into two like if you cur the shape in two pieces.
fig = plt.figure()
plt.subplot(121)
plt.scatter(X[Z<0.5],Y[Z<0.5],c=color[Z<0.5])
plt.title('down part')
plt.subplot(122)
plt.scatter(X[Z>=0.5],Y[Z>=0.5],c=color[Z>+0.5])
plt.title('top part')
plt.show()

How to apply a Colormap to a line interpolated using CubicHermitSpline, based on value in the y-direction

I want to add a colormap to this interpolated line, such that the colour of the line-segment changes with the value in the y-axis.
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import CubicHermiteSpline
fig, ax = plt.subplots()
x = [1,2,3,4,5,6,7,8,9,10]
y = [8,2,1,7,5,5,8,1,9,5]
cs = CubicHermiteSpline(x, y, np.zeros(len(x)))
xs = np.linspace(min(x), max(x), num=100)
ax.plot(xs, cs(xs))
Further updates:
I took the suggestion of playing around with this tutorial. I've been able to create a LineCollection and apply the colormap. However, the multicoloured line will only show up behind the plotted line. Removing the plot command removes all lines from plot.
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import CubicHermiteSpline
from matplotlib.collections import LineCollection
from matplotlib.colors import ListedColormap, BoundaryNorm
fig, ax = plt.subplots()
x = [1,2,3,4,5,6,7,8,9,10]
y = [8,2,1,7,5,5,8,1,9,5]
cs = CubicHermiteSpline(x, y, np.zeros(len(x)))
xs = np.linspace(min(x), max(x), num=400)
points = np.array([xs, cs(xs)]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
norm = plt.Normalize(cs(xs).min(), cs(xs).max())
lc = LineCollection(segments, cmap='rainbow', norm=norm)
lc.set_array(cs(xs))
lc.set_linewidth(2)
line = ax.add_collection(lc)
fig.colorbar(line, ax=ax)
ax.plot(xs, cs(xs))

Matplotlib not plotting all points

I am trying to plot a 3D-Array in matplotlib, but I only see a linear output. The expected output was a 10x10x10 cube.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
points = np.zeros((10, 10, 10))
for x in range(10):
for y in range(10):
for z in range(10):
points[x][y][z] = z
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(points[:,0],points[:,1],points[:,2])
plt.show()
OK, you were very, very close. I didn't realize how close until I tried it. The problem you had was that you made points a 3D array where each entry had a value. It needed to be a 2D array, 1000 x 3.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
points = []
for x in range(10):
for y in range(10):
for z in range(10):
points.append((x,y,z))
points = np.array(points)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(points[:,0],points[:,1],points[:,2])
plt.show()
You've got a good answer by Tim. However, there are alternatives approaches. For example, there is np.meshgrid() that are often used in your situation to produce and manipulate data. Here is the code to generate array of data and produce sample plot.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
n1 = 10 #number of grid rows/columns
xg, yg = np.meshgrid(np.arange(n1),np.arange(n1))
for i in np.arange(n1):
zg = np.ones(xg.shape) * i
ax.scatter(xg, yg, zg, s=3, c='k')
lim = n1 + 0.1*n1
ax.set_xlim3d(-0.1*n1, lim)
ax.set_ylim3d(-0.1*n1, lim)
ax.set_zlim3d(-0.1*n1, lim)
# set viewing angle
ax.azim = 120 # z rotation (default=270); 160+112
ax.elev = 35 # x rotation (default=0)
ax.dist = 10 # zoom (define perspective)
plt.show()

Matplotlib line plot: coloring regions of high curvature

I want to plot a line in 3D Space and color regions of high curvature. Right now I have a workaround using a discrete scatter plot:
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cmx
mpl.rcParams['legend.fontsize'] = 10
data = np.loadtxt('data',usecols=range(0,4))
x = data[:,0]
y = data[:,1]
z = data[:,2]
cs = data[:,3]
colorsMap='jet'
cm = plt.get_cmap(colorsMap)
cNorm = mpl.colors.Normalize(vmin=min(cs), vmax=max(cs))
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=cm)
fig = plt.figure()
scalarMap.set_array(cs)
fig.colorbar(scalarMap)
ax = fig.gca(projection='3d')
ax.scatter(x, y, z, c=scalarMap.to_rgba(cs), label='scatter curve')
ax.legend()
plt.show()
But I would rather have a continuous line plot.Is there a way to do that?
Depending on how many data points you have you might be able to get your way around this. For instance, consider the generated 3D spiral data below in substitution to your data.txt
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cmx
mpl.rcParams['legend.fontsize'] = 10
theta = np.linspace(-4 * np.pi, 4 * np.pi, 1000)
z = np.linspace(-2, 2, 1000)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)
cs = 1/r
colorsMap='jet'
cm = plt.get_cmap(colorsMap)
cNorm = mpl.colors.Normalize(vmin=min(cs), vmax=max(cs))
scalarMap = cmx.ScalarMappable(norm=cNorm, cmap=cm)
fig = plt.figure()
scalarMap.set_array(cs)
ax = fig.gca(projection='3d')
ax.scatter(x, y, z, c=scalarMap.to_rgba(cs), marker='_', s=1)
plt.colorbar(scalarMap)
plt.show()
If the sampling frequency of your data points is not as "tight", then this won't look as nice. However, you could use this accepted answer to improve upon this.

Plot NumPy ndarray into a 3D surface

I have a numpy.ndarray of size 200x200. I want to plot it as a 3D surface where x and y are indexes of the array and z is the value of that array element. Is there any easy way to do it or do I have to transform my array into a long list?
For example using matplotlib:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
#your index
x = np.linspace(1, 200, 200);
y = np.linspace(1, 200, 200)
X, Y = np.meshgrid(x, y); #making a grid from it
fig = plt.figure()
ax = fig.gca(projection='3d')
R = np.sqrt(X**2 + Y**2) #make some calculations on the grid
Z = np.sin(R) #some more calculations
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
ax.set_zlim(-5, 5)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
However, as your array is already quite large, you might want to consider a different plotting tool like mayavi. matplotlib usually puts a copy of your complete array into the plot. That's memory demanding when dealing with big data. But I'm not sure, whether mayavi does the same or not.
You can also use mayavi and plot your array as a plane with different colors representing the values. It would look like this:
import numpy
from mayavi import mlab
mlab.imshow(yourarray)
mlab.show()
Alternative you can create points with an elevation from the ground plane and get a fitting plane through the points. See here:http://docs.enthought.com/mayavi/mayavi/auto/example_surface_from_irregular_data.html#example-surface-from-irregular-data
What is best for you depends on the continuity of your data.
If what you want is to plot a 3D surface on top of a 2D grid what you could do is something similar to this:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
# create some fake data
array_distribution3d = np.ones((200, 200))
array_distribution3d[0:25, 0:25] = -1
# create the meshgrid to plot on
x = np.arange(0, array_distribution3d.shape[0])
y = np.arange(0, array_distribution3d.shape[1])
# here are the x,y and respective z values
X, Y = np.meshgrid(x, y)
z = []
for i in range(0, array_distribution3d.shape[0]):
z_y = []
for j in range(0, array_distribution3d.shape[1]):
z_y.append(array_distribution3d[i, j])
z.append(z_y)
Z = np.array(z)
# create the figure, add a 3d axis, set the viewing angle
fig = plt.figure(figsize=(12, 9))
ax = fig.add_subplot(111, projection='3d')
ax.view_init(45, 60)
# here we create the surface plot
ax.plot_surface(X, Y, Z)
However, to the best of my knowledge, this kind of data can be plotted as a colourmap.
This can be plotted as follows:
import numpy as np
import os.path
import matplotlib.pyplot as plt
array_distribution = np.ones((200, 200))
array_distribution[0:25, 0:25] = -1
fig = plt.imshow(array_distribution)
plt.colorbar(fraction=0.035, pad=0.035, ticks=[-1., 0., 1.])
axes = plt.gca()
axes.set_ylim([0, 200])
figure = plt.gcf()
file = os.path.join('demo1.png')
figure.savefig(file, dpi=250)
plt.close('all')
print('done')

Categories