Issue
I am trying to plot different series in a 3D scatter plot with a colormap that is normalized between the minimum and maximum values from the different series combined. The problem is that the colormap values of each series are normalized individually in an undesired manner. That is each series local minimum and maximum values are expanded to the global minimum and maximum values of the colormap. The test code below illustrates the issue (see image attached). The minimum and maximum values for the colormap are 0 and 100, but series 1 which only ranges from 0 to 50 has the same colors as series 3 which actually ranges from 0 to 100. Finally for series that have only 1 value (e.g. series 2 with all values = 50) then the color is set to the minimum value. I tried doing the normalization myself before sending the data to the colormap (commented off), since it only accepts values between 0 and 1, but it yields the same result.
Thanks in advance for any help with that.
'Create test dataframes'
n=11
x1, x2, x3 = np.arange(n), np.arange(n)+10, np.arange(n)+5
y1, y2, y3 = np.arange(n)*2, np.arange(n)*2+10, np.arange(n)*2+5
z1, z2, z3 = np.arange(n)*3, np.arange(n)*3+10, np.arange(n)*3+5
cm1 = np.arange(n)*5
cm2 = np.ones(n)*50
cm3 = np.arange(n)*100
'Create list of inputs'
l_x = [x1, x2, x3]
l_y = [y1, y2, y3]
l_z = [z1, z2, z3]
l_cm = [cm1, cm2, cm3]
l_ax_labels = ['x', 'y', 'z', 'cm']
legend = ['case 1', 'case 2', 'case 3']
'Create figure and axis objects'
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
markers = ['o', 'v', 'x']
'Temperature colormap parameters'
cmap = plt.cm.get_cmap(name='jet')
vmin = 0
vmax = 100
norm = mat.colors.Normalize(vmin=vmin, vmax=vmax)
map = mat.cm.ScalarMappable(norm=norm, cmap=cmap)
font = mat.font_manager.FontProperties(size=14)
'colormap axis properties'
cb = plt.colorbar(map, ax=ax, label=l_ax_labels[3])
cb.ax.yaxis.label.set_font_properties(font)
cb.ax.tick_params(labelsize=14)
'Plot different series'
for i in range(len(l_x)):
'Commented lines perform data normalization before sending, but yield same result'
# l_cm[i] = (l_cm[i] - vmin) / (vmax - vmin)
ax.scatter(l_x[i], l_y[i], l_z[i], marker=markers[i], c=l_cm[i], cmap=cmap)
'Fill in axis titles'
ax.set_xlabel(l_ax_labels[0], fontsize=10)
ax.set_ylabel(l_ax_labels[1], fontsize=10)
ax.set_zlabel(l_ax_labels[2], fontsize=10)
'Legend() requires a dummy plot since function does not support type returned by 3D scatter.'
l_scat_prox = []
for i in range(len(l_x)):
scat_proxy = mat.lines.Line2D([0],[0], linestyle="none", marker=markers[i])
l_scat_prox.append(scat_proxy)
ax.legend(l_scat_prox, legend, numpoints=1)#, loc=2, borderaxespad=-6., fontsize=fontsize_leg)
plt.show()
Solution
You want to set the vmin
and vmax
values when you use scatter. If you set those to 0 and 100 for all three plots, they will not be automatically scaled. From the docs:
vmin
,vmax
float, default: None
vmin
andvmax
are used in conjunction with the defaultnorm
to map the color arrayc
to the colormapcmap
. IfNone
, the respective min and max of the color array is used.
As you can see, if you don't set them, it will scale to the min and max of the color array, which is what is happening in your plot. So, for your case, you can just add vmin
and vmax
like so:
ax.scatter(l_x[i], l_y[i], l_z[i], marker=markers[i], c=l_cm[i],
cmap=cmap, vmin=0, vmax=100)
Answered By - tmdavison
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.