Matplotlib 3D scatter giving confusing error message - python

I want to set the color of different scatters and here comes the error, as is shown in the following code:
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
points = np.array([[1,2,3]])
labels = np.array([1])
colors = [[255, 0, 0],[0, 255, 0],[0, 0, 255], [255, 255, 0],[255, 0, 255],[0,255,255],[128,255,128]]
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for i in range(0,len(points)):
print('point and label')
print(points[i], labels[i])
color = colors[labels[i]-1]
print([0,0,0])
ax.scatter(points[i,0], points[i,1],zs=points[i,2],c=[0,0,0]) # work
print(color)
ax.scatter(points[i,0], points[i,1],zs=points[i,2],c=color) # error
print('finish')
plt.savefig('a.jpg',format='jpg')
The problem is that, if I set the c of the ax.scatter as [0,0,0], it works. However, if I set it to a list chosen from the colors I defined, it reports errors.
The complete print message is shown as follows (including the error message):
point and label
(array([1, 2, 3]), 1)
[0, 0, 0]
[255, 0, 0]
Traceback (most recent call last):
File "plot.py", line 47, in <module>
ax.scatter(points[i,0], points[i,1],zs=points[i,2],c=color) # error
File "mypath/local/lib/python2.7/site-packages/mpl_toolkits/mplot3d/axes3d.py", line 2362, in scatter
xs, ys, s=s, c=c, *args, **kwargs)
File "mypath/local/lib/python2.7/site-packages/matplotlib/__init__.py", line 1867, in inner
return func(ax, *args, **kwargs)
File "mypath/local/lib/python2.7/site-packages/matplotlib/axes/_axes.py", line 4293, in scatter
.format(c.shape, x.size, y.size))
AttributeError: 'list' object has no attribute 'shape'
What's wrong with my code and how to set the color of 3D scatter?
Thank you!

I cannot reproduce your error using matplotlib 3.0.1. However, here are a few suggestions.
First, matplotlib expects RGB[A] values to be in the range 0–1 and not 0–255
Second, do you really need to process your points in a loop? Your code could be simplified to a one line call to scatter:
points = np.random.random(size=(7,3))
colors = np.array([[1, 0, 0],[0, 1, 0],[0, 0, 1], [1, 1, 0],[1, 0, 1],[0,1,1],[0.5,1,0.5]])
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(points[:,0], points[:,1], zs=points[:,2], c=colors, s=100)
Third, if you have to pass points one at a time, you should have received a warning like:
'c' argument looks like a single numeric RGB or RGBA sequence, which
should be avoided as value-mapping will have precedence in case its
length matches with 'x' & 'y'. Please use a 2-D array with a single
row if you really want to specify the same RGB or RGBA value for all
points.
As it clearly states, when passing a single color, you should still use a 2D array to specify the color, i.e. ax.scatter(x,y,zs=z,c=[[0,0,0]])

Related

Trim figure (Matplotlib, 3D)

fig = plt.figure()
ax = Axes3D(fig, auto_add_to_figure=False)
fig.add_axes(ax)
ax.plot([0, 1], [1, 0], [-1, 1])
yields (borders added manually)
Adding
for anm in ('x', 'y', 'z'):
getattr(ax, f'set_{anm}ticks')([])
getattr(ax, f'w_{anm}axis').set_pane_color((1, 1, 1, 0))
getattr(ax, f'w_{anm}axis').line.set_color((1, 1, 1, 0))
getattr(ax, f'set_{anm}margin')(0)
I get what's on left, but I seek what's on right
How can this be accomplished? Full code
I seek a native solution (no save-stage post-processing, or third party libraries), as the figure is processed further natively in matplotlib (specifically in an animation). Ideally the solution is general-purpose, i.e. not specific to Axes3D.
To test the answer, it should produce the following output in full context code, as a .mp4 (I got this output with manual post-processing):

Matplotlib: Indexing color map

