[英]How can I add a 2D colorbar, or a color wheel, to matplotlib?
I am analyzing the magnetization mapping of a sample.我正在分析样品的磁化映射。 After getting the gradient and its direction, I plotted them as an HSV (the direction from -π to π was mapped to Hue from 0 to 1, and Value was the normalized gradient) converted to RGB by img_rgb = mpl.colors.hsv_to_rgb(img_hsv)
.获得梯度及其方向后,我将它们绘制为 HSV(从 -π 到 π 的方向映射到色调从 0 到 1,值是归一化梯度)通过img_rgb = mpl.colors.hsv_to_rgb(img_hsv)
。
I managed to add an HSV colorbar by using vmin and vmax, but this does not show the magnitude of the gradient:我设法使用 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()
Ideally, I would like to add a color wheel which encodes both the direction and the magnitude (maybe as something like a polar plot?).理想情况下,我想添加一个色轮来编码方向和幅度(可能类似于极坐标图?)。 If that is not possible, adding a 2D plot which extends the current colorbar to include the gradient magnitude on the x-axis.如果这是不可能的,请添加一个 2D 图,它扩展当前颜色条以包括 x 轴上的梯度幅度。
Subplots are obviously possible, but they seem like a kludge.次要情节显然是可能的,但它们似乎是杂七杂八的。 Is there a better way?有更好的方法吗?
First off, if you have two different parameters that you want to visualise simultaneously, you can do that by assigning two different channels to them (say red and green).首先,如果您想同时显示两个不同的参数,您可以通过为它们分配两个不同的通道(例如红色和绿色)来实现。 This can be done by normalising your two 2d arrays and feeding them to imshow
stacked similarly to this answer .这可以通过规范化您的两个 2d 数组并将它们提供给imshow
堆叠来imshow
类似于这个答案。
If you are content with a square-shaped 2d colormap, you can then get this colormap in the same way, by creating a meshgrid
that you then again stack and feed to 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()
The result looks like this:结果如下所示:
In principle the same thing should also be doable with a polar Axes
, but according to a comment in this github ticket , imshow
does not support polar axes and I couldn't make imshow
fill the entire disc.原则上,使用 polar Axes
也应该可以做同样的事情,但根据此 github 票证中的评论, imshow
不支持极轴,我无法让imshow
填满整个光盘。
EDIT :编辑:
Thanks to ImportanceOfBeingErnest and his answer to another question (the color
keyword did it), here now a 2d colormap on a polar axis using pcolormesh
.感谢 ImportanceOfBeingErnest 和他对另一个问题的回答( color
关键字做到了),现在这里使用pcolormesh
极轴上的二维颜色pcolormesh
。 There were a few caveats, most notable, the colors
dimension needs to be one smaller than the meshgrid
in theta
direction, otherwise the colormap has a spiral form:有一些警告,最值得注意的是, 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()
This produces this figure:这产生了这个数字:
I am having a similar problem when trying to visualize the radial and absolute components of a surface gradient.在尝试可视化表面梯度的径向和绝对分量时,我遇到了类似的问题。
I am converting the absolute value of the gradient plus the angle to a color via hsv (using the hue as the angle and the saturation and the value as the absolute value).我正在通过 hsv 将渐变的绝对值加上角度转换为颜色(使用色调作为角度,使用饱和度和值作为绝对值)。 This is the same as in magnetization plots as any vector field can be used replacing the gradient.这与磁化图中的相同,因为可以使用任何矢量场代替梯度。 The following function illustrates the idea.下面的函数说明了这个想法。 The full code is provided in the end of the answer.答案末尾提供了完整代码。
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]))
The resulting graph is the following.结果图如下。
The full example code on showing the gradient field of a surface:显示表面梯度场的完整示例代码:
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.