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:
Related
This is likely a very basic question, but my python knowledge is somewhat limited and I've had some trouble deciphering other questions+answers, so I'm hoping someone here can help...
I have a set of 1-D data points (the number of molecules at each point along a radius, essentially) that I want to plot as a 2-D radially symmetrical image. I want to rotate it around an origin (0,0).
Basically, I have something like
but I want something like
I've seen this done relatively easily with functions or defined vectors (i.e. with scipy's interpolate module), but I don't have a function or randomly-generated data -- I have a list. I know there must be a simple way to do this, but I'd really appreciate it if someone could point me in the right direction!
I've included a really really small example dataset (plotted as a line in log scale) for people to play with if they feel so inclined:
import numpy as np
import matplotlib.pyplot as plt
rad25 = np.array([25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1])
O_rad25 = np.array([1.01E+15,3.00E+14,1.20E+14,5.63E+13,2.90E+13,1.59E+13,9.21E+12,5.53E+12,3.43E+12,2.18E+12,1.42E+12,9.44E+11,6.38E+11,4.37E+11,3.03E+11,2.13E+11,1.51E+11,1.08E+11,7.77E+10,5.60E+10,4.02E+10,2.84E+10,1.94E+10,1.20E+10,5.78E+09])
plt.plot(rad25,O_rad25)
plt.yscale('log')
plt.xlabel('Radius (distance from center in um)')
plt.ylabel('Number of molecules')
plt.show()
You need to create an array of values from 0 - 360 degrees, and then create a meshgrid from this array and your array of values. This can then be plotted on a radially projected subplot.
Something like this, but with your data of cause:
import numpy as np
import matplotlib.pyplot as plt
# Swap out with your data
radialVals = np.linspace(0,1)
azm = np.linspace(0, 2 * np.pi)
r, th = np.meshgrid(radialVals, azm)
z = (r ** 2.0) / 4.0
plt.subplot(projection="polar")
plt.pcolormesh(th, r, z)
plt.plot(azm, r, ls='none')
plt.grid()
plt.show()
I borrowed some from Jon's answer (his is very nearly correct), and added a colorbar to illustrate, but this should get you what you want. Since you know your radius and that your data has radial symmetry, a polar plot is the natural choice.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import colors
fig, ax = plt.subplots(subplot_kw=dict(projection='polar'))
azm = np.linspace(0, 2 * np.pi)
r, th = np.meshgrid(rad25, azm)
z = np.tile(O_rad25, (r.shape[0], 1))
plt.pcolormesh(th, r, z, norm=colors.LogNorm(O_rad25.min(), O_rad25.max()))
plt.colorbar(label='Number of Molecules')
I tried looking this up a lot and there are lot of information on specific examples but they are too specific to understand.
How do I put data in a Numpy N-D Matrix to a 3D graph. please refer below example
import numpy as np
X =20
Y = 20
Z = 2
sample = np.zeros(((X,Y,Z)))
sample[1][2][2]=45
sample[1][3][0]=52
sample[1][8][1]=42
sample[1][15][1]=30
sample[1][19][2]=15
I Want to use values on X,Y,Z positions to be on a 3D graph (plot).
Thanks in advance
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
# Define size of data
P= 25
X = 70
Y = 25
Z = 3
# Create meshgrid
x,y = np.meshgrid(np.arange(X),np.arange(Y))
# Create some random data (your example didn't work)
sample = np.random.randn((((P,X,Y,Z))))
# Create figure
fig=plt.figure()
ax=fig.add_subplot(111,projection='3d')
fig.show()
# Define colors
colors=['b','r','g']
# Plot for each entry of in Z
for i in range(Z):
ax.plot_wireframe(x, y, sample[:,:,:,i],color=colors[i])
plt.draw()
plt.show()
But I only want to draw X,Y,Z only.
when I used above code python throws me lots of errors like ValueError: too many values to unpack
Are you looking for something like this?
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
# Define size of data
X = 20
Y = 20
Z = 3
# Create meshgrid
x,y = np.meshgrid(np.arange(X),np.arange(Y))
# Create some random data (your example didn't work)
sample = np.random.randn(X,Y,Z)
# Create figure
fig=plt.figure()
ax=fig.add_subplot(111,projection='3d')
fig.show()
# Define colors
colors=['b','r','g']
# Plot for each entry of in Z
for i in range(Z):
ax.plot_wireframe(x, y, sample[:,:,i],color=colors[i])
plt.draw()
plt.show()
which would you give
There are plenty of other ways to display 3D data in matplotlib, see also here. However, you are always limited to 3 dimensions (or 4, if you do a 3D scatter plot where color encodes the 4th dimension). So you need to make a decision which dimensions you want to show or if you can summarize them somehow.
I have got something it may work for you. To understand it I explain the process I go briefly. I have connected 4x4x4 = 64 point masses to each other and created a cube with dampers and springs and inner friction. I solved the kinematic and mechanical behaviour using numpy and then I need to visualise the cube all I have is X,Y,Z points for each time step of each mass.
What I have is 4x4x4 XYZ points of a cube for each time tn:
Here how it goes :
import matplotlib.pyplot as plt
zeroPoint=points[50] # at time step 50 elastic cube in space
surf0x=zeroPoint[0,:,:,0]
surf0y=zeroPoint[0,:,:,1]
surf0z=zeroPoint[0,:,:,2]
surf1x=zeroPoint[:,0,:,0]
surf1y=zeroPoint[:,0,:,1]
surf1z=zeroPoint[:,0,:,2]
surf2x=zeroPoint[:,:,0,0]
surf2y=zeroPoint[:,:,0,1]
surf2z=zeroPoint[:,:,0,2]
surf3x=zeroPoint[nmx-1,:,:,0]
surf3y=zeroPoint[nmx-1,:,:,1]
surf3z=zeroPoint[nmx-1,:,:,2]
surf4x=zeroPoint[:,nmy-1,:,0]
surf4y=zeroPoint[:,nmy-1,:,1]
surf4z=zeroPoint[:,nmy-1,:,2]
surf5x=zeroPoint[:,:,nmz-1,0]
surf5y=zeroPoint[:,:,nmz-1,1]
surf5z=zeroPoint[:,:,nmz-1,2]
fig = plt.figure(figsize=(10,10))
wf = plt.axes(projection ='3d')
wf.set_xlim(-0.5,2)
wf.set_ylim(-0.5,2)
wf.set_zlim(-0.5,2)
wf.plot_wireframe(surf0x, surf0y, surf0z, color ='green')
wf.plot_wireframe(surf1x, surf1y, surf1z, color ='red')
wf.plot_wireframe(surf2x, surf2y, surf2z, color ='blue')
wf.plot_wireframe(surf3x, surf3y, surf3z, color ='black')
wf.plot_wireframe(surf4x, surf4y, surf4z, color ='purple')
wf.plot_wireframe(surf5x, surf5y, surf5z, color ='orange')
# displaying the visualization
wf.set_title('Its a Cube :) ')
pyplot.show()
at time step 190 same cube (animation is 60 FPS) :
The trick is as you see you need to create surfaces from points before you go. You dont even need np.meshgrid to do that. People does it for parametric z values calculation. If you have all points you dont need it.
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.
I'm trying to do a heat map over a shape file in python. I need to make quite a few of these so don't want to read in the .shp every time.
Instead, I thought I could create a lineCollection instance of the map boundaries and overlay the two images. Problem is - I can't seem to get the two to line up correctly.
Here is the code, where linecol is the lineCollection object.
fig = plt.figure()
ax = fig.add_subplot(111)
ax.contourf(xi,yi,zi)
ax.add_collection(linecol, autolim = False)
plt.show()
Is there an easy way to fix the limits of linecol to match those of the other plot? I've had a play with set_xlim and transforms.Bbox, but can't seem to manage it.
Thank you very much for your help!
Transforms are tricky because of the various coordinate systems involved. See http://matplotlib.sourceforge.net/users/transforms_tutorial.html.
I managed to scale a LineCollection to the appropriate size like this. The key was to realize that I needed to add + ax.transData to the new transform I set on the LineCollection. (When you don't set any transform on an artist object, ax.transData is the default. It converts data coordinates into display coordinates.)
from matplotlib import cm
import matplotlib.pyplot as plt
import matplotlib.collections as mc
import matplotlib.transforms as tx
import numpy as np
fig = plt.figure()
# Heat map spans 1 x 1.
ax = fig.add_subplot(111)
xs = ys = np.arange(0, 1.01, 0.01)
zs = np.random.random((101,101))
ax.contourf(xs, ys, zs, cmap=cm.autumn)
lines = mc.LineCollection([[(5,1), (9,5), (5,9), (1,5), (5,1)]])
# Shape spans 10 x 10. Resize it to 1 x 1 before applying the transform from
# data coords to display coords.
trans = tx.Affine2D().scale(0.1) + ax.transData
lines.set_transform(trans)
ax.add_collection(lines)
plt.show()
(Output here: http://i.stack.imgur.com/hDNN8.png Not enough reputation to post inline.)
It should be easy to modify this if you need the shape translated or scaled unequally on x and y.
effectively I have a large 1D array of heights. As a small example consider:
u=array([0,1,2,1,0,2,4,6,4,2,1])
and a 1D array, the same size as u, of radial values which the heights correspond to, e.g.:
r=array([0,1,2,3,4,5,6,7,8,9,10])
Obviously plotting these with:
pylab.plot(r,u)
gives a nice 2D plot.
How can one sweep this out around 360 degrees, to give a 3D contour/surface plot?
If you can imagine it should look like a series of concentric, circular ridges, like for the wavefunction of an atom.
any help would be much appreciated!
You're better off with something more 3D oriented than matplotlib, in this case...
Here's a quick example using mayavi:
from enthought.mayavi import mlab
import numpy as np
# Generate some random data along a straight line in the x-direction
num = 100
x = np.arange(num)
y, z = np.ones(num), np.ones(num)
s = np.cumsum(np.random.random(num) - 0.5)
# Plot using mayavi's mlab api
fig = mlab.figure()
# First we need to make a line source from our data
line = mlab.pipeline.line_source(x,y,z,s)
# Then we apply the "tube" filter to it, and vary the radius by "s"
tube = mlab.pipeline.tube(line, tube_sides=20, tube_radius=1.0)
tube.filter.vary_radius = 'vary_radius_by_scalar'
# Now we display the tube as a surface
mlab.pipeline.surface(tube)
# And finally visualize the result
mlab.show()
#!/usr/bin/python
from mpl_toolkits.mplot3d import Axes3D
import matplotlib
import numpy as np
from scipy.interpolate import interp1d
from matplotlib import cm
from matplotlib import pyplot as plt
step = 0.04
maxval = 1.0
fig = plt.figure()
ax = Axes3D(fig)
u=np.array([0,1,2,1,0,2,4,6,4,2,1])
r=np.array([0,1,2,3,4,5,6,7,8,9,10])
f=interp1d(r,u)
# walk along the circle
p = np.linspace(0,2*np.pi,50)
R,P = np.meshgrid(r,p)
# transform them to cartesian system
X,Y = R*np.cos(P),R*np.sin(P)
Z=f(R)
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.jet)
ax.set_xticks([])
plt.show()