Plotting per-point alpha values in 3D scatterplot throws ValueError - python

I have data in form of a 3D array, with "intensities" at every point. Depending on the intensity, I want to plot the point with a higher alpha. There are a lot of low-value outliers, so color coding (with scalar floats) won't work since they eclipse the real data.
What I have tried:
#this generates a 3D array with higher values around the center
a = np.array([0,1,2,3,4,5,4,3,2,1])
aa = np.outer(a,a)
aaa = np.einsum("ij,jk,jl",aa,aa,aa)
x_,y_,z_,v_ = [],[],[],[]
from matplotlib.colors import to_rgb,to_rgba
for x in range(aaa.shape[0]):
for y in range(aaa.shape[1]):
for z in range(aaa.shape[2]):
x_.append(x)
y_.append(y)
z_.append(z)
v_.append(aaa[x,y,z])
r,g,b = to_rgb("blue")
color = np.array([[r,g,b,a] for a in v_])
fig = plt.figure()
ax = fig.add_subplot(projection = '3d')
ax.scatter(x_,y_,z_,c =color)
plt.show()
the scatterplot documentation says that color can be a 2D array of RGBA, which I do pass. Hoever when I try to run the code, I get the following error:
ValueError: 'c' argument has 4000 elements, which is inconsistent with 'x' and 'y' with size 1000.

I just found my own answer.
The "A 2D array in which the rows are RGB or RGBA." statement in the documentation was a bit confusing - one needs to convert the RGBA rows to RGBA objects first, so that list comprehension should read:
color = np.array([to_rgba([r,g,b,a]) for a in v_])

Related

plt.imshow() shows only one color

I am trying to plot a heatmap from a 2000x2000 NumPy array. I have tried every solution from this post and many others. I have tried many cmaps and interpolation combinations.
This is the code that prepares the data:
def parse_cords(cord: float):
cord = str(cord).split(".")
h_map[int(cord[0])][int(cord[1])] += 1
df["coordinate"] is a pandas series of floats x,y coordinate. x and y are ranging from 0 to 1999.
I have decided to modify the array so that values will range from 0 to 1, but I have tested the code also without changing the range.
h_map = np.zeros((2000, 2000), dtype='int')
cords = df["coordinate"].map(lambda cord: parse_cords(cord))
maximum = float(np.max(h_map))
precent = lambda x: x/maximum
h_map = precent(h_map)
h_map looks like this:
[[0.58396242 0.08840799 0.03153833 ... 0.00285187 0.00419393 0.06324442]
[0.09075658 0.11172622 0.01476262 ... 0.00134206 0.00687804 0.0082201 ]
[0.02986076 0.01862104 0.03959067 ... 0.00100654 0.00134206 0.00251636]
...
[0.00301963 0.00134206 0.00134206 ... 0.00100654 0.00150981 0.00553598]
[0.00419393 0.00268411 0.00100654 ... 0.00201309 0.00402617 0.01342057]
[0.05183694 0.00251636 0.00184533 ... 0.00301963 0.00838785 0.1016608 ]]
Now the plot:
fig, ax = plt.subplots(figsize=figsize)
ax = plt.imshow(h_map)
And result:
final plot
The result is always a heatmap with only a single color depending on the cmap used. Is my array just too big to be plotted like this or am I doing something wrong?
EDIT:
I have added plt.colorbar() and removed scaling from 0 to 1. The plot knows the range of data (0 to 5500) but assumes that every value is equal to 0.
I think that is because you only provide one color channel. Therefore, plt.imshow() interprets the data as black and white image. You could either add more channels or use a different function e.g. sns.heatmap().
from seaborn import sns

how to not color nan values when making a contour plot with matplotlib.collections.PolyCollection

