I am trying to create a plot similar like the one below taken from this paper, essentially a 3d plot with two distinct y-axis. Following guidance in this blog, I created a minimal example.
Modules
from mpl_toolkits import mplot3d
import numpy as np
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
Create some data
def f(x, y):
return np.sin(np.sqrt(x ** 2 + y ** 2))
x = np.linspace(-6, 6, 30)
y = np.linspace(-6, 6, 30)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
Z2 = Z*100+100
Plotting
This creates a nice 3d plot, but obviously with only one y-axis. I could not find any advice online on how to get there for python, albeit some for matlab.
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.plot_surface(X, Y, Z2, rstride=1, cstride=1,
cmap='viridis', edgecolor='none')
ax.set_title('surface');
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z');
Code gives:
Reference graph:
Related
I want to plot a stack of heatmaps, contour, or grid computed over time. The plot should like this,
I have tried this:
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.gca(projection='3d')
x = np.linspace(0, 1, 100)
X, Z = np.meshgrid(x, x)
Y = np.sin(X)*np.sin(Z)
levels = np.linspace(-1, 1, 40)
ax.contourf(X, Y, Z, zdir='y')
ax.contourf(X, Y+3, Z, zdir='y')
ax.contourf(X, Y+7, Z, zdir='y')
ax.legend()
ax.view_init(15,155)
plt.show()
For one my plot looks ugly. It also does not look like what I want. I cannot make a grid there, and the 2d surfaces are tilted.
Any help is really appreciated! I am struggling with this.
Related stackoverflow:
[1] Python plot - stacked image slices
[2] Stack of 2D plot
How about making a series of 3d surface plots, with the data your wish to present in contour plotted as facecolor?
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(-5, 5, 0.25)
Z = np.arange(-5, 5, 0.25)
X, Z = np.meshgrid(X, Z)
C = np.random.random(size=40*40*3).reshape((40, 40, 3))
ax.plot_surface(X, np.ones(shape=X.shape)-1, Z, facecolors=C, linewidth=0)
ax.plot_surface(X, np.ones(shape=X.shape), Z, facecolors=C, linewidth=0)
ax.plot_surface(X, np.ones(shape=X.shape)+1, Z, facecolors=C, linewidth=0)
in mplot3d how do I change my axis limits such gets cut off the at the limites. When using ax.set_xlim3d() the graph continuous out the plot.
Consider the graph generated by:
import numpy as np; import matplotlib.pyplot as plt;
from mpl_toolkits import mplot3d
def func(x, y):
return x ** 2 + 0.5*y ** 3
x = np.linspace(-6, 6, 30)
y = np.linspace(-6, 6, 30)
X, Y =np. meshgrid(x, y)
Z = func(X, Y)
plt.clf()
fig = plt.figure(1)
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X, Y, Z, rstride=2, cstride=1,
cmap='viridis', edgecolor='none')
Say I want to cut my z = 50, so the last part disappears. ax.set_zlim3d(-100,50) doesn't do the trick. I have a lot of code written in this form so I prefer not to use the method described here mplot3D fill_between extends over axis limits where the core code of the plots are totally different than mine. TI figure there must be a way to fix my problem my adding a line of code to my existing code.
I'm very new in Python and trying to plot a single curve on a surface.
Here is where I came so far and plotted a surface in s domain:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import cmath
x = np.linspace(-400, 0, 100)
y = np.linspace(-100, 100, 100)
X, Y = np.meshgrid(x,y)
fc=50
wc=2*np.pi*fc
s = X + Y*1j
Z= abs(1/(1+s/wc))
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, Z)
ax.plot(X, Y, Z)
plt.ylabel('Im')
plt.show()
I now need to plot the curve for X = 0 in different color which means the curve on the same surface along the imaginary axis. surf = ax.plot_surface(0, Y, Z) did not work. Does anybody have experience with such plot?
I'm assuming you meant you wanted to plot y=0 instead of x=0 (since x=0 would be pretty boring).
Since you want to plot a single slice of your data, you can't use the meshgrid format (or if you can, it would require some weird indexing that I don't want to figure out).
Here's how I would plot the y=0 slice:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import cmath
x = np.linspace(-400, 0, 100)
y = np.linspace(-100, 100, 100)
X, Y = np.meshgrid(x,y)
fc=50
wc=2*np.pi*fc
s = X + Y*1j
Z= abs(1/(1+s/wc))
fig = plt.figure()
ax = fig.gca(projection='3d')
surf = ax.plot_surface(X, Y, Z)
# create data for y=0
z = abs(1/(1+x/wc))
ax.plot(x,np.zeros(np.shape(x)),z)
plt.ylabel('Im')
plt.show()
Beginner using python and I have a scatter plot (http://i.stack.imgur.com/sQNHM.png). What I want to do is produce a 3D plot that shows a spike in the Z direction at these points and 0 everywhere else.
This is the code I'm currently using:
plt.scatter(X, Y)
plt.show()
X, Y = np.meshgrid(X, Y)
Z = [1] * len(X)
fig = plt.figure()
ax = fig.add_subplot(1,1,1, projection='3d')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.plot_surface(X, Y, Z)
plt.show()
This gives me a strange result (http://i.stack.imgur.com/7fLeT.png) that I'm not sure what to do to fix it.
You probably don't want to use the x and y values from your 2D plot as the input for meshgrid because you want this plot to be defined for all integer values of x and y in your range. The original x and y should define the location of the spikes if I understand your problem correctly. Here's a way to get a 3D plot with spikes of height 100 at defined locations:
from matplotlib import pyplot as plt
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# Create X, Y and Z arrays
x = range(0,250)
y = range(0,250)
X, Y = np.meshgrid(x, y)
Z = np.zeros((250,250))
# Locations of the spikes. These are some made up numbers.
dataX = np.array([25,80,90,145,180])
dataY = np.array([170,32,130,10,88])
# Set spikes to 100
Z[dataX,dataY] = 100
# Plot
fig = plt.figure()
ax = fig.add_subplot(1,1,1, projection='3d')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
ax.plot_surface(X, Y, Z)
plt.show()
I am trying to plot a surface using matplotlib using the code below:
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import axes3d, Axes3D
import pylab as p
vima=0.5
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(0, 16.67, vima)
Y = np.arange(0, 12.5, vima)
X, Y = np.meshgrid(X, Y)
Z = np.sqrt(((1.2*Y+0.6*X)**2+(0.2*Y+1.6*X)**2)/(0.64*Y**2+0.36*X**2))
surf = ax.plot_surface(X, Y, Z,rstride=1, cstride=1, alpha=1,cmap=cm.jet, linewidth=0)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
If you run it you will see a blue surface, but I want to use the whole color range of jet... I know there is a class "matplotlib.colors.Normalize", but I don't know how to use it. Could you please add the necessary code in order to do it?
I realise that the poster's issue has already been resolved, but the question of normalizing the colors was never dealt with. Since I've figured out how I thought I'd just drop this here for anyone else who might need it.
First you create a norm and pass that to the plotting function, I've tried to add this to the OP's code.
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import axes3d, Axes3D
import pylab as p
import matplotlib
vima=0.5
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(0, 16.67, vima)
Y = np.arange(0, 12.5, vima)
X, Y = np.meshgrid(X, Y)
Z = np.sqrt(((1.2*Y+0.6*X)**2+(0.2*Y+1.6*X)**2)/(0.64*Y**2+0.36*X**2))
Z = np.nan_to_num(Z)
# Make the norm
norm = matplotlib.colors.Normalize(vmin = np.min(Z), vmax = np.max(Z), clip = False)
# Plot with the norm
surf = ax.plot_surface(X, Y, Z,rstride=1, cstride=1, norm=norm, alpha=1,cmap=cm.jet, linewidth=0)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
The norm works the same way for the "imshow" command.
As JoshAdel noted in a comment (credit belongs to him), it appears that the surface plot is improperly ranging the colormap when a NaN is in the Z array. A simple work-around is to simply convert the NaN's to zero or very large or very small numbers so that the colormap can be normalized to the z-axis range.
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import axes3d, Axes3D
import pylab as p
vima=0.5
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(0, 16.67, vima)
Y = np.arange(0, 12.5, vima)
X, Y = np.meshgrid(X, Y)
Z = np.sqrt(((1.2*Y+0.6*X)**2+(0.2*Y+1.6*X)**2)/(0.64*Y**2+0.36*X**2))
Z = np.nan_to_num(Z) # added this line
surf = ax.plot_surface(X, Y, Z,rstride=1, cstride=1, alpha=1,cmap=cm.jet, linewidth=0)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
Replying to an old question, I know, but the answers posted were at least in my case somewhat unsatisfactory. For those still stumbling here, I give a solution that worked for me.
Firstly, I did not want use zeros to replace NaNs, as for me they represent points with missing or undefined data. I'd rather not have anything plotted at these points. Secondly, the whole z range of my data was way above zero, so dotting the plot with zeros would result in an ugly and badly scaled plot.
Solution given by leifdenby was quite close, so +1 for that (though as pointed out, the explicit normalisation does not add to the earlier solution). I just dropped the NaN-to-zero replacement, and used the functions nanmin and nanmax instead of min and max in the color scale normalisation. These functions give the min and max of an array but simply ignore all NaNs. The code now reads:
# Added colors to the matplotlib import list
from matplotlib import cm, colors
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import axes3d, Axes3D
import pylab as p
vima=0.5
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(0, 16.67, vima)
Y = np.arange(0, 12.5, vima)
X, Y = np.meshgrid(X, Y)
Z = np.sqrt(((1.2*Y+0.6*X)**2+(0.2*Y+1.6*X)**2)/(0.64*Y**2+0.36*X**2))
# MAIN IDEA: Added normalisation using nanmin and nanmax functions
norm = colors.Normalize(vmin = np.nanmin(Z),
vmax = np.nanmax(Z))
# Added the norm=norm parameter
surf = ax.plot_surface(X, Y, Z,rstride=1, cstride=1, alpha=1, norm=norm, cmap=cm.jet, linewidth=0)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
Running this, I get a correctly scaled plot, with the (0, 0) datapoint missing. This is also the behaviour that I find most preferable, as the limit (x, y) to (0, 0) does not seem to exist for the function in question.
This has been my first contribution to StackOverflow, I hope it was a good one (wink).