Surface plot not graduating colours - python

I'm working with python 3.4. I'm trying to plot a simple surface from a 2D array (11x13), but the colour map is not graduating properly. There's only a small range, but the colour bar looks reasonable while the plot just has stripes. Doesn't seem to matter what colourmap I use I get an equivalent result.
From another post, I tried using the rstride & cstride parameters, but that just turned the entire surface pink (with this map).
Any suggestions? I adapted the code from one of the mplot3d tutorial examples - basically just replaced X, Y, Z and the axis limits and tried different colour maps.
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(0,550,50)
Y = np.arange(-12,1,1)
X, Y = np.meshgrid(X, Y)
# Array calculated with a process in excel...I'll learn to python it later.
Z = np.array([[0,0,0,0,0,0,0,0,9.318546297,9.32278014,9.324432509],
[0,0,0,0,0,0,9.270465014,9.281098393,9.287418343,9.291620087,9.293257704],
[0,0,0,9.183895053,9.20724459,9.226419172,9.239450746,9.25002849,9.257383697,9.261920422,9.263632585],
[9,9.086332608,9.126397936,9.153091476,9.176369804,9.195477721,9.210108238,9.22128034,9.229169896,9.233975347,9.235758722],
[9,9.0556775,9.095671635,9.125345711,9.149164982,9.168378335,9.183464661,9.194945002,9.203079816,9.208024884,9.209844315],
[9,9.040637647,9.075058225,9.10310915,9.126085,9.144796641,9.15970021,9.171125191,9.179264934,9.184223248,9.186039585],
[9,9.031750626,9.060617885,9.085619791,9.106813693,9.12444679,9.13872781,9.149807723,9.157769849,9.162644172,9.164426795],
[9,9.025687536,9.049859786,9.071632721,9.09067842,9.106911657,9.120316269,9.130877982,9.138559174,9.14329971,9.145034568],
[9,9.021085904,9.041333766,9.060092969,9.076965929,9.091711302,9.104158214,9.114155647,9.121547513,9.126169075,9.127867857],
[9,9.017274923,9.034147314,9.050188629,9.065030255,9.07836292,9.08991453,9.099425441,9.106628029,9.111235402,9.112955947],
[9,9.01382656,9.027663537,9.041265696,9.054295453,9.066400007,9.077233581,9.086451516,9.09368943,9.098527457,9.100440892],
[9,9.010336943,9.021310255,9.032733661,9.044224745,9.055367181,9.065750219,9.074968252,9.082599449,9.088144367,9.090799207],
[9,9.00619218,9.014431776,9.023994936,9.034293996,9.04481216,9.055077078,9.064646767,9.073106766,9.080108346,9.085588416]])
# replacing the zeroes with NaN
for i in range(np.shape(Z)[0]):
for j in range(np.shape(Z)[1]):
if Z[i,j] == 0:
Z[i,j] = float('nan')
# Plot the surface.
surf = ax.plot_surface(X, Y, Z, cmap=cm.cool,
linewidth=0, antialiased=False)
# Customize the z axis.
ax.set_zlim(9, 9.5)
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()

For anyone else that finds this thread with a similar question:
After reading a question about handling of NaN cells, I tried modifying the ax.plot_surface command with nanmin and nanmax arguments and that sorted out the colour stretching:
surf = ax.plot_surface(X, Y, Z, cmap=cm.coolwarm, linewidth=0, antialiased=False, rstride=1, cstride=1,
vmin=np.nanmin(Z), vmax=np.nanmax(Z))

Related

3D plot in python, space between x-ticks and the label

Consider the following picture. How do I create distance between the x-axis numbering and the label?
The plot is created following the steps.
The structure of the code is more or less like this:
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d;
ax = plt.axes(projection='3d')
ax.plot_surface(X, Y, Z, rstride=1, cstride=1,
cmap='viridis')
ax.set_title('surface');
You can specify a value of your choice to the labelpad argument as following. The same can be done for y and z axis labels as well.
ax.set_xlabel('xxxxxxxxx', labelpad=10)