I am trying to plot a tri/quad mesh along with results on that mesh. I am plotting results of a CFD simulation.
I am using matplotlib.collections.PolyCollection to plot because it handles non-tri elements, where other methods only support tri elements.
my current code works fine, but when I try to plot results where some cells have no water (have them set to np.nan right now), the plotting crashes and the contour colors get all screwed up.
My current code is:
ax = plt.subplot(111)
cmap = matplotlib.cm.jet
polys = element_coords #list of Nx2 np.arrays containing the coordinates of each element polygon)
facecolors = element_values #np array of values at each element, same length as polys
pc = matplotlib.collections.PolyCollection(polys, cmap=cmap)
pc.set_array(facecolors)
ax.add_collection(pc)
ax.plot()
When element_values does not contain any nan values, it works fine and looks something like this:
However, when element_values does contain nan values, it crashes and I get this error:
C:\Users\deden\AppData\Local\Continuum\anaconda3\envs\test\lib\site-packages\matplotlib\colors.py:527: RuntimeWarning: invalid value encountered in less
xa[xa < 0] = -1
I played around with element_values and can confirm this only happens when nan values are present.
I initially tried to ignore the nan values by doing this just to make them clear:
pc.cmap.set_bad(color='white',alpha=0)
But I still get the same error.
So... I tried setting all the nan values to -999 then trying to cut off the colormap like this:
vmin = np.nanmin(facecolors)
vmax = np.nanmax(facecolors)
facecolors[np.isnan(facecolors)] = -999
pc.cmap.set_under(color='white',alpha=0)
then tried to set the limits of the colormap based on other stack questions I've seen..like:
pc.cmap.set_clim(vmin,vmax)
but then I get:
AttributeError: 'ListedColormap' object has no attribute 'set_clim'
I'm out of ideas here...can anyone help me? I just want to NOT COLOR any element where the value is nan.
To reproduce my error..you can try using this dummy data:
polys = [np.array([[ 223769.2075899 , 1445713.24572239],
[ 223769.48419606, 1445717.09102757],
[ 223764.48282055, 1445714.84782264]]),
np.array([[ 223757.9584215 , 1445716.57576502],
[ 223764.48282055, 1445714.84782264],
[ 223762.05868674, 1445720.48031478]])]
facecolors = np.array([np.nan, 1]) #will work if you replace np.nan with a number
SIDE NOTE - if anyone knows how I can plot this mesh+data without polycollections that'd be great..it includes 3 and 4 sided mesh elements
Matplotlib's colormapping mechanics come from a time when numpy.nan wasn't around. Instead it works with masked arrays.
facecolors = np.ma.array(facecolors, mask=np.isnan(facecolors))
Concerning the other error you get, note that .set_clim is an attribute of the colorbar, not the colormap.
Finally, if your mesh contained only 3-sided elements, you could use tripcolor, but that won't work with 4-sided meshes.

get the colors assigned by mayavi colormap

I'm using mayavi and plotting my triangular mesh using the scalar attribute in mlab.triangular_mesh
model_plot = mlab.triangular_mesh(self.model.vx, self.model.vy, self.model.vz, self.model.triv,
scalars=self.P_colors[:, np.newaxis],
name='model')
With the resulting
But I would like to change specific values in the mesh (e.g paint the head in green).
For that, I tried to use the LOT of the figure, but I don't understand how to use it (I.e, from scalar X -> to color (R,G,B,A))
model_plot.module_manager.scalar_lut_manager.lut.table = model_colors
The goal is somehow to transfer the (7000) scalar array, to a (7000,4) RGBA array corresponds to the LOT.
Finally, I find the solution of this question.
Just 2 key point:
scalar
"This scalar value can be used to modulate the color and the size of the points."
make a RGBA LUT N*4 e.g. 7000*4
#xyz= N*3 points
#tex = N*3 color_per_point,0~255
#tl = triangle_list of pints, M*3
# N*4 0~255 RGBA color
color=np.hstack((tex,255.*np.ones((xyz.shape[0],1))))
s = np.arange(xyz.shape[0])
fig2=mlab.figure(figure="test2",fgcolor=(0., 0., 0.), bgcolor=(0, 0, 0))
mlab.figure(fig2)
mesh = mlab.triangular_mesh(xyz[:,0], xyz[:,1], xyz[:,2], tl,scalars=s,
representation='wireframe',
opacity=1)
mesh.module_manager.scalar_lut_manager.lut.number_of_colors = len(s)
mesh.module_manager.scalar_lut_manager.lut.table = color
mlab.draw()
mlab.show()
this works
Easiest way would be to filter the head out of the original mesh, and plot it separately and then assign it a single-color-LUT

tripcolor using RGB values for each vertex