I have a 3-d plot, but 4 of my 16 data points have invalid data from one of components (ie a vector has good x and y data but bad z data). I could just omit the points entirely, but then I'm getting rid of good data (in the x and y directions). Ideally, I would like to be able to change the color of the ones missing data as a kind of flag, so that they are still plotted but I can tell which ones are missing the data.
I have have very little experience with color maps, especially in 3-d. I've tried adapting solutions from:
(python) plot 3d surface with colormap as 4th dimension, function of x,y,z
Create own colormap using matplotlib and plot color scale
and this one which seems the most helpful:
Adding colors to a 3d quiver plot in matplotlib
But I just want all of them to be the same colors except for very specific data points and I'm not sure how to index the color map.
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d, Axes3D
def main():
data = [[ 1.52940323e-06, 3.30263460e-07, 1.23333738e-02],
[ 0.00062357, -0.00061659, -0.21386033],
[-0.00028384, 0.00088403, -0.21165629],
[ 0.00225299, 0.00180132, -0.1964095 ],
[-0.00066298, 0.00271399, -0.23091235],
[ 0.00054687, -0.00063866, -0.24299 ],
[ 0.00170783, -0.00140304, -0.09094558],
[-0.00378669, -0.00592137, -0.07358853],
[ 5.84581114e-07, -3.58723162e-07, -3.08931350e-02],
[ 0.0003522 , -0.00067592, -0.23933634],
[ -5.84077540e-07, 2.08945622e-07, -4.31579608e-02],
[-0.00196888, 0.00261409, -0.28115362],
[ -1.65606166e-04, 1.89755530e-01, -1.49510581e-02],
[-0.00048166, 0.00095946, -0.26929835],
[ 8.26054997e-04, -7.75840354e-05, -3.05118605e-01],
[ 0.0018271 , 0.00078126, -0.18526635]]
fig = plt.figure()
ax = Axes3D(fig)
x = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
y = [-0.002, -0.002, -0.002, -0.002, -0.001, -0.001, -0.001, -0.001,
0.001, 0.001, 0.001, 0.001, 0.002, 0.002, 0.002, 0.002]
z = [-0.0022500000000000003, -0.00075, 0.00075, 0.0022500000000000003,
-0.0022500000000000003, -0.00075, 0.00075, 0.0022500000000000003,
-0.0022500000000000003, -0.00075, 0.00075, 0.0022500000000000003,
-0.0022500000000000003, -0.00075, 0.00075, 0.0022500000000000003]
# data[0]= [0]*3
# data[8]= [0]*3
# data[10]= [0]*3
# data[12]= [0]*3
u = [data[i][0] for i in range(len(data))]
v = [data[i][1] for i in range(len(data))]
w = [data[i][2] for i in range(len(data))]
cdict = {'green': np.array(len(u)), 'green': np.array(len(u)),
'green':np.array(len(u))}
cdict = {'red': [0],'red': [0],'red': [0]}
ax.quiver(x,y,z,u,v,w, length=0.001, normalize=True, colors=cdict)
ax.set_xlabel("X direction")
ax.set_ylabel("Y direction")
ax.set_zlabel("Z direction")
plt.axis('equal')
plt.show()
if __name__ == '__main__':
main()
This plots everything as red, instead of everything green and only certain vectors red. I'm not really sure what I'm doing, and any help would be much appreciated! (Also yes, I am stuck in the stone age with python 2)
Edit:
Inspired by the answer below, I created a mask for 'bad' channels, then created two plots, one with the good channels in blue, and one with the bad channels in red.
dead_chans = [1, 0,0,0,0,0,0,0,1,0,1,0,1,0,0,0]
good_u = np.ma.masked_array(u,dead_chans)
good_v = np.ma.masked_array(v, dead_chans)
good_w = np.ma.masked_array(w, dead_chans)
dead_u = np.ma.masked_array(u, np.logical_not(dead_chans))
dead_v = np.ma.masked_array(v, np.logical_not(dead_chans))
dead_w = np.ma.masked_array(w, np.logical_not(dead_chans))
ax.quiver(x,y,z,good_u,good_v,good_w, length=0.01, normalize=True, color=
'b')
ax.quiver(x,y,z,dead_u,dead_v,dead_w, length=0.01, normalize=True, color =
'r')
plt.show()
You can set the z values to a np.nan instead of ignoring them altogether. Then you could use colormap.set_bad() to change the color of your nan values to something specific.
Hope that helps
https://matplotlib.org/api/_as_gen/matplotlib.colors.Colormap.html
Edit
with help from this post:
How can I plot NaN values as a special color with imshow in matplotlib?
Here's an example.
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
#Set the defualt cmap for mpl
mpl.rcParams['image.cmap'] = 'Blues'
cmap = mpl.cm.get_cmap()
cmap.set_bad(color='Red')
#Generate some data with np.nan values
x = np.arange(1,13,1)
y = np.arange(1,13,1)
vals = np.zeros([12,12])
for i in range(12):
for j in range(12):
if (i+j)%7==0:
vals[i,j] = np.nan
else:
vals[i,j] = i*j
plt.pcolormesh(x,y,arr)
This code returns the following plot:

