Smooth surface Plot with Pyplot - python

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

Related

Surface plot not graduating colours

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))

How can I speed up rendering of surface plots in matplotlib?

I'd like to generate surface plots in Python using matplotlib with around 100x100 points. I'm using the code below just to test it out:
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(0, 10, 0.1)
Y = np.arange(0, 10, 0.1)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
linewidth=0, antialiased=True)
ax.set_zlim(-1.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))
plt.show()
It all works fine, except for the fact that rotating the plot is very slow - the view changes around once per second, which is less than ideal. Using a wireframe plot instead of a surface plot gives acceptable performance, but it makes the data harder to see and means a colourmap can't be used. (Turning off anti-aliasing actually makes little difference.)
Is there anything I can do to speed it up, even if it's only a little?

Colormap in Matplotlib without using the "scatter" function

I have constructed a scatter plot with x and y positions. Now I have an array with a third variable, density, and I want to assign a color for each point in my scatter plot depending on its density value. I know how to do it using the "scatter" task of matplotlib, for example:
x = [1,2,3,4]
y = [5,3,7,1]
density = [1,2,3,4]
map = plt.scatter(x, y, c=density)
colorbar = plt.colorbar(map)
Now, I would like to do the same using the "plot" function instead, something like:
map = plt.plot(x,y, '.', c=t)
I am trying to do an animation of a galaxy merger, and assign each particle a color depending of the density of that region. So far the code only works with the "plot" task, so I need to implement it that way, but all the examples I've found use the former way.
Thanks in advance!
First off, #tcaswell is right. You're probably wanting to animate a scatter plot. Using lots of plot calls for this will result in much worse performance than changing the collection that scatter returns.
However, here's how you'd go about using multiple plot calls to do this:
import numpy as np
import matplotlib.pyplot as plt
xdata, ydata, zdata = np.random.random((3, 10))
cmap = plt.cm.gist_earth
norm = plt.Normalize(zdata.min(), zdata.max())
fig, ax = plt.subplots()
for x, y, z in zip(xdata, ydata, zdata):
ax.plot([x], [y], marker='o', ms=20, color=cmap(norm(z)))
sm = plt.cm.ScalarMappable(norm, cmap)
sm.set_array(zdata)
fig.colorbar(sm)
plt.show()
Just for comparison, here's the exact same thing using scatter:
import numpy as np
import matplotlib.pyplot as plt
xdata, ydata, zdata = np.random.random((3, 10))
fig, ax = plt.subplots()
scat = ax.scatter(xdata, ydata, c=zdata, s=200, marker='o')
fig.colorbar(scat)
plt.show()
If you wanted to change the position of the markers in the scatter plot, you'd use scat.set_offsets(xydata), where xydata is an Nx2 array-like sequence.

Matplotlib 2d Plot on Faces of 3d Plot

I am producing plots of a spacecraft's trajectory at a specific point in its orbit.
I have a piece of code which produces a 3d line plot in 3dMatplotlib (a part of mycode and figure is shown here (I have drastically reduced the number of points within X,Y,Z to ~20 per array to make it easier to simply copy and paste as the principle is the same):
#
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D
from numpy import *
XdS=[14.54156005, 14.53922242, 14.53688586, 14.53454823, 14.5322106 , 14.52987297, 14.52753426, 14.52519555, 14.52285792, 14.52051922, 14.51818051, 14.51584073, 14.51350095, 14.51116117, 14.5088214 , 14.50648162, 14.50414076, 14.50179991, 14.49945906, 14.49711821]
YdS=[31.13035144, 31.12920087, 31.12805245, 31.12690188, 31.12575131, 31.12460073, 31.12345016, 31.12229745, 31.12114473, 31.11999201, 31.1188393 , 31.11768443, 31.11652957, 31.11537471, 31.11421984, 31.11306283, 31.11190582, 31.11074882, 31.10959181, 31.1084348]
ZdS=[3.94109446, 3.94060316, 3.94011186, 3.93962083, 3.93912926, 3.93863796, 3.93814639, 3.93765482, 3.93716325, 3.93667169, 3.93617985, 3.93568828, 3.93519618, 3.93470434, 3.9342125 , 3.9337204 , 3.93322829, 3.93273592, 3.93224382, 3.93175144]
fig=plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(XdS,YdS,ZdS,c='black',linewidth=2)
ax.set_xlabel('XKSM (Saturn Radii)')
ax.set_ylabel('YKSM (Saturn Radii)')
ax.set_zlabel('ZKSM (Saturn Radii)')
plt.show()
#
What I want to do is be able to plot the 2d plots X vs Y, X vs Z, and Y vs Z on the edges/planes of this plot i.e. show what the 3d trajectory looks like looking at it in the 3 2d planes and display them at each axis of the current plot. (It isn’t actually as complicated as it might sound, as I already have the X,Y,Z, values for the trajectory). Here I found a similar example which achieves this, however utilising all 3d plot functions, available at: http://matplotlib.org/1.3.1/examples/mplot3d/contour3d_demo3.html : If you check out check out the link it will show the type of image i am trying to achieve.
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm
fig = plt.figure()
ax = fig.gca(projection='3d')
X, Y, Z = axes3d.get_test_data(0.05)
ax.plot_surface(X, Y, Z, rstride=8, cstride=8, alpha=0.3)
cset = ax.contour(X, Y, Z, zdir='z', offset=-100, cmap=cm.coolwarm)
cset = ax.contour(X, Y, Z, zdir='x', offset=-40, cmap=cm.coolwarm)
cset = ax.contour(X, Y, Z, zdir='y', offset=40, cmap=cm.coolwarm)
ax.set_xlabel('X')
ax.set_xlim(-40, 40)
ax.set_ylabel('Y')
ax.set_ylim(-40, 40)
ax.set_zlabel('Z')
ax.set_zlim(-100, 100)
plt.show()
This is in theory exactly what I need, in the way it takes sort of a planar view of the 3d situation. However I cannot implement a 2d line plot on a 3d axis nor can I use the offset command in a 2d plot (getting the error: TypeError: There is no line property "offset").
Is there a 2d equivalent to the 3d “offset” command and Is it possible to plot the 2d values on the planes of the 3d plot as I desire? Also is there a way to plot 2d lines having initialised a 3d projection? Can anyone offer any ideas/point me in any direction in general to help me achieve this?
My sincere thanks in advance and apologies if any part of this post is out of order, this is my first one!
Try this:
xmin = min(XdS)
ymax = max(YdS)
zmin = min(ZdS)
length_of_array = len(XdS)
xmin_array = [xmin] * length_of_array
ymax_array = [ymax] * length_of_array
zmin_array = [zmin] * length_of_array
fig=plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot(XdS,YdS,ZdS,zdir='z', c='r')
ax.plot(XdS,YdS,zmin_array, zdir='z', c='g')
ax.plot(xmin_array, YdS, ZdS, 'y')
ax.plot(XdS,ymax_array,ZdS,'b')
ax.set_xlabel('XKSM (Saturn Radii)')
ax.set_ylabel('YKSM (Saturn Radii)')
ax.set_zlabel('ZKSM (Saturn Radii)')
plt.show()

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