首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何创建从二维彩色地图绘制其颜色的二维直方图?

如何创建从二维彩色地图绘制其颜色的二维直方图?
EN

Stack Overflow用户
提问于 2022-03-07 16:29:59
回答 1查看 1.2K关注 0票数 1

老问题:如何在matplotlib中创建一个具有恒定亮度的HSL颜色图?

根据matplotlib的彩色地图文档的说法,它们的默认共垫的亮度值不是常数。但是,我想从HSL颜色空间创建一个颜色图,它具有恒定的亮度。我怎么能这么做?

一般情况下,创建你自己的网格并不难,我不知道如何做到这一点,同时满足轻盈的标准。也许这可以通过从彩色地图文档中逆向工程代码来完成?

解决方案

我想我找到了一种基于这个职位的方法。首先,在HSL颜色空间工作对我的总体目标来说并不是最好的主意,所以我转而使用HSV。这样,我就可以从matplotlib加载首选颜色映射,从它创建一组RGB颜色,将它们转换为HSV,设置它们的颜色值常数,将它们转换回RGB,最后再从它们创建一个颜色映射(例如,我可以使用它来进行2d直方图)。

背景

我需要一个HSV的颜色映射与一个恒定的颜色值,因为然后我可以唯一地映射颜色到RGB空间,从托盘是跨越色调和饱和度。这反过来将允许我创建一个2d直方图,在那里,我可以对计数(通过饱和度)和第三个变量(通过色调)进行彩色编码。

例如,在下面的MWE中(与这里略有不同),颜色值不变的彩色地图,在每个bin i中,可以使用饱和度来表示计数的数量(例如,颜色越轻,数目越低),并使用色调来表示平均z值。这将使我基本上可以把下面的两个情节合并成一个。(在2d直方图中也有添加alpha值的本教程,但在这种情况下,我认为这是行不通的。)

目前,您仍然需要这两幅图才能获得完整的图片,因为如果没有直方图,您将无法判断一个bin中的某个z值可能有多重要,因为同一颜色是独立于对其贡献的数据点使用的(因此,从颜色判断,一个只有一个数据点的bin看起来可能与具有相同颜色的bin一样重要,但包含更多的数据点;因此存在偏向于离群者的偏见)。

代码语言:javascript
复制
import matplotlib.pyplot as plt
import numpy as np


# make data: correlated + noise
n = 1000
x, y = np.random.uniform(-2, 2, (2, n))
z = np.sqrt(x**2 + y**2) + np.random.uniform(0, 1, n)

bins = 20
fig, axs = plt.subplots(1, 2, figsize=(7, 3), constrained_layout=True)
_, _, _, img = axs[0].hist2d(x, y, bins=bins)
fig.colorbar(img, ax=axs[0])
axs[0].set(xlabel='x', ylabel='y', title='histogram')

sums, xbins, ybins = np.histogram2d(x, y, bins=bins, weights=z)
counts, _, _ = np.histogram2d(x, y, bins=bins)
with np.errstate(divide='ignore', invalid='ignore'):
    # suppress possible divide-by-zero warnings
    img = axs[1].pcolormesh(xbins, ybins, sums / counts, cmap='inferno')
fig.colorbar(img, ax=axs[1], label='z')
axs[1].set(xlabel='x', ylabel='y', title='weighed by z')
fig.show()

问题的其余部分

现在我找到了一种方法来创建具有恒定颜色值的渐变图,剩下的就是如何从二维颜色图中绘制二维直方图。因为2d直方图创建了一个QuadMesh的实例,而且很明显您可以设置它的外观颜色,也许这是一种方法,但我还没有弄清楚如何设置它。下面是我至少创建2d彩色地图的实现:

代码语言:javascript
复制
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
from matplotlib.colors import hsv_to_rgb, rgb_to_hsv, ListedColormap

# make data: correlated + noise
np.random.seed(100)
n = 1000
x, y = np.random.uniform(-2, 2, (2, n))
z = np.sqrt(x**2 + y**2) + np.random.uniform(0, 1, n)

bins = 20
fig, axs = plt.subplots(1, 3, figsize=(8, 3), constrained_layout=True)
_, _, _, img = axs[0].hist2d(x, y, bins=bins)
fig.colorbar(img, ax=axs[0], label='N')
axs[0].set(xlabel='x', ylabel='y', title='histogram')