Creating a dict of labels and their point objects from a scatter plot

I am creating an interactive graph where I can select the points of a scatter plot for further operations e.g. swapping positions with another point. When the point is selected, it turns black to indicate that the point has been selected.
There may be several scatter plots in the figure and each plot has a unique color so to retain the original color for each point, I need to create a dictionary of labels (key) and their point objects (value). Other values in the pairing include original color, xy position, etc.
Here's a single scatter plot and it's pick event function:
self.scatter = self.mplwidget_layout.canvas.ax.scatter(
y=...,
x=...,
color=...,
edgecolors=...,
picker=True
)
self.mplwidget_layout.canvas.mpl_connect('pick_event', self.select_point)
def select_point(self, event):
if event.mouseevent.button == 1:
facecolor = self.scatter._facecolors[event.ind,:]
if (facecolor == np.array([[0, 0, 0, 1]])).all():
# Look up label-object dict
else:
self.scatter._facecolors[event.ind,:] = (0, 0, 0, 1)
self.scatter._edgecolors[event.ind,:] = (1, 1, 1, 1)
self.mplwidget_layout.canvas.draw()
It seems like I can't assign individual labels (in an array) to their points but I can map the offsets to the labels. Is there a way to give each point a name and retrieve them by name?
How do I access the list of point objects in a scatter plot?
Points and their assigned names:
In order to get back the original color after clicking the point you may use the event.ind and the list of colors you have used initially to colorize the points. I do not see the need for a dictionary here at all.
import matplotlib.pyplot as plt
import numpy as np
x = np.random.rand(6)
y = np.random.rand(6)
fig, ax = plt.subplots()
ax.set_facecolor("k")
fcolor = plt.cm.RdYlBu(x)
ecolor = ["k"]*6
scatter = ax.scatter(x,y, s=100, facecolors=fcolor,edgecolors=ecolor , picker=True)
def select_point(event):
if event.mouseevent.button == 1:
facecolor = scatter._facecolors[event.ind,:]
if (facecolor == np.array([[0, 0, 0, 1]])).all():
scatter._facecolors[event.ind,:] = fcolor[event.ind]
scatter._edgecolors[event.ind,:] = (0, 0, 0, 1)
else:
scatter._facecolors[event.ind,:] = (0, 0, 0, 1)
scatter._edgecolors[event.ind,:] = (1, 1, 1, 1)
fig.canvas.draw_idle()
fig.canvas.mpl_connect('pick_event', select_point)
plt.show()

RuntimeError: Can not put single artist in more than one figure when using matplotlib 1.5

