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()
Related
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()
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 !
I want to plot four points (or lines connected to each other) in 3D coordinates. And the coordinates are stored in X, Y and Z. For example, I have the following lines:
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.gca(projection='3d')
X = np.matrix('-1, 1, 1, 2')
Y = np.matrix('1, 2, 4, 6')
Z = np.matrix('3, 4, 2, 1')
ax.plot(X, Y, Z)
plt.show()
But after running, there will be a type error. I guess it's due to the input of the matrix type in numpy. Anybody know how to easily solve this problem?
Error message:
Traceback (most recent call last):
File "C:\Users\I077165\Desktop\tmp.py", line 11, in <module>
ax.plot(X, Y, Z)
File "C:\Python27\lib\site-packages\mpl_toolkits\mplot3d\axes3d.py", line 1312, in plot
lines = Axes.plot(self, xs, ys, *args[argsi:], **kwargs)
File "C:\Python27\lib\site-packages\matplotlib\axes.py", line 3848, in plot
for line in self._get_lines(*args, **kwargs):
File "C:\Python27\lib\site-packages\matplotlib\axes.py", line 323, in _grab_next_args
for seg in self._plot_args(remaining, kwargs):
File "C:\Python27\lib\site-packages\matplotlib\axes.py", line 284, in _plot_args
raise ValueError, 'third arg must be a format string'
ValueError: third arg must be a format string
Thanks.
Use np.array() instead of np.matrix() for all three datasets.
So for the X data, np.matrix('-1, 1, 1, 2') should become np.array([-1, 1, 1, 2])
I can't seem to find the answer anywhere! I found a discussion here, but trying this I get a TypeError: 'NoneType' object is not iterable:
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> x, y = np.meshgrid(np.arange(10),np.arange(10))
>>> z = x + y
>>> cs = plt.contourf(x,y,z,levels=[2,3])
>>> cs.collections[0].set_label('test')
>>> plt.legend()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/pyplot.py", line 2791, in legend
ret = gca().legend(*args, **kwargs)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/axes.py", line 4475, in legend
self.legend_ = mlegend.Legend(self, handles, labels, **kwargs)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/legend.py", line 365, in __init__
self._init_legend_box(handles, labels)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/legend.py", line 627, in _init_legend_box
handlebox)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/legend_handler.py", line 110, in __call__
handlebox.get_transform())
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/legend_handler.py", line 352, in create_artists
width, height, fontsize)
File "/opt/local/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/matplotlib/legend_handler.py", line 307, in get_sizes
size_max = max(orig_handle.get_sizes())*legend.markerscale**2
TypeError: 'NoneType' object is not iterable
EDIT: I'm looking for something like this:
You could also do it directly with the lines of the contour, without using proxy artists.
import matplotlib
import numpy as np
import matplotlib.cm as cm
import matplotlib.mlab as mlab
import matplotlib.pyplot as plt
matplotlib.rcParams['xtick.direction'] = 'out'
matplotlib.rcParams['ytick.direction'] = 'out'
delta = 0.025
x = np.arange(-3.0, 3.0, delta)
y = np.arange(-2.0, 2.0, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
# difference of Gaussians
Z = 10.0 * (Z2 - Z1)
# Create a simple contour plot with labels using default colors. The
# inline argument to clabel will control whether the labels are draw
# over the line segments of the contour, removing the lines beneath
# the label
plt.figure()
CS = plt.contour(X, Y, Z)
plt.clabel(CS, inline=1, fontsize=10)
plt.title('Simplest default with labels')
labels = ['line1', 'line2','line3','line4',
'line5', 'line6']
for i in range(len(labels)):
CS.collections[i].set_label(labels[i])
plt.legend(loc='upper left')
Will produce:
However, you might also want to look into annotations for your own need. In my opinion it will give you a more fine grained control on where and what you write on the image, here is the same example with some annotation:
### better with annotation, more flexible
plt.figure(2)
CS = plt.contour(X, Y, Z)
plt.clabel(CS, inline=1, fontsize=10)
plt.title('Simplest default with labels')
plt.annotate('some text here',(1.4,1.6))
plt.annotate('some text there',(-2,-1.5))
You can create proxy artists to make the legend:
import numpy as np
import matplotlib.pyplot as plt
x, y = np.meshgrid(np.arange(10),np.arange(10))
z = np.sqrt(x**2 + y**2)
cs = plt.contourf(x,y,z,levels=[2,3,4,6])
proxy = [plt.Rectangle((0,0),1,1,fc = pc.get_facecolor()[0])
for pc in cs.collections]
plt.legend(proxy, ["range(2-3)", "range(3-4)", "range(4-6)"])
plt.show()
Adding to this answer to make it less manual:
import numpy as np
import matplotlib.pyplot as plt
x, y = np.meshgrid(np.arange(10),np.arange(10))
z = np.sqrt(x**2 + y**2)
levels=[2,3,4,6]
cs = plt.contourf(x,y,z,levels=levels)
proxy = [plt.Rectangle((0,0),1,1,fc = pc.get_facecolor()[0])
for pc in cs.collections]
plt.legend(proxy, [f"{lower:2.1f} - {upper:2.1f}" for lower, upper in zip(levels[:-1], levels[1:])])
plt.show()
I had a similar question but needed to go a bit beyond HYRY's answer. To make a package user friendly I wanted ax.legend() to work without requiring users to pass any handles, which can be achieved by passing the label on to the proxy
proxy = plt.Rectangle((0, 0), 1, 1, fc='red', label='some label')
and then adding the proxy to the axis' patches:
ax.patches += [proxy]
(do ax = plt.gca() to get the current axis)
This is described in more detail in this answer.
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: