using this code :
data=np.genfromtxt('jpdfomegal2_90.dat')
x_len= len(np.unique(data[:, 0]))
y_len= len(np.unique(data[:, 1]))
#reshape X, Y, and Z into 2D arrays
X = data[:, 0].reshape(x_len, y_len)
Y = data[:, 1].reshape(x_len, y_len)
Z = data[:, 2].reshape(x_len, y_len)
Zmin = np.where(Z > 0, Z, np.inf).min()
Zmax = Z.max()
Z[Z==0] = 0.9 * Zmin
Zlog = np.log10(Z)
fig = plt.figure(figsize=(12,8))
ax = fig.add_subplot(projection='3d')
rc('font',family='palatino')
rc('font',size=14)
ax.set_xlim3d(0,15)
ax.set_zlim3d(np.floor(np.log10(Zmin))-2, np.ceil(np.log10(10))) #,font:'palatino')
ax.zaxis.set_major_formatter(mticker.FuncFormatter(log_tick_formatter))
ax.zaxis.set_major_locator(mticker.MaxNLocator(integer=True))
ax.contour(X, Y, np.log10(Z), 4, lw=0.1, colors="k", linestyles="--", offset=np.floor(np.log10(Zmin))-2)
ax.plot_surface(X, Y, np.log10(Z), cmap="binary", lw=0.1,alpha=0.5)
ax.plot_wireframe(X, Y, np.log10(Z),linewidth=0.5,color='k')
ax.contour(X, Y, np.log10(Z), 4, lw=0.1, colors="k", linestyles="solid")
ax.view_init(elev=17, azim=-60)
for spine in ax.spines.values():
spine.set_visible(False)
plt.tight_layout()
plt.savefig('jpdf_lambda2_90.pdf', bbox_inches='tight')
plt.show()
I obtain a palatino font in x,y axis numerations but not in Z like. in the piucture :
how can obtain the same font (everywhere) ?
and how can I define the z-axis to obtain sub-thicks like in the classical logarithmic scale? thanks
Related
I want to shade the surface and the contours of a specific function based on some constraint in the domain of the function. So far I have the following and I want to improve it.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import LinearLocator, FormatStrFormatter
plt.figure(figsize=(8, 6))
plt.axes(projection="3d")
xdata = np.linspace(-3, 3, 20000)
ydata = np.linspace(-3, 3, 20000)
X, Y = np.meshgrid(xdata, ydata)
Z1 = X ** 2 + Y ** 2
Z2 = Z1.copy()
Z3 = Z1.copy()
Z1[np.multiply(X, Y) > 3] = np.nan
Z2[np.multiply(X, Y) <= 3] = np.nan
Z3[np.multiply(X, Y) == 3] = np.nan
ax3d = plt.axes(projection='3d')
ax3d.plot_surface(X, Y, Z1, cmap='Greys', antialiased=True, vmin=-np.nanmin(Z1), vmax=np.nanmax(Z1))
ax3d.plot_surface(X, Y, Z2, cmap='YlGnBu', antialiased=True, vmin=-np.nanmin(Z2), vmax=np.nanmax(Z2))
ax3d.contourf(X, Y, Z1, zdir='z', offset=0, cmap='Greys')
ax3d.contourf(X, Y, Z2, zdir='z', offset=0, cmap='Greys')
ax3d.set_title('Surface Plot in Matplotlib')
ax3d.set_xlabel('X')
ax3d.set_ylabel('Y')
ax3d.set_zlabel('Z')
plt.show()
Could you please someone help to solve the following problems:
The surface is over-imposed by the contour surface.
There are some gaps in the surface.
The contours of the two constraints are not continuous.
Is it possible to plot a line in the border of the two surfaces and contours?
Any help is highly appreciated.
The code below makes the following changes:
creating a custom colormap combining the two existing colormaps
using a TwoSlopeNorm to have the separation at z=3
setting antialiased=False (otherwise matplotlib creates a plot of antialiased lines instead of polygons)
xdata and ydata with 300 steps
using rstride=1, cstride=1 so every x and every y will be considered; this makes the surface smoother (but takes more time)
calling plt.axes(...) only once to prevent a dummy subplot
calling contourf before plot_surface; due to the painter's algorithm, matplotlib only minimally supports 3D overlaps
import matplotlib.pyplot as plt
from matplotlib.colors import TwoSlopeNorm, ListedColormap
import numpy as np
xdata = np.linspace(-3, 3, 300)
ydata = np.linspace(-3, 3, 300)
X, Y = np.meshgrid(xdata, ydata)
Z1 = X ** 2 + Y ** 2
cmap1 = plt.get_cmap('Greys')
cmap2 = plt.get_cmap('YlGnBu')
cmap = ListedColormap(np.r_[cmap1(np.linspace(0, 1, 128)), cmap2(np.linspace(0, 1, 128))])
norm = TwoSlopeNorm(vmin=np.nanmin(Z1), vmax=np.nanmax(Z1), vcenter=3)
plt.figure(figsize=(8, 6))
ax3d = plt.axes(projection='3d')
ax3d.contourf(X, Y, Z1, zdir='z', offset=0, cmap=cmap, norm=norm)
ax3d.plot_surface(X, Y, Z1, cmap=cmap, antialiased=False, norm=norm, rstride=1, cstride=1)
ax3d.set_title('Surface Plot in Matplotlib')
ax3d.set_xlabel('X')
ax3d.set_ylabel('Y')
ax3d.set_zlabel('Z')
plt.show()
xdata = np.linspace(-3, 3, 1000)
ydata = np.linspace(-3, 3, 1000)
X, Y = np.meshgrid(xdata, ydata)
Z1 = X ** 2 + Y ** 2
Z2 = Z1.copy()
Z3 = Z1.copy()
Z2[np.multiply(X, Y) <= 3] = np.nan
Z3[np.multiply(X, Y) == 3] = np.nan
plt.figure(figsize=(8, 6))
ax3d = plt.axes(projection='3d')
ax3d.contourf(X, Y, Z1, zdir='z', offset=0, cmap='Greys')
ax3d.contourf(X, Y, Z2, zdir='z', offset=0, cmap='YlGnBu')
ax3d.plot_surface(X, Y, Z1, cmap='Greys', antialiased=True, vmin=-np.nanmin(Z1), vmax=np.nanmax(Z1), alpha=.5)
ax3d.plot_surface(X, Y, Z2, cmap='YlGnBu', antialiased=True, vmin=-np.nanmin(Z2), vmax=np.nanmax(Z2), alpha=.5)
ax3d.set_title('Surface Plot in Matplotlib')
ax3d.set_xlabel('X')
ax3d.set_ylabel('Y')
ax3d.set_zlabel('Z')
plt.show()
I am trying to plot some points on a surface. The result I want to get is (the plot at the right):
I tried the following code (x_k contains the coordinates of the points I want to plot):
f = lambda x : x[0]**2+x[1]**3+3*x[0]*x[1]
x_axis = [x_k[i][0] for i in range(0, len(x_k))]
y_axis = [x_k[i][1] for i in range(0, len(x_k))]
z_axis = [f(x_k[i]) for i in range(0, len(x_k))]
x = np.linspace(-6, 6, 30)
y = np.linspace(-6, 6, 30)
X, Y = np.meshgrid(x, y)
Z = f(np.array([X, Y]))
ax = plt.axes(projection='3d')
ax.plot_surface(X, Y, Z,alpha=0.8,cmap='viridis',edgecolor='white',linewidth=0.3)
ax.scatter(x_axis, y_axis, z_axis, zdir='z',marker='.', s=10, c='black', depthshade=False)
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
ax.set_aspect("equal")
plt.tight_layout()
plt.show()
And I get this:
See that points are transparent because of the surface...
I want them to be black, so as to see them better. How could I do that? Thank you!
I'm trying to visualise a decision boundary for a LogisticRegression() classifier.
But getting a ValueError: X has 2 features per sample; expecting 24 when calling plot_boundary(LogisticRegression(), X, y, "Log Reg")
I've checked the X.shape and it is (14635, 24)
What could be wrong with my function?
def plot_boundary(clf, X, y, plot_title):
xx, yy = np.meshgrid(np.linspace(-3, 3, 50), np.linspace(-3, 3, 50))
clf.fit(X, y)
# plot the decision function for each datapoint on the grid
Z = clf.predict_proba(np.vstack((xx.ravel(), yy.ravel())).T)[:, 1]
Z = Z.reshape(xx.shape)
image = plt.imshow(Z, interpolation='nearest', extent=(xx.min(), xx.max(),
yy.min(), yy.max()), aspect='auto', origin='lower',
cmap=plt.cm.PuOr_r)
contours = plt.contour(xx, yy, Z, levels=[0], linewidths=2, linetypes='--')
plt.scatter(X[:, 0], X[:, 1], s=30, c=y, cmap=plt.cm.Paired)
plt.xticks(())
plt.yticks(())
plt.xlabel(r'$x_1$')
plt.ylabel(r'$x_2$')
plt.axis([-3, 3, -3, 3])
plt.colorbar(image)
plt.title(plot_title, fontsize=12)
My goal is to plot the probability distribution functions (pdfs) of two classes. Each class has Gaussian likelihood and equivalent covariance matrices, but different mean vectors. I want both pdfs on the same plane in the z-axis, with the x and y axes containing the projections of the pdfs.
The following code (mostly borrowed from here) plots one of the pdfs:
# Our 2-dimensional distribution will be over variables X and Y
N = 100
X = np.linspace(-10, 15, N)
Y = np.linspace(-10, 15, N)
X, Y = np.meshgrid(X, Y)
# Mean vector and covariance matrix
mu1 = np.array([8, 2])
mu2 = np.array([2,8])
Sigma1 = Sigma2 = np.array([[4.1,0],[0,2.8]])
# Pack X and Y into a single 3-dimensional array
pos = np.empty(X.shape + (2,))
pos[:, :, 0] = X
pos[:, :, 1] = Y
F1 = multivariate_normal(mu1, Sigma1)
F2 = multivariate_normal(mu2, Sigma2)
Z1 = F1.pdf(pos)
Z2 = F2.pdf(pos)
# Create a surface plot and projected filled contour plot under it.
fig1 = plt.figure(figsize=[10,10])
ax1 = fig1.gca(projection='3d')
ax1.plot_surface(X, Y, Z1, rstride=3, cstride=3, linewidth=1,
antialiased=True,cmap=cm.inferno)
cset = ax1.contourf(X, Y, Z1, zdir='z', offset=-0.15, cmap=cm.inferno)
# Adjust the limits, ticks and view angle
ax1.set_zlim(-0.15,0.2)
ax1.set_zticks(np.linspace(0,0.2,5))
ax1.view_init(27, -21)
plt.show()
This is the plot that results from the above code: plot_surfaces for plotting bivariate pdf. However, I need to plot both Z1 and Z2 on the same plane. If I try creating two plots, they overlap and the Z2 pdf cannot be seen. Tweaking the code slightly, I get roughly what I want:
ax1 = fig1.gca(projection='3d')
ax2 = fig1.gca(projection='3d')
ax1.plot_wireframe(X, Y, Z1, rstride=3, cstride=3, linewidth=1,
antialiased=True,cmap=cm.inferno)
ax2.plot_wireframe(X,Y,Z2,rstride=3, cstride=3, linewidth=1,
antialiased=True,cmap=cm.inferno)
the resulting plot of which can be found here: wireframe method to plot 2 bivariate pdfs. But these plots are still overlapping. How can I get around this issue? I want the result to be styled as the first plot, with the surface_plot method and projections on the x-y plane.
You can play with alpha:
ax1.plot_surface(X, Y, Z1, rstride=3, cstride=3, linewidth=1,
antialiased=True,cmap=cm.inferno, alpha = 0.5)
ax1.plot_surface(X, Y, Z2, rstride=3, cstride=3, linewidth=1,
antialiased=True,cmap=cm.inferno, alpha = 1)
cset = ax1.contourf(X, Y, Z1, zdir='z', offset=-0.15, cmap=cm.inferno, alpha=1)
cset = ax1.contourf(X, Y, Z2, zdir='z', offset=-0.15, cmap=cm.inferno, alpha=0.5)
But in some cases you can get the same results by just summing your pdf's
ax1.plot_surface(X, Y, Z1 + Z2, rstride=3, cstride=3, linewidth=1, antialiased=True,cmap=cm.inferno)
cset = ax1.contourf(X, Y, Z1 + Z2, zdir='z', offset=-0.15, cmap=cm.inferno)
I am trying to 'paint' the faces of a cube with a contourf function using Python Matplotlib. Is this possible?
This is similar idea to what was done here but obviously I cannot use patches. Similarly, I don't think I can use add_collection3d like this as it only supports PolyCollection, LineColleciton and PatchCollection.
I have been trying to use contourf on a fig.gca(projection='3d'). Toy example below.
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
plt.close('all')
fig = plt.figure()
ax = fig.gca(projection='3d')
############################################
# plotting the 'top' layer works okay... #
############################################
X = np.linspace(-5, 5, 43)
Y = np.linspace(-5, 5, 28)
X, Y = np.meshgrid(X, Y)
varone=np.random.rand(75,28,43)
Z=varone[0,:,:]
cset = ax.contourf(X, Y, Z, zdir='z', offset=1,
levels=np.linspace(np.min(Z),np.max(Z),30),cmap='jet')
#see [1]
plt.show()
#################################################
# but now trying to plot a vertical slice.... #
#################################################
plt.close('all')
fig = plt.figure()
ax = fig.gca(projection='3d')
Z=varone[::-1,:,-1]
X = np.linspace(-5, 5, 28)
Y = np.linspace(-5, 5, 75)
X, Y = np.meshgrid(X, Y)
#this 'projection' doesn't result in what I want, I really just want to rotate it
cset = ax.contourf(X, Y, Z, offset=5,zdir='x',
levels=np.linspace(np.min(Z),np.max(Z),30),cmap='jet')
#here's what it should look like....
ax=fig.add_subplot(1, 2,1)
cs1=ax.contourf(X,Y,Z,levels=np.linspace(np.min(Z),np.max(Z),30),cmap='jet')
#see [2]
plt.show()
1 From the example, the top surface comes easily:
2 But I'm not sure how to do the sides. Left side of this plot is what the section should look like (but rotated)...
Open to other python approaches. The data I'm actually plotting are geophysical netcdf files.
You have to assign the data to the right axis. The zig-zag results from the fact that now you are at x = const and have your oscillation in the z-direction (from the random data, which is generated between 0 and 1).
If you you assign the matrixes differently in your example, you end up with the desired result:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
plt.close('all')
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.linspace(-5, 5, 43)
Y = np.linspace(-5, 5, 28)
X, Y = np.meshgrid(X, Y)
varone=np.random.rand(75,28,43) * 5.0 - 10.0
Z=varone[0,:,:]
cset = [[],[],[]]
# this is the example that worked for you:
cset[0] = ax.contourf(X, Y, Z, zdir='z', offset=5,
levels=np.linspace(np.min(Z),np.max(Z),30),cmap='jet')
# now, for the x-constant face, assign the contour to the x-plot-variable:
cset[1] = ax.contourf(Z, Y, X, zdir='x', offset=5,
levels=np.linspace(np.min(Z),np.max(Z),30),cmap='jet')
# likewise, for the y-constant face, assign the contour to the y-plot-variable:
cset[2] = ax.contourf(X, Z, Y, zdir='y', offset=-5,
levels=np.linspace(np.min(Z),np.max(Z),30),cmap='jet')
# setting 3D-axis-limits:
ax.set_xlim3d(-5,5)
ax.set_ylim3d(-5,5)
ax.set_zlim3d(-5,5)
plt.show()
The result looks like this:
The answer given below is not fully satisfying. Indeed, planes in x, y and z direction reproduce the same field.
Hereafter, a function that allows to represent the correct field in each of the planes.
import numpy as np
import matplotlib.pyplot as plt
def plot_cube_faces(arr, ax):
"""
External faces representation of a 3D array with matplotlib
Parameters
----------
arr: numpy.ndarray()
3D array to handle
ax: Axes3D object
Axis to work with
"""
x0 = np.arange(arr.shape[0])
y0 = np.arange(arr.shape[1])
z0 = np.arange(arr.shape[2])
x, y, z = np.meshgrid(x0, y0, z0)
xmax, ymax, zmax = max(x0), max(y0), max(z0)
vmin, vmax = np.min(arr), np.max(arr)
ax.contourf(x[:, :, 0], y[:, :, 0], arr[:, :, -1].T,
zdir='z', offset=zmax, vmin=vmin, vmax=vmax)
ax.contourf(x[0, :, :].T, arr[:, 0, :].T, z[0, :, :].T,
zdir='y', offset=0, vmin=vmin, vmax=vmax)
ax.contourf(arr[-1, :, :].T, y[:, 0, :].T, z[:, 0, :].T,
zdir='x', offset=xmax, vmin=vmin, vmax=vmax)
x0 = np.arange(30)
y0 = np.arange(20)
z0 = np.arange(10)
x, y, z = np.meshgrid(x0, y0, z0)
arr = (x + y + z) // 10
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
plot_cube_faces(arr, ax)
plt.show()