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).
Related
I have an issue with smoothing out the mesh representation of my 3D surface with matplotlib. Below, please see my example. I am having a hard time figuring out how to make the plot look nicer/smoother if possible. Thank you for your time in advance!
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.colors import LightSource
import numpy as np
X = [1,1,1,1,1,1,50,50,50,50,50,50]
Y = [3,5,7,8,9,10,3,5,7,8,9,10]
Z = [5.23,3.11,17.54,0.93,40.11,10.15,1.47,14.32,5.46,55.93,40.8,10.2]
x = np.reshape(X, (2, 6))
y = np.reshape(Y, (2, 6))
z = np.reshape(Z, (2, 6))
X, Y = np.meshgrid(x, y)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x, y, z)
ax.set_xlabel('Persistence Length')
ax.set_ylabel('Complexity')
ax.set_zlabel('Relative number of configurational states')
surf = ax.plot_surface(x, y, z, cmap=cm.coolwarm,
linewidth=0, antialiased=False)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
To obtain smooth line/surface you can set antialiased=True on the surface plot. Note that you were plotting two identical surface: in the following example I have eliminated the first.
To obtain a smoother mesh, you probably want to interpolate between your data points. One way to do that is to use griddata from the scipy.interpolate module.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
from scipy.interpolate import griddata
X = [1,1,1,1,1,1,50,50,50,50,50,50]
Y = [3,5,7,8,9,10,3,5,7,8,9,10]
Z = [5.23,3.11,17.54,0.93,40.11,10.15,1.47,14.32,5.46,55.93,40.8,10.2]
points = np.array([X, Y]).T
# create a grid of coordinates between the minimum and
# maximum of your X and Y. 50j indicates 50 discretization
# points between the minimum and maximum.
X_grid, Y_grid = np.mgrid[1:50:50j, 3:10:50j]
# interpolate your values on the grid defined above
Z_grid = griddata(points, Z, (X_grid, Y_grid), method='cubic')
fig = plt.figure(constrained_layout=True)
ax = fig.add_subplot(111, projection='3d')
ax.set_xlabel('Persistence Length')
ax.set_ylabel('Complexity')
ax.set_zlabel('Relative number of configurational states')
surf = ax.plot_surface(X_grid, Y_grid, Z_grid, cmap=cm.coolwarm,
linewidth=0, antialiased=True)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
Here is an example of antialiased=False on the left, vs antialiased=True on the right:
I would like to create a smooth plot in Python. Generally, you can make a plot that looks like the one below:
Source
While this is a nice image, it looks as though it's made out of a mesh of polygons, making it look "coarse." In my own plots I have tried increasing the resolution of my function to no avail. I am trying to achieve the following "smooth" look:
Source
How do I achieve this?
Maybe you were missing rcount and ccount?
# This import registers the 3D projection, but is otherwise unused.
from mpl_toolkits.mplot3d import Axes3D # noqa: F401 unused import
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
# Make data.
X = np.arange(-5, 5, 0.05)
Y = np.arange(-5, 5, 0.05)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm,
linewidth=0, antialiased=False, rcount=200, ccount=200)
# Customize the z axis.
ax.set_zlim(-1.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
# Add a color bar which maps values to colors.
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
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()
I am using mplot3d from the mpl_toolkits library. When displaying the 3D surface on the figure I'm realized the axis were not positioned as I wished they would.
Let me show, I have added to the following screenshot the position of each axis:
Is there a way to change the position of the axes in order to get this result:
Here's the working code:
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
ax = Axes3D(plt.figure())
def f(x,y) :
return -x**2 - y**2
X = np.arange(-1, 1, 0.02)
Y = np.arange(-1, 1, 0.02)
X, Y = np.meshgrid(X, Y)
Z = f(X, Y)
ax.plot_surface(X, Y, Z, alpha=0.5)
# Hide axes ticks
ax.set_xticks([-1,1])
ax.set_yticks([-1,1])
ax.set_zticks([-2,0])
ax.set_yticklabels([-1,1],rotation=-15, va='center', ha='right')
plt.show()
I have tried using xaxis.set_ticks_position('left') statement, but it doesn't work.
No documented methods, but with some hacking ideas from https://stackoverflow.com/a/15048653/1149007 you can.
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = ax = fig.add_subplot(111, projection='3d')
ax.view_init(30, 30)
def f(x,y) :
return -x**2 - y**2
X = np.arange(-1, 1, 0.02)
Y = np.arange(-1, 1, 0.02)
X, Y = np.meshgrid(X, Y)
Z = f(X, Y)
ax.plot_surface(X, Y, Z, alpha=0.5)
# Hide axes ticks
ax.set_xticks([-1,1])
ax.set_yticks([-1,1])
ax.set_zticks([-2,0])
ax.xaxis._axinfo['juggled'] = (0,0,0)
ax.yaxis._axinfo['juggled'] = (1,1,1)
ax.zaxis._axinfo['juggled'] = (2,2,2)
plt.show()
I can no idea of the meaning of the third number in triples. If set zeros nothing changes in the figure. So should look in the code for further tuning.
You can also look at related question Changing position of vertical (z) axis of 3D plot (Matplotlib)? with low level hacking of _PLANES property.
Something changed, code blow doesn't work, all axis hide...
ax.xaxis._axinfo['juggled'] = (0,0,0)
ax.yaxis._axinfo['juggled'] = (1,1,1)
ax.zaxis._axinfo['juggled'] = (2,2,2)
I suggest using the plot function to create a graph
I'm new to python and after installing it I've accomplished to plot my 3d data using matplotlib. Sadly the only thing I don't know how to get done is the color part. My image just shows the surface but doesn't use the color bar at all. Here is my code.
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
from matplotlib.mlab import griddata
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
data = np.genfromtxt('Uizq.txt')
x = data[:,0]
y = data[:,1]
z = data[:,2]
xi = np.linspace(min(x), max(x))
yi = np.linspace(min(y), max(y))
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('U')
X, Y = np.meshgrid(xi, yi)
Z = griddata(x, y, z, xi, yi)
ax.set_zlim3d(np.min(Z), np.max(Z))
surf = ax.plot_surface(X, Y, Z, rstride=2, cstride=2, cmap=cm.jet,
linewidth=0.5, antialiased=False)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
you can obviously see that it is all blue, and I want to relate the color with "U" using the full cm.jet spectrum. This might be a very noob question, so sorry if you rolled your eyes.
Add the line
surf.set_clim([np.min(Z),np.max(Z)])
before you add the color bar.
It seems that the 3D plotting does not take into account the masking, so you are including NaN in the data, which confuses the automatic color limits.