I have a 2D triangle mesh with n vertices that is stored in a variable tri (a matplotlib.tri.Triangulation object); I can plot the mesh with matplotlib's tripcolor function easily enough and everything works fine. However, I also have (r,g,b) triples for each vertex (vcolors), and these values do not fall along a single dimension thus can't be easily converted to a color-map (for example, imagine if you overlaid a triangle mesh on a large photo of a park, then assigned each vertex the color of the pixel beneath it).
I thought I would be able to do something like this:
matplotlib.pyplot.tripcolor(tri, vcolors)
ValueError: Collections can only map rank 1 arrays
Is there a convenient way to convert a vcolors-like (n x 3) matrix into something usable by tripcolor? Is there an alternative to tripcolor that accepts vertex colors?
One thing I have tried is to make my own colormap:
z = numpy.asarray(range(len(vcolors)), dtype=np.float) / (len(vcolors) - 1)
cmap = matplotlib.colors.Colormap(vcolors, N=len(vcolors))
matplotlib.pyplot.tripcolor(tri, z, cmap=cmap)
matplotlib.pyplot.show()
This however did nothing---no figure appears and no error is raised; the function returns a figure handle but nothing ever gets rendered (I'm using an IPython notebook). Note that if I call the following, a plot appears just fine:
tripcolor(tri, np.zeros(len(vcolors)))
matplotlib.pyplot.show()
I'm using Python 2.7.
After rooting around in matplotlib's tripcolor and Colormap code, I came up with the following solution, which seems to work only as long as one uses 'gouraud' shading (otherwise, it does a very poor job of deducing the face colors; see below).
The trick is to create a colormap that, when given n evenly spaced numbers between 0 and 1 (inclusive) reproduces the original array of colors:
def colors_to_cmap(colors):
'''
colors_to_cmap(nx3_or_nx4_rgba_array) yields a matplotlib colormap object that, when
that will reproduce the colors in the given array when passed a list of n evenly
spaced numbers between 0 and 1 (inclusive), where n is the length of the argument.
Example:
cmap = colors_to_cmap(colors)
zs = np.asarray(range(len(colors)), dtype=np.float) / (len(colors)-1)
# cmap(zs) should reproduce colors; cmap[zs[i]] == colors[i]
'''
colors = np.asarray(colors)
if colors.shape[1] == 3:
colors = np.hstack((colors, np.ones((len(colors),1))))
steps = (0.5 + np.asarray(range(len(colors)-1), dtype=np.float))/(len(colors) - 1)
return matplotlib.colors.LinearSegmentedColormap(
'auto_cmap',
{clrname: ([(0, col[0], col[0])] +
[(step, c0, c1) for (step,c0,c1) in zip(steps, col[:-1], col[1:])] +
[(1, col[-1], col[-1])])
for (clridx,clrname) in enumerate(['red', 'green', 'blue', 'alpha'])
for col in [colors[:,clridx]]},
N=len(colors))
Again, note that 'gouraud' shading is required for this to work. To demonstrate why this fails, the following code blocks show my particular use case. (I am plotting part of a flattened cortical sheet with a partially transparent data overlay). In this code, there are 40,886 vertices (in the_map.coordinates) and 81,126 triangles (in the_map.indexed_faces); the colors array has shape (40886, 3).
The following code works fine with 'gouraud' shading:
tri = matplotlib.tri.Triangulation(the_map.coordinates[0],
the_map.coordinates[1],
triangles=the_map.indexed_faces.T)
cmap = rgbs_to_cmap(colors)
zs = np.asarray(range(the_map.vertex_count), dtype=np.float) / (the_map.vertex_count - 1)
plt.figure(figsize=(16,16))
plt.tripcolor(tri, zs, cmap=cmap, shading='gouraud')
But without 'gouraud' shading, the face-colors are perhaps being assigned according to the average of their vertices (have not verified this), which is clearly wrong:
plt.figure(figsize=(16,16))
plt.tripcolor(tri, zs, cmap=cmap)
A much simpler way of creating the color map is via from_list:
z = numpy.arange(n)
cmap = matplotlib.colors.LinearSegmentedColormap.from_list(
'mymap', rgb, N=len(rgb)
)
While for the tripcolor function, use of a colormap is obligatory, the PolyCollection and TriMesh classes (from matplotlib.collection) that it calls internally can deal with RGB color arrays as well. I have used the following code, based on the tripcolor source, to draw a triangle mesh with given RGB face colors:
tri = Triangulation(...)
colors = nx3 RGB array
maskedTris = tri.get_masked_triangles()
verts = np.stack((tri.x[maskedTris], tri.y[maskedTris]), axis=-1)
collection = PolyCollection(verts)
collection.set_facecolor(colors)
plt.gca().add_collection(collection)
plt.gca().autoscale_view()
To set colors per vertex (Gouraud shading), use a TriMesh instead (with set_facecolor).

Python scatter plot 2 dimensional array

I'm trying to do something that I think should be pretty straight forward but I can't seem to get it to work.
I'm trying to plot 16 byte values measured over time to see how they change. I'm trying to use a scatter plot to do this with:
x axis being the measurement index
y axis being the index of the byte
and the color indicating the value of the byte.
I have the data stored in a numpy array where data[2][14] would give me the value of the 14th byte in the 2nd measurement.
Every time I try to plot this, I'm getting either:
ValueError: x and y must be the same size
IndexError: index 10 is out of bounds for axis 0 with size 10
Here is the sample test I'm using:
import numpy
import numpy.random as nprnd
import matplotlib.pyplot as plt
#generate random measurements
# 10 measurements of 16 byte values
x = numpy.arange(10)
y = numpy.arange(16)
test_data = nprnd.randint(low=0,high=65535, size=(10, 16))
#scatter plot the measurements with
# x - measurement index (0-9 in this case)
# y - byte value index (0-15 in this case)
# c = test_data[x,y]
plt.scatter(x,y,c=test_data[x][y])
plt.show()
I'm sure it is something stupid I'm doing wrong but I can't seem to figure out what.
Thanks for the help.
Try using a meshgrid to define your point locations, and don't forget to index into your NumPy array properly (with [x,y] rather than [x][y]):
x, y = numpy.meshgrid(x,y)
plt.scatter(x,y,c=test_data[x,y])
plt.show()

Categories