Set 3d plot linewidth in matplotlib 2.x

I'm using a newer version of matplotlib and the argument that sets the linewidth was removed. They seem to have changed it so I set it in Collections object, but I can't find a way of doing this.
I tried their example with a different linewidth:
from mpl_toolkits.mplot3d import Axes3D
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.25)
Y = np.arange(-5, 5, 0.25)
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=10, antialiased=False)
# 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()
But as the figure shows, it doesn't add lines to my surface.
What is the new method for setting linewidths?
Thanks!
The linewidth can of course only take effect if there is actually a line to be shown. So one would need to specify the color of the lines to show in order to see them.
surf = ax.plot_surface(X, Y, Z, cmap="RdYlGn", linewidth=2, edgecolor="limegreen")

Matplotlib 3D: axis bounds always too large (doesn't set lims correctly)

I have an issue (bug?) with 3D plotting in matplotlib that I wonder if anyone may be able to help with please?
As can be seen by the matplotlib gallery example plots (e.g. from: https://matplotlib.org/examples/mplot3d/surface3d_demo.html), when the user sets the axis limits manually, and a tick is placed at the limit, the axis bound is extended a little:
I want the axis to show the 1.01 tick label, but this should be the vertex of the cube (with no grey space / black axis line above it). Is this possible please?
As a separate, more minor request, I'd then like to draw a solid black line around the edge of the grid to make it stand out. This is less important than fixing the bounds, however.
For reference, here is the code that makes the above plot:
from mpl_toolkits.mplot3d import Axes3D
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.25)
Y = np.arange(-5, 5, 0.25)
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)
# 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()

Smooth surface Plot with Pyplot

My question is almost similar to this on:
smoothing surface plot from matrix
only that my toolset is matplotlib and numpy (so far).
I have sucessfully generated a X, Y and Z-grid to plot
with
fig = plt.figure(figsize=(12,12))
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(X, Y, Z, cmap='summer', rstride=1, cstride=1, alpa=None)
However, as the values are quite jumpy, it looks terribly.
I'd like to smoothen things up, make at least the vertices connected, or look like that.
My data is generated like that:
I have a function
svOfMatrix(x, y)
which produces a matrix in dependence on x, calculates its y-th power, selects a subset of columns and rows, and calculates the maximum singular value.
So, Z[x,y] is svOfMatrix(x, y)
As this calculation is quite expensive, I don't want to make the steps for x too small, and Y is bound to be integer
Further, even for very small steps, there might be quite some changes, I don't want see. So I'd like to interpolate it somehow.
I found
http://docs.scipy.org/doc/scipy-0.14.0/reference/tutorial/interpolate.html
but I don't get it to work.
From the link you suggested, the example here is probably closest to what you want. You can use the example with your values,
import numpy as np
from scipy import interpolate
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d, Axes3D
X, Y = np.mgrid[-1:1:20j, -1:1:20j]
Z = (X+Y) * np.exp(-6.0*(X*X+Y*Y)) + np.random.rand(X.shape[0])
xnew, ynew = np.mgrid[-1:1:80j, -1:1:80j]
tck = interpolate.bisplrep(X, Y, Z, s=0)
znew = interpolate.bisplev(xnew[:,0], ynew[0,:], tck)
fig = plt.figure(figsize=(12,12))
ax = fig.gca(projection='3d')
ax.plot_surface(X, Y, Z, cmap='summer', rstride=1, cstride=1, alpha=None)
plt.show()
fig = plt.figure(figsize=(12,12))
ax = fig.gca(projection='3d')
ax.plot_surface(xnew, ynew, znew, cmap='summer', rstride=1, cstride=1, alpha=None, antialiased=True)
plt.show()
Also, antialiased=True may make it look better but I think is on by default. The first plot looks like this,
and the smoothed plot like this,
The problem with your the low frequency noise in your data is that it will be difficult to define a grid fine enough to resolve. You can adjust the level of smoothing with the s argument to interpolate.bisplrep or perhaps coarse grain/filter your data to leave only major trends (e.g. using scipy.ndimage.interpolation.zoom if you have regular gridded data). Alternatively, consider a different type of plot such as pcolormesh as the data is essentially 2D.
Simply put the data_frame into this function. You'll get a proper smoothen surface plot. Incase you face any error, just choose only those features from data_frame which are numerical.
'data_frame = data_frame.select_dtypes(include='number')'
from scipy import interpolate
from mpl_toolkits.mplot3d import axes3d, Axes3D
def surface(data_frame, title=None, title_x=0.5, title_y=0.9):
X, Y = np.mgrid[-10:10:complex(0,data_frame.shape[0]),
-10:10:complex(0,data_frame.shape[1])]
Z = data_frame.values
xnew, ynew = np.mgrid[-1:1:80j, -1:1:80j]
tck = interpolate.bisplrep(X, Y, Z, s=0)
znew = interpolate.bisplev(xnew[:,0], ynew[0,:], tck)
fig = go.Figure(data=[go.Surface(z=znew)])
fig.update_layout(template='plotly_dark',
width=800,
height=800,
title = title,
title_x = title_x,
title_y = title_y
)
return fig