Here is my code:
import matplotlib.pyplot as plt
plt.figure(1) # the first figure
plt.subplot(211) # the first subplot in the first figure
plt.plot([1, 2, 3])
plt.subplot(212) # the second subplot in the first figure
plt.plot([4, 5, 6])
plt.figure(2) # a second figure
plt.plot([4, 5, 6]) # creates a subplot(111) by default
plt.text(.5,1.5,'211',figure = 211) #tring to add text in previous subplot
plt.figure(1) # figure 1 current; subplot(212) still current
plt.subplot(211) # make subplot(211) in figure1 current
plt.title('Easy as 1, 2, 3') # subplot 211 title
The error:
Traceback (most recent call last):
File "C:/Users/ezhou/Desktop/python/test3.py", line 11, in <module>
plt.text(.5,1.5,'211',figure = 211)
File "C:\Python27\lib\site-packages\matplotlib\pyplot.py", line 3567, in text
ret = gca().text(x, y, s, fontdict=fontdict, withdash=withdash, **kwargs)
File "C:\Python27\lib\site-packages\matplotlib\axes\_axes.py", line 619, in text
self._add_text(t)
File "C:\Python27\lib\site-packages\matplotlib\axes\_base.py", line 1720, in _add_text
self._set_artist_props(txt)
File "C:\Python27\lib\site-packages\matplotlib\axes\_base.py", line 861, in _set_artist_props
a.set_figure(self.figure)
File "C:\Python27\lib\site-packages\matplotlib\artist.py", line 640, in set_figure
raise RuntimeError("Can not put single artist in "
RuntimeError: Can not put single artist in more than one figure
I was trying to understand the kwargs 'figure' in class matplotlib.text.Text(), but it will always reply 'Can not put single artist in more than one figure'. So I was confused about how to use this 'figure' kwarg. Can anyone give me some advise? Thanks!
You shouldn't pass figure as a kwarg, instead use text method of a Figure (or Axes) instance. Example:
import matplotlib.pyplot as plt
fig1, fig2 = plt.figure(1), plt.figure(2)
sp1, sp2 = fig1.add_subplot(211), fig2.add_subplot(211)
sp1.plot([1, 2, 3])
sp2.plot([0, 1, 3])
fig1.text(.5, .3, 'whole figure')
sp2.text(.5, .5, 'subplot')
Please note that coordinates are relative (0, 1).
P.S if you find matplotlib needlessly complicated (as I do), you may wish to have a look at Plotly

matplotlib: fail to plot line or bar; valid axes

I'm attempting generate matplotlib images through a loop. I have two iterations of loops that generate images. The first loop works, the second doesn't. The axes are valid, I can see that when I print the numpy arrays.
plt_mean = float(week_occurrences) / len(x_axis)
y_np = np.array(y_axis)
std_d = np.std(y_np)
plt.plot(x_axis, y_np, color='#758AA8')
plt.axis([y_axis[0], y_axis[6], 0, int(y_max * 1.2)])
plt.axhline(plt_mean, color='black')
plt.ylabel("Events")
plt.xlabel("Day")
plt.title(event)
plt.savefig("tmp/{} {}.jpg".format(event, y_axis[0]), bbox_inches='tight')
plt.clf()
print(event)
print(y_max)
print(plt_mean)
print(x_axis)
raw_input(y_np)
output:
A user account was changed.
384
111.571428571
[5, 22, 4, 384, 363, 3, 0]
[166 167 168 169 170 171 172]
What am I missing? Why won't it plot the associated lines?
I believe the line is plotted, but I think your axis limits are wrong. I'm not entirely sure what you're trying to do, because it looks like you've inverted your x and y.
here is the result after the line:
plt.plot(x_axis, y_np, color='#758AA8')
However, after the line
plt.axis([y_axis[0], y_axis[6], 0, int(y_max * 1.2)])
the axes limit do not make any sense anymore and you're seeing a region where there are no data.
plt.axis() takes its argument in the order [xmin, xmax, ymin, ymax]
Looks like you didn't define y_max correctly. This works for me:
import numpy as np
import matplotlib.pylab as plt
x_axis = [5, 22, 4, 384, 363, 3, 0]
y_axis = [166, 167, 168, 169, 170, 171, 172]
y_max = np.max(y_axis)
event = np.str('A user account was changed.')
week_occurrences = 780.999999997
plt_mean = float(week_occurrences) / len(x_axis)
y_np = np.array(y_axis)
std_d = np.std(y_np)
plt.plot(x_axis, y_np, color='#758AA8')
plt.axis([y_axis[0], y_axis[6], 0, int(y_max * 1.2)])
plt.axhline(plt_mean, color='black')
plt.ylabel("Events")
plt.xlabel("Day")
plt.title(event)
# plt.savefig("tmp/{} {}.jpg".format(event, y_axis[0]), bbox_inches='tight')
# plt.clf()
print(event)
print(y_max)
print(plt_mean)
print(x_axis)

Categories