# creating the colormap
inferno = cm.get_cmap('inferno')
hsv_inferno = rgb_to_hsv(inferno(np.linspace(0, 1, 300))[:, :3])
hsv_inferno[:, 2] = 1
rgb_inferno = hsv_to_rgb(hsv_inferno)

# plotting the data
sums, xbins, ybins = np.histogram2d(x, y, bins=bins, weights=z)
counts, _, _ = np.histogram2d(x, y, bins=bins)
with np.errstate(divide='ignore', invalid='ignore'):
    # suppress possible divide-by-zero warnings
    img = axs[1].pcolormesh(
        xbins, ybins, sums / counts, cmap=ListedColormap(rgb_inferno)
    )
axs[1].set(xlabel='x', ylabel='y', title='weighed by z')

# adding the custom colorbar
S, H = np.mgrid[0:1:100j, 0:1:300j]
V = np.ones_like(S)
HSV = np.dstack((H, S, V))
HSV[:, :, 0] = hsv_inferno[:, 0]
# HSV[:, :, 2] = hsv_inferno[:, 2]
RGB = hsv_to_rgb(HSV)
z_min, z_max = np.min(img.get_array()), np.max(img.get_array())
c_min, c_max = np.min(counts), np.max(counts)
axs[2].imshow(
    np.rot90(RGB), origin='lower', extent=[c_min, c_max, z_min, z_max],
    aspect=14
)
axs[2].set_xlabel("N")
axs[2].set_ylabel("z")
axs[2].yaxis.set_label_position("right")
axs[2].yaxis.tick_right()

# readjusting the axes a bit
fig.show()  # necessary to get the proper positions
pos = axs[1].get_position()
pos.x0 += 0.065
pos.x1 += 0.065
axs[1].set_position(pos)
fig.show()

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-03-09 17:48:11

我想到的是在你已经定义的2D颜色空间中进行插值。使用n=100000在上一个示例之后运行下面的代码,以获得更平滑的图像。

代码语言:javascript
复制
from scipy import interpolate 

z = np.divide(sums, counts, where=counts != 0);
points = np.mgrid[
    0:np.max(counts):1j*RGB.shape[0], # use counts for the first axis
    0:np.max(z):1j*RGB.shape[1], # use sum in for the second axis
]
# arrange points in a N x 2 array
points = np.stack(points, axis=2).reshape(-1, 2)
# arrange the colors in a N x 3 array
values =  RGB.reshape(-1, 3) # use your 2D colormap as values

# Creates an interpolator from (..., 2) to (..., 3)
cmap2d = interpolate.LinearNDInterpolator(
    points, values
)
# stack counts and sums in an array of (n1, n2, 2)
cpoints = np.stack([counts, z], axis=2)
# gets an (n1, n2, 3) array
img = cmap2d(cpoints)
# plot the img as a RGB image
plt.imshow(img, extent=[xbins[0], xbins[-1], ybins[0], ybins[-1]])

这就是你得到的

对于对数比例尺,您可以将对数应用到限制值上,但要使用相同的空间网格。插值时使用坐标的对数。

代码语言:javascript
复制
from scipy import interpolate 

z = np.divide(sums, counts, where=counts != 0);
points = np.mgrid[
    # apply log to the limits from 1/e to max(count)
    -1:np.log(np.max(counts)):1j*RGB.shape[0], # use counts for the first axis
    0:np.max(z):1j*RGB.shape[1], # use sum in for the second axis
]
# arrange points in a N x 2 array
points = np.stack(points, axis=2).reshape(-1, 2)
# arrange the colors in a N x 3 array
values =  RGB.reshape(-1, 3) # use your 2D colormap as values

# Creates an interpolator from (..., 2) to (..., 3)
cmap2d = interpolate.LinearNDInterpolator(
    points, values
)
# stack counts and sums in an array of (n1, n2, 2)
# apply log to the values
cpoints = np.stack([np.log(np.maximum(counts, 1)), z], axis=2)
# gets an (n1, n2, 3) array
img = cmap2d(cpoints)
# plot the img as a RGB image
plt.imshow(img, extent=[xbins[0], xbins[-1], ybins[0], ybins[-1]])

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71384228

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档