Scaled colormap of facecolors with mplot3d

I have a simple task that should have a simple solution, but I have been trying for days now. I try to be specific.
I try to plot a surface using matplotlib's mplot3d and plot_surface.
When I plot the surface of a dataset 'z' and try to scale the colormap to a certain maximum value I change the 'vmax' property to this value. That works great.
When I try to plot a surface of one dataset (z) and use the facecolors of a second dataset (fc), this also works fine.
When I want to scale the colormap of the facecolors, the vmax property is overruled by the facecolors values. Vmax therefore has no effect (attempt1). The lines also disappeared, but that's another issue.
Also trying to change the values of the facecolor dataset (fc) did not have the desired effect (attempt2).
I try to get a figure with a scaled colormap (as in the figure 'scaled' below) but scaled to the facecolors, and not the z-values.
The code below is what I have now, and the results look like this:
Does anyone know what I am missing here? Any thoughts are appreciated!
import pylab as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
plt.ion()
# creating dataset
profile = np.arange(20)**2
z = profile.repeat(20).reshape(20,20)
fc= np.rot90(z.copy())
x = np.arange(z.shape[0])
y = np.arange(z.shape[1])
X, Y = np.meshgrid(x,y)
# plotting
vmax = 100
fig = plt.figure()
ax = fig.add_subplot(1,4,1, projection='3d', azim=210)
ax.plot_surface(X,Y,z, cmap=plt.cm.jet, cstride=1, rstride=1)
ax.set_title('normal')
ax = fig.add_subplot(1,4,2, projection='3d', azim=210)
ax.plot_surface(X,Y,z, cmap=plt.cm.jet, cstride=1, rstride=1, vmax=vmax)
ax.set_title('scaled')
ax = fig.add_subplot(1,4,3, projection='3d', azim=210)
ax.plot_surface(X,Y,z, facecolors=plt.cm.jet(fc), cstride=1, rstride=1, vmax=vmax)
ax.set_title('rotated (attempt1)')
ax = fig.add_subplot(1,4,4, projection='3d', azim=210)
fc[fc> vmax] = vmax
ax.plot_surface(X,Y,z, facecolors=plt.cm.jet(fc), cstride=1, rstride=1)
ax.set_title('rotated (attempt2)')
One - dirty - solution would be to rescale the clipped facecolors such that the maximum is equal to the maximum of your height map (in addition to basically what you suggested as attempt 2):
ax.plot_surface(X,Y,z, facecolors=plt.cm.jet(np.clip(fc,0,vmax)*np.max(z)/vmax), cstride=1, rstride=1, vmax=vmax)
Does this give the result you are looking for?

Categories