3D surface plot with complex z matplotlib - python

I have x, y, z as:
x = list(range(20, 100, 20))
y = list(range(100, 200, 20))
Z = function(x, y, a, b)
where a is a Dataframe with datetimeindex and single column, b is a boolean series, and function gives an array Z((len(x), len(y))), function is a nested loop with other functions used inside.
I wanted to plot a surface that shows how z change with x and y by:
X = list(range(20, 100, 20))
Y = list(range(100, 200, 20))
X, Y = np.meshgrid(X, Y)
Z = function(x, y, a, b)
surf = ax.plot_surface(X, Y, Z, inewidth=0, antialiased=False)
fig.colorbar(surf, shrink=0.5, aspect=5)
plt.show()
However i got this error message:
Traceback (most recent call last):
File "C:/Users/simulation.py", line 82, in <module>
linewidth=0, antialiased=False)
File "C:\Users\baili\PycharmProjects\import_yahoo_data\venv_1\lib\site-packages\mpl_toolkits\mplot3d\axes3d.py", line 1621, in plot_surface
X, Y, Z = np.broadcast_arrays(X, Y, Z)
File "<__array_function__ internals>", line 6, in broadcast_arrays
File "C:\Users\baili\AppData\Local\Programs\Python\Python37-32\lib\site-packages\numpy\lib\stride_tricks.py", line 264, in broadcast_arrays
shape = _broadcast_shape(*args)
File "C:\Users\baili\AppData\Local\Programs\Python\Python37-32\lib\site-packages\numpy\lib\stride_tricks.py", line 191, in _broadcast_shape
b = np.broadcast(*args[:32])
ValueError: shape mismatch: objects cannot be broadcast to a single shape
I think it has something to do with the z function....but I am not sure exactly how.....
Thanks !

Related

Python 3D Plot from dictonary values

I do have a script, which creates a dict with XYZ-values. Following dict includes values at x from -2 to 2, with y from 0 to 2.
my_dict = {
-2:{0:1,1:1,2:0},
-1:{0:3,1:1,2:0},
0:{0:6,1:1,2:9},
1:{0:-2,1:1,2:2},
2:{0:1,1:1,2:6}}
Now, I don't now how to create a 3D plot out of this. I am aware of the matplotlib library,but I am not sure how to generate my Z-Data. I tried to write a function, to get my Z-data in a mesh, but it doesn't work. This is what I got so far:
x = np.arange(-2, 2, 1)
y = np.arange(0, 2, 1)
X, Y = np.meshgrid(x, y)
Z = f(X,Y) #HERE, the function f is what I am searching for.
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.contour3D(X, Y, Z, 50, cmap='binary')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
Is there any numpy or pythonic way to do this?
Is this what you're looking for?
my_dict = {
-2:{0:1,1:1,2:0},
-1:{0:3,1:1,2:0},
0:{0:6,1:1,2:9},
1:{0:-2,1:1,2:2},
2:{0:1,1:1,2:6}}
x = np.arange(-2, 3, 1)
y = np.arange(0, 3, 1)
X, Y = np.meshgrid(x, y)
def f(x, y):
z = np.zeros(X.reshape(-1).shape) # Create an "empty" tensor that matches the "flattened" meshgrid
c = 0 # To index over our "z"
for i in y:
for j in x:
z[c] = my_dict[j][i] # Fill the empty tensor with its corresponding values from the dictionary (depending on x and y)
c += 1
z = z.reshape(X.shape) # Reshape it back to match meshgrid's shape
return z
Z = f(x, y)
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.contour3D(X, Y, Z, 50, cmap='binary')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
plt.show()
I believe you can get away with accessing the correct value in your dict via:
Z = mydict[x][y]

3D data contour ploting using a kde

