3D surface/volume plot of list - python
I would like to represent a set of 3D points as a surface. The points are in an array format for x, y, z. I managed to plot the points in 3D in a sub-optimal solution by adding an array c which just contains 1's for each (x,y,z). There's surely a better solution for that already. What I want to do now is connect the points somehow and fill out a volume which contains these points in 3D.
import numpy as np
import matplotlib.pyplot as plt
x = np.array([3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8])
y = np.array([15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,21,21,21,15,15,15,15,15,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,26,26,17,17,18,18,18,18,18,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,30,30,30,30,30,20,20,20,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,30,30,30,30,30,30,30,30,30,30,31,31,31,31,31,31,31,31,31,31,32,32,32,32,32,32,32,32,32,32,33,33,33,33,33,33,33,33,33,33,22,23,23,23,23,24,24,24,24,24,24,24])
z = np.array([5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,8,9,10,1,2,3,4,5,1,2,3,4,5,6,7,8,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,4,5,6,7,8,9,10,9,10,1,2,1,2,3,4,5,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,6,7,8,9,10,1,2,3,1,2,3,4,5,6,1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,6,7,8,9,10,1,1,2,3,4,1,2,3,4,5,6,7])
c = np.array([1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1])
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
img = ax.scatter(x, y, z, c=c)
plt.show()
This is what I get:
What I approximately try to get:
So basically I'm looking for something that encloses all my points.
I hope this is not a duplicate, at least I did not find any answer that worked out for me.
Edit: Just added Mayavi to the tags, since I'm trying to find a workaround with the package.
Related
Stretching a line from a point in the xy-plane towards (x,y,z) point in matplotlib
I am trying to create a line that starts from the (x,y,0) point in the xy-plane and ends at the (x,y,z) value. Is this possible in matplotlib? Here's an example of what I have: versus what I want: Here we have the dot at (1,1,1). So basically I want to know if it's possible to extend a line going from (1,1,0) in the xy-plane to (1,1,1) in 3D. Hope my question is clear to understand with this example.
Here is what I did. from mpl_toolkits.mplot3d import Axes3D import matplotlib.cm as cm import numpy as np fig = plt.figure(figsize=(1[![enter image description here][1]][1]5,10)) ax = fig.add_subplot(111, projection='3d') # points xs = np.asarray([np.cos(i) for i in np.arange(0,np.pi/2,(np.pi/2)/10)]) ys = np.arange(0,10,1) zs = np.arange(0,10,1) # plot points ax.scatter(xs=xs, ys=ys, zs=zs, s=200, c=zs, cmap=cm.viridis_r, alpha=1.0) # create the line zs_l = np.asarray([[i, -1] for i in zs]) # color list cl = [cm.get_cmap('viridis_r')(i/zs_l.shape[0]) for i in range(zs_l.shape[0])] # draw the lines for i, p in enumerate(zs_l): ax.plot(xs=[xs[i]]*2, ys=[ys[i]]*2, zs=zs_l[i], markersize=0, lw=3, c=cl[i])
This doesn't make any sense. If you're working in three dimensions, then every point can be described in three dimensions, including your starting point. Describing something with only two coordinates in three dimensional space is essentially describing a line; if you only explicitly label the x and y coordinates, then all possible z values are valid- so you end up with a line. What you want to do is make one of the coordinates (x, y, or z) zero.
Best way to plot a 3D matrix in python
I am trying to visualize 3D data. This is a full 3D matrix: each (x,y,z) coordinate has a value, unlike a surface or a collection of individual data vectors. The way I am trying to do this is to plot an opaque cube, where each edge of the cube shows the sum of the data over the orthogonal dimension. Some example data -- basically, a blob centered at (3,5,7): import numpy as np (x,y,z) = np.mgrid[0:10,0:10, 0:10] data = np.exp(-((x-3)**2 + (y-5)**2 + (z-7)**2)**(0.5)) edge_yz = np.sum(data,axis=0) edge_xz = np.sum(data,axis=1) edge_xy = np.sum(data,axis=2) So the idea would be here to generate a 3D plot that showed a cube; each surface of the cube would show the appropriate 2D matrix edge_*. This would be like plotting 3 4-sided polygons at the appropriate 3D positions (or 6 if you did the back sides of the cube as well) except that each polygon is actually a matrix of values to be plotted in color. My best approximation at the moment is to compute larger matrices that contained skewed versions of edge, and concatenate these into a single, larger 2D matrix, and imshow() that larger matrix. Seems pretty clumsy, and does a lot of work that some engine in matplotlib or m3plot or something I'm sure already does. It also only works to view a static image at a single view angle, but that's not something I need to overcome at the moment. Is there a good way to plot these cube edges in a true 3D plot using an existing python tool? Is there a better way to plot a 3D matrix?
Falko's suggestion to use contourf works with a bit of finagling. It's a bit limited since at least my version of contourf has a few bugs where it sometimes renders one of the planes in front of other planes it should be behind, but for now only plotting either the three front or three back sides of the cube will do: import numpy as np import math import matplotlib.pyplot as plot import mpl_toolkits.mplot3d.axes3d as axes3d def cube_marginals(cube, normalize=False): c_fcn = np.mean if normalize else np.sum xy = c_fcn(cube, axis=0) xz = c_fcn(cube, axis=1) yz = c_fcn(cube, axis=2) return(xy,xz,yz) def plotcube(cube,x=None,y=None,z=None,normalize=False,plot_front=False): """Use contourf to plot cube marginals""" (Z,Y,X) = cube.shape (xy,xz,yz) = cube_marginals(cube,normalize=normalize) if x == None: x = np.arange(X) if y == None: y = np.arange(Y) if z == None: z = np.arange(Z) fig = plot.figure() ax = fig.gca(projection='3d') # draw edge marginal surfaces offsets = (Z-1,0,X-1) if plot_front else (0, Y-1, 0) cset = ax.contourf(x[None,:].repeat(Y,axis=0), y[:,None].repeat(X,axis=1), xy, zdir='z', offset=offsets[0], cmap=plot.cm.coolwarm, alpha=0.75) cset = ax.contourf(x[None,:].repeat(Z,axis=0), xz, z[:,None].repeat(X,axis=1), zdir='y', offset=offsets[1], cmap=plot.cm.coolwarm, alpha=0.75) cset = ax.contourf(yz, y[None,:].repeat(Z,axis=0), z[:,None].repeat(Y,axis=1), zdir='x', offset=offsets[2], cmap=plot.cm.coolwarm, alpha=0.75) # draw wire cube to aid visualization ax.plot([0,X-1,X-1,0,0],[0,0,Y-1,Y-1,0],[0,0,0,0,0],'k-') ax.plot([0,X-1,X-1,0,0],[0,0,Y-1,Y-1,0],[Z-1,Z-1,Z-1,Z-1,Z-1],'k-') ax.plot([0,0],[0,0],[0,Z-1],'k-') ax.plot([X-1,X-1],[0,0],[0,Z-1],'k-') ax.plot([X-1,X-1],[Y-1,Y-1],[0,Z-1],'k-') ax.plot([0,0],[Y-1,Y-1],[0,Z-1],'k-') ax.set_xlabel('X') ax.set_ylabel('Y') ax.set_zlabel('Z') plot.show() plot_front=True plot_front=False Other data (not shown)
Take a look at MayaVI. The contour3d() function may be what you want. Here's an answer I gave to a similar question with an example of the code and resulting plot https://stackoverflow.com/a/24784471/3419537
Matplotlib - creating a filled 2d contour plot
I've calculated some values representing a potential as a function of x,y using relaxation method. And I want to display a contour plot with colors (not lines) but, the examples at matplotlib are all fancy 3d plots. I have a ufinal object which is a 2 dimensional numpy array. I did see some nice answers with very nice plots here on SO but I wasn't able to use them properly with my data. I was able to plot a 3d plot using the examples but that's not what I need: fig = plt.figure() ax = fig.gca(projection='3d') X,Y=meshgrid(x,y) surf=ax.plot_surface(X,Y,ufinal,rstride=1,cstride=1,cmap=cm.jet,linewidth=0.1) fig.colorbar(surf,shrink=0.5,aspect=5) As suggested I've tried using the contourf example like so: CS = plt.contourf(X, Y, ufinal,cmap=cm.jet) plt.clabel(CS, inline=1, fontsize=10) plt.title('Simplest default with labels')
As David said, use contourf: import numpy as np import pylab as pl x,y = np.mgrid[:1:1E-3,:1:1E-3] xs = ((x-0.3)**2.) ys = ((y-0.5)**2.) z = np.exp(-1*(xs/0.5+ys/0.3)) pl.contourf(x,y,z,20)
In case anyone is still interested I found a solution for the granularity (a.k.a. nice-looking-ness problem) as part of the solution over here: Symmetrical Log color scale in matplotlib contourf plot
Waterfall plot python?
Is there a python module that will do a waterfall plot like MATLAB does? I googled 'numpy waterfall', 'scipy waterfall', and 'matplotlib waterfall', but did not find anything.
You can do a waterfall in matplotlib using the PolyCollection class. See this specific example to have more details on how to do a waterfall using this class. Also, you might find this blog post useful, since the author shows that you might obtain some 'visual bug' in some specific situation (depending on the view angle chosen). Below is an example of a waterfall made with matplotlib (image from the blog post): (source: austringer.net)
Have a look at mplot3d: # copied from # http://matplotlib.sourceforge.net/mpl_examples/mplot3d/wire3d_demo.py from mpl_toolkits.mplot3d import axes3d import matplotlib.pyplot as plt import numpy as np fig = plt.figure() ax = fig.add_subplot(111, projection='3d') X, Y, Z = axes3d.get_test_data(0.05) ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10) plt.show() I don't know how to get results as nice as Matlab does. If you want more, you may also have a look at MayaVi: http://mayavi.sourceforge.net/
The Wikipedia type of Waterfall chart one can obtain also like this: import numpy as np import pandas as pd def waterfall(series): df = pd.DataFrame({'pos':np.maximum(series,0),'neg':np.minimum(series,0)}) blank = series.cumsum().shift(1).fillna(0) df.plot(kind='bar', stacked=True, bottom=blank, color=['r','b']) step = blank.reset_index(drop=True).repeat(3).shift(-1) step[1::3] = np.nan plt.plot(step.index, step.values,'k') test = pd.Series(-1 + 2 * np.random.rand(10), index=list('abcdefghij')) waterfall(test)
I have generated a function that replicates the matlab waterfall behaviour in matplotlib. That is: It generates the 3D shape as many independent and parallel 2D curves Its color comes from a colormap in the z values I started from two examples in matplotlib documentation: multicolor lines and multiple lines in 3d plot. From these examples, I only saw possible to draw lines whose color varies following a given colormap according to its z value following the example, which is reshaping the input array to draw the line by segments of 2 points and setting the color of the segment to the z mean value between these 2 points. Thus, given the input matrixes n,m matrixes X,Y and Z, the function loops over the smallest dimension between n,m to plot each of the waterfall plot independent lines as a line collection of the 2 points segments as explained above. def waterfall_plot(fig,ax,X,Y,Z,**kwargs): ''' Make a waterfall plot Input: fig,ax : matplotlib figure and axes to populate Z : n,m numpy array. Must be a 2d array even if only one line should be plotted X,Y : n,m array kwargs : kwargs are directly passed to the LineCollection object ''' # Set normalization to the same values for all plots norm = plt.Normalize(Z.min().min(), Z.max().max()) # Check sizes to loop always over the smallest dimension n,m = Z.shape if n>m: X=X.T; Y=Y.T; Z=Z.T m,n = n,m for j in range(n): # reshape the X,Z into pairs points = np.array([X[j,:], Z[j,:]]).T.reshape(-1, 1, 2) segments = np.concatenate([points[:-1], points[1:]], axis=1) # The values used by the colormap are the input to the array parameter lc = LineCollection(segments, cmap='plasma', norm=norm, array=(Z[j,1:]+Z[j,:-1])/2, **kwargs) line = ax.add_collection3d(lc,zs=(Y[j,1:]+Y[j,:-1])/2, zdir='y') # add line to axes fig.colorbar(lc) # add colorbar, as the normalization is the same for all # it doesent matter which of the lc objects we use ax.auto_scale_xyz(X,Y,Z) # set axis limits Therefore, plots looking like matlab waterfall can be easily generated with the same input matrixes as a matplotlib surface plot: import numpy as np; import matplotlib.pyplot as plt from matplotlib.collections import LineCollection from mpl_toolkits.mplot3d import Axes3D # Generate data x = np.linspace(-2,2, 500) y = np.linspace(-2,2, 60) X,Y = np.meshgrid(x,y) Z = np.sin(X**2+Y**2)-.2*X # Generate waterfall plot fig = plt.figure() ax = fig.add_subplot(111, projection='3d') waterfall_plot(fig,ax,X,Y,Z,linewidth=1.5,alpha=0.5) ax.set_xlabel('X'); ax.set_ylabel('Y'); ax.set_zlabel('Z') fig.tight_layout() The function assumes that when generating the meshgrid, the x array is the longest, and by default the lines have fixed y, and its the x coordinate what varies. However, if the size of the y array is longer, the matrixes are transposed, generating the lines with fixed x. Thus, generating the meshgrid with the sizes inverted (len(x)=60 and len(y)=500) yields: To see what are the possibilities of the **kwargs argument, refer to the LineCollection class documantation and to its set_ methods.
Create a stack of polar plots using Matplotlib/Python
I need to generate a stack of 2D polar plots (a 3D cylindrical plot) so that I can view a distorted cylinder. I want to use matplotlib since I already have it installed and want to distribute my code to others who only have matplotlib. For example, say I have a bunch of 2-D arrays. Is there any way I can do this without having to download an external package? Here's my code. #!usr/bin/env python import matplotlib.pyplot as plt import numpy as np x = np.arange(-180.0,190.0,10) theta = (np.pi/180.0 )*x # in radians A0 = 55.0 offset = 60.0 R = [116.225,115.105,114.697,115.008,115.908,117.184,118.61,119.998,121.224,122.216,\ 122.93,123.323,123.343,122.948,122.134,120.963,119.575,118.165,116.941,116.074,115.66\ ,115.706,116.154,116.913,117.894,119.029,120.261,121.518,122.684,123.594,124.059,\ 123.917,123.096,121.661,119.821,117.894,116.225] fig = plt.figure() ax = fig.add_axes([0.1,0.1,0.8,0.8],polar=True) # Polar plot ax.plot(theta,R,lw=2.5) ax.set_rmax(1.5*(A0)+offset) plt.show() I have 10 more similar 2D polar plots and I want to stack them up nicely. If there's any better way to visualize a distorted cylinder in 3D, I'm totally open to suggestions. Any help would be appreciated. Thanks!
If you want to stack polar charts using matplotlib, one approach is to use the Axes3D module. You'll notice that I used polar coordinates first and then converted them back to Cartesian when I was ready to plot them. from numpy import * from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt n = 1000 fig = plt.figure() ax = fig.gca(projection='3d') for k in linspace(0, 5, 5): THETA = linspace(0, 2*pi, n) R = ones(THETA.shape)*cos(THETA*k) # Convert to Cartesian coordinates X = R*cos(THETA) Y = R*sin(THETA) ax.plot(X, Y, k-2) plt.show() If you play with the last argument of ax.plot, it controls the height of each slice. For example, if you want to project all of your data down to a single axis you would use ax.plot(X, Y, 0). For a more exotic example, you can map the height of the data onto a function, say a saddle ax.plot(X, Y, -X**2+Y**2 ). By playing with the colors as well, you could in theory represent multiple 4 dimensional datasets (though I'm not sure how clear this would be). Examples below: