![](/img/trans.png)
[英]How can I get matplotlib to make a colorbar with a color centered on 0?
[英]How can I add a 2D colorbar, or a color wheel, to matplotlib?
我正在分析样品的磁化映射。 获得梯度及其方向后,我将它们绘制为 HSV(从 -π 到 π 的方向映射到色调从 0 到 1,值是归一化梯度)通过img_rgb = mpl.colors.hsv_to_rgb(img_hsv)
。
我设法使用 vmin 和 vmax 添加了一个 HSV 颜色条,但这并没有显示渐变的大小:
plt.imshow(img_rgb, cmap='hsv', vmin=-180, vmax=180, extent=(0, 100, 0,100))
plt.xlabel('μm')
plt.ylabel('μm')
plt.colorbar()
理想情况下,我想添加一个色轮来编码方向和幅度(可能类似于极坐标图?)。 如果这是不可能的,请添加一个 2D 图,它扩展当前颜色条以包括 x 轴上的梯度幅度。
次要情节显然是可能的,但它们似乎是杂七杂八的。 有更好的方法吗?
首先,如果您想同时显示两个不同的参数,您可以通过为它们分配两个不同的通道(例如红色和绿色)来实现。 这可以通过规范化您的两个 2d 数组并将它们提供给imshow
堆叠来imshow
类似于这个答案。
如果您对方形 2d 颜色图meshgrid
,那么您可以通过创建一个meshgrid
,然后再次堆叠并馈送到imshow
以相同的方式获得此颜色imshow
:
from matplotlib import pyplot as plt
import numpy as np
##generating some data
x,y = np.meshgrid(
np.linspace(0,1,100),
np.linspace(0,1,100),
)
directions = (np.sin(2*np.pi*x)*np.cos(2*np.pi*y)+1)*np.pi
magnitude = np.exp(-(x*x+y*y))
##normalize data:
def normalize(M):
return (M-np.min(M))/(np.max(M)-np.min(M))
d_norm = normalize(directions)
m_norm = normalize(magnitude)
fig,(plot_ax, bar_ax) = plt.subplots(nrows=1,ncols=2,figsize=(8,4))
plot_ax.imshow(
np.dstack((d_norm,m_norm, np.zeros_like(directions))),
aspect = 'auto',
extent = (0,100,0,100),
)
bar_ax.imshow(
np.dstack((x, y, np.zeros_like(x))),
extent = (
np.min(directions),np.max(directions),
np.min(magnitude),np.max(magnitude),
),
aspect = 'auto',
origin = 'lower',
)
bar_ax.set_xlabel('direction')
bar_ax.set_ylabel('magnitude')
plt.show()
结果如下所示:
原则上,使用 polar Axes
也应该可以做同样的事情,但根据此 github 票证中的评论, imshow
不支持极轴,我无法让imshow
填满整个光盘。
编辑:
感谢 ImportanceOfBeingErnest 和他对另一个问题的回答( color
关键字做到了),现在这里使用pcolormesh
极轴上的二维颜色pcolormesh
。 有一些警告,最值得注意的是, colors
维度需要在theta
方向上比meshgrid
小一,否则颜色图具有螺旋形式:
fig= plt.figure(figsize=(8,4))
plot_ax = fig.add_subplot(121)
bar_ax = fig.add_subplot(122, projection = 'polar')
plot_ax.imshow(
np.dstack((d_norm,m_norm, np.zeros_like(directions))),
aspect = 'auto',
extent = (0,100,0,100),
)
theta, R = np.meshgrid(
np.linspace(0,2*np.pi,100),
np.linspace(0,1,100),
)
t,r = np.meshgrid(
np.linspace(0,1,99),
np.linspace(0,1,100),
)
image = np.dstack((t, r, np.zeros_like(r)))
color = image.reshape((image.shape[0]*image.shape[1],image.shape[2]))
bar_ax.pcolormesh(
theta,R,
np.zeros_like(R),
color = color,
)
bar_ax.set_xticks(np.linspace(0,2*np.pi,5)[:-1])
bar_ax.set_xticklabels(
['{:.2}'.format(i) for i in np.linspace(np.min(directions),np.max(directions),5)[:-1]]
)
bar_ax.set_yticks(np.linspace(0,1,5))
bar_ax.set_yticklabels(
['{:.2}'.format(i) for i in np.linspace(np.min(magnitude),np.max(magnitude),5)]
)
bar_ax.grid('off')
plt.show()
这产生了这个数字:
在尝试可视化表面梯度的径向和绝对分量时,我遇到了类似的问题。
我正在通过 hsv 将渐变的绝对值加上角度转换为颜色(使用色调作为角度,使用饱和度和值作为绝对值)。 这与磁化图中的相同,因为可以使用任何矢量场代替梯度。 下面的函数说明了这个想法。 答案末尾提供了完整代码。
import matplotlib.colors
# gradabs is the absolute gradient value,
# gradang is the angle direction, z the vector field
# the gradient was calculated of
max_abs = np.max(gradabs)
def grad_to_rgb(angle, absolute):
"""Get the rgb value for the given `angle` and the `absolute` value
Parameters
----------
angle : float
The angle in radians
absolute : float
The absolute value of the gradient
Returns
-------
array_like
The rgb value as a tuple with values [0..1]
"""
global max_abs
# normalize angle
angle = angle % (2 * np.pi)
if angle < 0:
angle += 2 * np.pi
return matplotlib.colors.hsv_to_rgb((angle / 2 / np.pi,
absolute / max_abs,
absolute / max_abs))
# convert to colors via hsv
grad = np.array(list(map(grad_to_rgb, gradang.flatten(), gradabs.flatten())))
# reshape
grad = grad.reshape(tuple(list(z.shape) + [3]))
结果图如下。
显示表面梯度场的完整示例代码:
import numpy as np
import matplotlib.colors
import matplotlib.pyplot as plt
r = np.linspace(0, np.pi, num=100)
x, y = np.meshgrid(r, r)
z = np.sin(y) * np.cos(x)
fig = plt.figure()
ax = fig.add_subplot(1, 3, 1, projection='3d')
ax.plot_surface(x, y, z)
# ax.imshow(z)
ax.set_title("Surface")
ax = fig.add_subplot(1, 3, 2)
ax.set_title("Gradient")
# create gradient
grad_y, grad_x = np.gradient(z)
# calculate length
gradabs = np.sqrt(np.square(grad_x) + np.square(grad_y))
max_abs = np.max(gradabs)
# calculate angle component
gradang = np.arctan2(grad_y, grad_x)
def grad_to_rgb(angle, absolute):
"""Get the rgb value for the given `angle` and the `absolute` value
Parameters
----------
angle : float
The angle in radians
absolute : float
The absolute value of the gradient
Returns
-------
array_like
The rgb value as a tuple with values [0..1]
"""
global max_abs
# normalize angle
angle = angle % (2 * np.pi)
if angle < 0:
angle += 2 * np.pi
return matplotlib.colors.hsv_to_rgb((angle / 2 / np.pi,
absolute / max_abs,
absolute / max_abs))
# convert to colors via hsv
grad = np.array(list(map(grad_to_rgb, gradang.flatten(), gradabs.flatten())))
# reshape
grad = grad.reshape(tuple(list(z.shape) + [3]))
ax.imshow(grad)
n = 5
gx, gy = np.meshgrid(np.arange(z.shape[0] / n), np.arange(z.shape[1] / n))
ax.quiver(gx * n, gy * n, grad_x[::n, ::n], grad_y[::n, ::n])
# plot color wheel
# Generate a figure with a polar projection, inspired by
# https://stackoverflow.com/a/48253413/5934316
ax = fig.add_subplot(1, 3, 3, projection='polar')
n = 200 # the number of secants for the mesh
t = np.linspace(0, 2 * np.pi, n)
r = np.linspace(0, max_abs, n)
rg, tg = np.meshgrid(r, t)
c = np.array(list(map(grad_to_rgb, tg.T.flatten(), rg.T.flatten())))
cv = c.reshape((n, n, 3))
m = ax.pcolormesh(t, r, cv[:,:,1], color=c, shading='auto')
m.set_array(None)
ax.set_yticklabels([])
plt.show()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.