I have two Arrays of positional Data (X,Y) and a corresponding 1D Array of Integers (Z) that weighs the positional Data. So my Data set looks like that:
X = [ 507, 1100, 1105, 1080, 378, 398, 373]
Y = [1047, 838, 821, 838, 644, 644, 659]
Z = [ 300, 55, 15, 15, 55, 15, 15]
I want to use that Data to create a KDE thats equivalent to a KDE that gets only X and Y as input but gets the X and Y values Z times. To apply that KDE to a np.mgrid to create a contourplot.
I already got it working by just iterating over the arrays in a FOR Loop and adding Z times X and Y, but that looks to me like a rather inelegant Solution and I hope you can help me to find a better way of doing this.
You could use the weights= parameter of scipy.stats.gaussian_kde:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
import numpy as np
from scipy import stats
X = [ 507, 1100, 1105, 1080, 378, 398, 373]
Y = [1047, 838, 821, 838, 644, 644, 659]
Z = [ 300, 55, 15, 15, 55, 15, 15]
kernel = stats.gaussian_kde(np.array([X, Y]), weights=Z)
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
xs, ys = np.mgrid[0:1500:30j, 0:1500:30j]
zs = kernel(np.array([xs.ravel(), ys.ravel()])).reshape(xs.shape)
ax.plot_surface(xs, ys, zs, cmap="hot_r", lw=0.5, rstride=1, cstride=1, ec='k')
plt.show()

facecolors in plot_surface matplotlib

I want to reproduce this example: https://matplotlib.org/3.3.1/gallery/mplot3d/surface3d_3.html#sphx-glr-gallery-mplot3d-surface3d-3-py but with different colors.
the original code is:
import matplotlib.pyplot as plt
from matplotlib.ticker import LinearLocator
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
# Make data.
X = np.arange(-5, 5, 0.25)
xlen = len(X)
Y = np.arange(-5, 5, 0.25)
ylen = len(Y)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
# Create an empty array of strings with the same shape as the meshgrid, and
# populate it with two colors in a checkerboard pattern.
colortuple = ('y', 'b')
colors = np.empty(X.shape, dtype=str)
for y in range(ylen):
for x in range(xlen):
colors[x, y] = colortuple[(x + y) % len(colortuple)]
# Plot the surface with face colors taken from the array we made.
surf = ax.plot_surface(X, Y, Z, facecolors=colors, linewidth=0)
# Customize the z axis.
ax.set_zlim(-1, 1)
ax.zaxis.set_major_locator(LinearLocator(6))
plt.show()
when i change "colortuple =('y', 'b')" to "colortuple =('#ff7f0e', '#2ca02c') i am having this error:
File "<ipython-input-41-a355b1dad171>", line 1, in <module>
runfile('C:/Users/camrane/Downloads/dflt_style_changes-1.py', wdir='C:/Users/camrane/Downloads')
File "C:\ProgramData\Anaconda3\lib\site-packages\spyder\utils\site\sitecustomize.py", line 705, in runfile
execfile(filename, namespace)
File "C:\ProgramData\Anaconda3\lib\site-packages\spyder\utils\site\sitecustomize.py", line 102, in execfile
exec(compile(f.read(), filename, 'exec'), namespace)
File "C:/Users/camrane/Downloads/dflt_style_changes-1.py", line 27, in <module>
surf = ax.plot_surface(X, Y, Z, facecolors=colors, linewidth=0)
File "C:\ProgramData\Anaconda3\lib\site-packages\mpl_toolkits\mplot3d\axes3d.py", line 1740, in plot_surface
colset = self._shade_colors(colset, normals)
File "C:\ProgramData\Anaconda3\lib\site-packages\mpl_toolkits\mplot3d\axes3d.py", line 1790, in _shade_colors
color = mcolors.to_rgba_array(color)
File "C:\ProgramData\Anaconda3\lib\site-packages\matplotlib\colors.py", line 267, in to_rgba_array
result[i] = to_rgba(cc, alpha)
File "C:\ProgramData\Anaconda3\lib\site-packages\matplotlib\colors.py", line 168, in to_rgba
rgba = _to_rgba_no_colorcycle(c, alpha)
File "C:\ProgramData\Anaconda3\lib\site-packages\matplotlib\colors.py", line 212, in _to_rgba_no_colorcycle
raise ValueError("Invalid RGBA argument: {!r}".format(orig_c))
ValueError: Invalid RGBA argument: '#'
)"
and when it is to "colortuple =('gray', 'orange') i have this error
File "<ipython-input-42-a355b1dad171>", line 1, in <module>
runfile('C:/Users/camrane/Downloads/dflt_style_changes-1.py', wdir='C:/Users/camrane/Downloads')
File "C:\ProgramData\Anaconda3\lib\site-packages\spyder\utils\site\sitecustomize.py", line 705, in runfile
execfile(filename, namespace)
File "C:\ProgramData\Anaconda3\lib\site-packages\spyder\utils\site\sitecustomize.py", line 102, in execfile
exec(compile(f.read(), filename, 'exec'), namespace)
File "C:/Users/camrane/Downloads/dflt_style_changes-1.py", line 27, in <module>
surf = ax.plot_surface(X, Y, Z, facecolors=colors, linewidth=0)
File "C:\ProgramData\Anaconda3\lib\site-packages\mpl_toolkits\mplot3d\axes3d.py", line 1740, in plot_surface
colset = self._shade_colors(colset, normals)
File "C:\ProgramData\Anaconda3\lib\site-packages\mpl_toolkits\mplot3d\axes3d.py", line 1790, in _shade_colors
color = mcolors.to_rgba_array(color)
File "C:\ProgramData\Anaconda3\lib\site-packages\matplotlib\colors.py", line 267, in to_rgba_array
result[i] = to_rgba(cc, alpha)
File "C:\ProgramData\Anaconda3\lib\site-packages\matplotlib\colors.py", line 168, in to_rgba
rgba = _to_rgba_no_colorcycle(c, alpha)
File "C:\ProgramData\Anaconda3\lib\site-packages\matplotlib\colors.py", line 212, in _to_rgba_no_colorcycle
raise ValueError("Invalid RGBA argument: {!r}".format(orig_c))
ValueError: Invalid RGBA argument: 'o'
It is like the code accpets only basic colors.
Thanks
Interesting problem. This is due to the way the colors array is created in the example.
colortuple = ('y', 'b')
colors = np.empty(X.shape, dtype=str)
for y in range(ylen):
for x in range(xlen):
colors[x, y] = colortuple[(x + y) % len(colortuple)]
because the colors array is declared with dtype=str, it only accepts only 1 char per cell, and the colors that you are trying to use are truncated.
From this answer, you can initialize the array with a larger size to circumvent this issue colors = np.empty(X.shape, dtype='U50')
import matplotlib.pyplot as plt
from matplotlib.ticker import LinearLocator
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
# Make data.
X = np.arange(-5, 5, 0.25)
xlen = len(X)
Y = np.arange(-5, 5, 0.25)
ylen = len(Y)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
# Create an empty array of strings with the same shape as the meshgrid, and
# populate it with two colors in a checkerboard pattern.
colortuple = ('xkcd:blue with a hint of purple', '#ff7f0e', 'orange')
colors = np.empty(X.shape, dtype='U50')
for y in range(ylen):
for x in range(xlen):
colors[x, y] = colortuple[(x + y) % len(colortuple)]
# Plot the surface with face colors taken from the array we made.
surf = ax.plot_surface(X, Y, Z, facecolors=colors, linewidth=0)
# Customize the z axis.
ax.set_zlim(-1, 1)
ax.zaxis.set_major_locator(LinearLocator(6))
plt.show()

Matplotlib griddata fails

I am trying to write a function which makes a contour plot from a text data file (or a numpy array) formatted as "x, y, z". However, when I try to use griddata to interpolate the data, I get a "type" error:
if not len(x)==len(y)==len(z):
TypeError: object of type 'numpy.float64' has no len()
This is my function:
def ContourPlot(datafile,columns=[0,1,2], nXvals=100, nYvals=100, title='', xlab='', ylab='', colormap='rainbow', contours=10):
if type(datafile)==type(str()):
try:
x, y, z = np.loadtxt(datafile, dtype='float', unpack=True, usecols=columns)
except:
print "Can't open the input file!"
exit
elif type(datafile)==np.ndarray:
x = datafile[0]
y = datafile[1]
z = datafile[2]
else:
print "ERROR: You tried to pass data to the ContourPlot() function in a format it cannot read"
exit
print type(x)
xi = np.linspace(np.amin(x), np.amax(x), nXvals)
yi = np.linspace(np.amin(y), np.amax(y), nYvals)
zi = griddata(x, y, z, xi, yi)
norm = colors.Normalize(vmin = np.min(z), vmax = np.max(z), clip = False)
pl.figure()
pl.contourf(xi, yi, zi, 30, cmap = pl.get_cmap(colormap), norm =norm)
CS = pl.contour(xi, yi, zi, colors = 'k',lw = 3, levels= contours)
pl.clabel(CS, inline=1, fontsize=10)
pl.tick_params(axis='x', labelsize=20)
pl.tick_params(axis='y', labelsize=20)
pl.title(title, fontsize=17)
pl.xlabel(xlab, fontsize=20)
pl.ylabel(ylab, fontsize=20)
pl.show()
I tried converting x, y, z to regular Python lists with the tolist() method, but it didn't work.
Any help would be greatly appreciated!
I suppose you are using an older version of matplotlib? Where are you importing your griddata from? Have a look at the griddate function in your matplotlib/mlab.py file and look whether there is a line similar to
if not len(x)==len(y)==len(z):
raise TypeError("inputs x,y,z must all be 1D arrays of the same length")
The current version at https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/mlab.py does not have this line any more but now checksthis way:
# Check input arguments.
x = np.asanyarray(x, dtype=np.float64)
y = np.asanyarray(y, dtype=np.float64)
z = np.asanyarray(z, dtype=np.float64)
if x.shape != y.shape or x.shape != z.shape or x.ndim != 1:
raise ValueError("x, y and z must be equal-length 1-D arrays")
So an update to a more recent version might already solve your problem. In any case currently at least one of your x, y, z inputs is of type numpy.float64 and for some reason has no len().

Color matplotlib plot_surface command with surface gradient

I would like to convert surf command from MATLAB to plot_surface command in matplotlib.
The challenge I am facing is when using cmap function in plot_surface command to color the surface with gradient.
Here is the matlab script
% Matlab Commands
x = -5:.25:5; y = x
[x,y] = meshgrid(x);
R = sqrt(x.^2 + y.^2);
Z = sin(R)
surf(x,y,Z,gradient(Z))
The figure from such a command can be found here. (http://www.mathworks.com/help/techdoc/visualize/f0-18164.html#f0-46458)
Here is the python scipt
When using python and matplotlib to create a similar function I am unable to color the surface with a gradient.
# Python-matplotlib Commands
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
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)
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=gradient(Z), linewidth=0, antialiased=False)
plt.show()
I get the following error message:
Traceback (most recent call last):
File "<ipython console>", line 1, in <module>
File "C:\Python26\lib\site-packages\spyderlib\widgets\externalshell\startup.py", line 122, in runfile
execfile(filename, glbs)
File "C:\Documents and Settings\mramacha\My Documents\Python\Candela\tmp.py", line 13, in <module>
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=gradient(Z), linewidth=0, antialiased=False)
File "C:\Python26\lib\site-packages\mpl_toolkits\mplot3d\axes3d.py", line 729, in plot_surface
polyc = art3d.Poly3DCollection(polys, *args, **kwargs)
File "C:\Python26\lib\site-packages\mpl_toolkits\mplot3d\art3d.py", line 344, in __init__
PolyCollection.__init__(self, verts, *args, **kwargs)
File "C:\Python26\lib\site-packages\matplotlib\collections.py", line 570, in __init__
Collection.__init__(self,**kwargs)
File "C:\Python26\lib\site-packages\matplotlib\collections.py", line 86, in __init__
cm.ScalarMappable.__init__(self, norm, cmap)
File "C:\Python26\lib\site-packages\matplotlib\cm.py", line 155, in __init__
self.cmap = get_cmap(cmap)
File "C:\Python26\lib\site-packages\matplotlib\cm.py", line 126, in get_cmap
if name in cmap_d:
TypeError: unhashable type: 'list'
Any inputs would be helpful.
Praboo
First, it looks like you want the colors mapped from gradient magnitude. You are trying to use the gradient vectors which is why you are getting the 'list' error.
Second, you can supply a cmap, but it only defines how you want the Z values mapped to a color. If you want new face colors then use the facecolors argument.
Third, you'll want to normalize the values to 0..1 then map them thru a colormap. (I think there is another way, but dividing the magnitude by the max is pretty simple)
Here's the code:
# Python-matplotlib Commands
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.arange(-5, 5, .25)
Y = np.arange(-5, 5, .25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
Gx, Gy = np.gradient(Z) # gradients with respect to x and y
G = (Gx**2+Gy**2)**.5 # gradient magnitude
N = G/G.max() # normalize 0..1
surf = ax.plot_surface(
X, Y, Z, rstride=1, cstride=1,
facecolors=cm.jet(N),
linewidth=0, antialiased=False, shade=False)
plt.show()
And the result:

Categories

Resources