具有多种配色方案的带注释的热图
Annotated heatmap with multiple color schemes
我有以下数据框,想在热图中用不同的配色方案区分每个 "step" 中的小数差异。
示例数据:
Sample Step 2 Step 3 Step 4 Step 5 Step 6 Step 7 Step 8
A 64.847 54.821 20.897 39.733 23.257 74.942 75.945
B 64.885 54.767 20.828 39.613 23.093 74.963 75.928
C 65.036 54.772 20.939 39.835 23.283 74.944 75.871
D 64.869 54.740 21.039 39.889 23.322 74.925 75.894
E 64.911 54.730 20.858 39.608 23.101 74.956 75.930
F 64.838 54.749 20.707 39.394 22.984 74.929 75.941
G 64.887 54.781 20.948 39.748 23.238 74.957 75.909
H 64.903 54.720 20.783 39.540 23.028 74.898 75.911
I 64.875 54.761 20.911 39.695 23.082 74.897 75.866
J 64.839 54.717 20.692 39.377 22.853 74.849 75.939
K 64.857 54.736 20.934 39.699 23.130 74.880 75.903
L 64.754 54.746 20.777 39.536 22.991 74.877 75.902
M 64.798 54.811 20.963 39.824 23.187 74.886 75.895
我正在寻找的示例:
我的第一种方法是基于具有多个子图的图形。图的数量将等于数据框中的列数;地块之间的差距可以缩小到零:
cm = ['Blues', 'Reds', 'Greens', 'Oranges', 'Purples', 'bone', 'winter']
f, axs = plt.subplots(1, df.columns.size, gridspec_kw={'wspace': 0})
for i, (s, a, c) in enumerate(zip(df.columns, axs, cm)):
sns.heatmap(np.array([df[s].values]).T, yticklabels=df.index, xticklabels=[s], annot=True, fmt='.2f', ax=a, cmap=c, cbar=False)
if i>0:
a.yaxis.set_ticks([])
结果:
不确定这是否会产生有用的甚至是自我描述的数据可视化,但这是您的选择 - 也许这有助于开始...
补充:
关于添加彩条:当然可以。但是——除了不知道你的数据背景和可视化的目的——我想补充一些关于这一切的想法:
首先:将所有这些颜色条作为单独的一组条添加到热图的一侧或下方可能是可能的,但我发现已经很难读取数据了,加上:你已经有了所有这些注释——我想它会把一切都搞砸的。
另外:与此同时,@ImportanceOfBeingErnest 就该主题提供了如此漂亮的解决方案,在我看来,这在我看来意义不大。
第二:如果你真的想坚持使用热图,也许拆分并为每一列提供颜色条会更适合:
cm = ['Blues', 'Reds', 'Greens', 'Oranges', 'Purples', 'bone', 'winter']
f, axs = plt.subplots(1, df.columns.size, figsize=(10, 3))
for i, (s, a, c) in enumerate(zip(df.columns, axs, cm)):
sns.heatmap(np.array([df[s].values]).T, yticklabels=df.index, xticklabels=[s], annot=True, fmt='.2f', ax=a, cmap=c)
if i>0:
a.yaxis.set_ticks([])
f.tight_layout()
然而,话虽如此 - 我敢怀疑这是否是您数据的最佳可视化效果。当然,我不知道你想用这些图说、看或发现什么,但这就是重点:如果可视化类型符合需要,我想我会知道(或至少可以想象)。
举个例子:
一个简单的 df.plot()
结果是
我觉得这比热图更能在十分之一秒内说明列的不同特征。
或者您是否明确地关注了每列均值的差异?
(df - df.mean()).plot()
...或者他们周围各列的分布情况?
(df - df.mean()).boxplot()
我想说的是:在你begin/have解释任何事情之前,当一个情节开始讲述底层数据时,数据可视化就会变得强大...
我想这个问题可以分为几个部分。
将不同颜色图的多个热图合并到同一张图片中。这可以通过按列屏蔽整个数组,通过 imshow
分别绘制每个屏蔽数组并应用不同的颜色图来完成。形象化概念:
获取不同数量的不同颜色图。 Matplotlib 提供了大量的颜色图,但是,它们在亮度和饱和度方面通常非常不同。在这里似乎希望有不同色调的色彩映射,但在其他方面具有相同的饱和度和亮度。
一个选项是动态创建颜色图,选择 n
不同(且间距相等)的色调,并使用相同的饱和度和亮度创建颜色图。
为每列获取不同的颜色条。由于列中的值可能采用完全不同的比例,因此需要为每列设置一个颜色条来了解显示的值,例如在第一列中,最亮的颜色可能对应于值 1,而在第二列中,它可能对应于值 100。可以在 GridSpec
的轴内创建多个颜色条,该 GridSpec
位于实际的热图轴。该网格规范的列数和行数将取决于数据框中的列数。
总的来说,这可能如下所示。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib.gridspec import GridSpec
def get_hsvcmap(i, N, rot=0.):
nsc = 24
chsv = mcolors.rgb_to_hsv(plt.cm.hsv(((np.arange(N)/N)+rot) % 1.)[i,:3])
rhsv = mcolors.rgb_to_hsv(plt.cm.Reds(np.linspace(.2,1,nsc))[:,:3])
arhsv = np.tile(chsv,nsc).reshape(nsc,3)
arhsv[:,1:] = rhsv[:,1:]
rgb = mcolors.hsv_to_rgb(arhsv)
return mcolors.LinearSegmentedColormap.from_list("",rgb)
def columnwise_heatmap(array, ax=None, **kw):
ax = ax or plt.gca()
premask = np.tile(np.arange(array.shape[1]), array.shape[0]).reshape(array.shape)
images = []
for i in range(array.shape[1]):
col = np.ma.array(array, mask = premask != i)
im = ax.imshow(col, cmap=get_hsvcmap(i, array.shape[1], rot=0.5), **kw)
images.append(im)
return images
### Create some dataset
ind = list("ABCDEFGHIJKLM")
m = len(ind)
n = 8
df = pd.DataFrame(np.random.randn(m,n) + np.random.randint(20,70,n),
index=ind, columns=[f"Step {i}" for i in range(2,2+n)])
### Plot data
fig, ax = plt.subplots(figsize=(8,4.5))
ims = columnwise_heatmap(df.values, ax=ax, aspect="auto")
ax.set(xticks=np.arange(len(df.columns)), yticks=np.arange(len(df)),
xticklabels=df.columns, yticklabels=df.index)
ax.tick_params(bottom=False, top=False,
labelbottom=False, labeltop=True, left=False)
### Optionally add colorbars.
fig.subplots_adjust(left=0.06, right=0.65)
rows = 3
cols = len(df.columns) // rows + int(len(df.columns)%rows > 0)
gs = GridSpec(rows, cols)
gs.update(left=0.7, right=0.95, wspace=1, hspace=0.3)
for i, im in enumerate(ims):
cax = fig.add_subplot(gs[i//cols, i % cols])
fig.colorbar(im, cax = cax)
cax.set_title(df.columns[i], fontsize=10)
plt.show()
我有以下数据框,想在热图中用不同的配色方案区分每个 "step" 中的小数差异。
示例数据:
Sample Step 2 Step 3 Step 4 Step 5 Step 6 Step 7 Step 8
A 64.847 54.821 20.897 39.733 23.257 74.942 75.945
B 64.885 54.767 20.828 39.613 23.093 74.963 75.928
C 65.036 54.772 20.939 39.835 23.283 74.944 75.871
D 64.869 54.740 21.039 39.889 23.322 74.925 75.894
E 64.911 54.730 20.858 39.608 23.101 74.956 75.930
F 64.838 54.749 20.707 39.394 22.984 74.929 75.941
G 64.887 54.781 20.948 39.748 23.238 74.957 75.909
H 64.903 54.720 20.783 39.540 23.028 74.898 75.911
I 64.875 54.761 20.911 39.695 23.082 74.897 75.866
J 64.839 54.717 20.692 39.377 22.853 74.849 75.939
K 64.857 54.736 20.934 39.699 23.130 74.880 75.903
L 64.754 54.746 20.777 39.536 22.991 74.877 75.902
M 64.798 54.811 20.963 39.824 23.187 74.886 75.895
我正在寻找的示例:
我的第一种方法是基于具有多个子图的图形。图的数量将等于数据框中的列数;地块之间的差距可以缩小到零:
cm = ['Blues', 'Reds', 'Greens', 'Oranges', 'Purples', 'bone', 'winter']
f, axs = plt.subplots(1, df.columns.size, gridspec_kw={'wspace': 0})
for i, (s, a, c) in enumerate(zip(df.columns, axs, cm)):
sns.heatmap(np.array([df[s].values]).T, yticklabels=df.index, xticklabels=[s], annot=True, fmt='.2f', ax=a, cmap=c, cbar=False)
if i>0:
a.yaxis.set_ticks([])
结果:
不确定这是否会产生有用的甚至是自我描述的数据可视化,但这是您的选择 - 也许这有助于开始...
补充:
关于添加彩条:当然可以。但是——除了不知道你的数据背景和可视化的目的——我想补充一些关于这一切的想法:
首先:将所有这些颜色条作为单独的一组条添加到热图的一侧或下方可能是可能的,但我发现已经很难读取数据了,加上:你已经有了所有这些注释——我想它会把一切都搞砸的。
另外:与此同时,@ImportanceOfBeingErnest 就该主题提供了如此漂亮的解决方案,在我看来,这在我看来意义不大。
第二:如果你真的想坚持使用热图,也许拆分并为每一列提供颜色条会更适合:
cm = ['Blues', 'Reds', 'Greens', 'Oranges', 'Purples', 'bone', 'winter']
f, axs = plt.subplots(1, df.columns.size, figsize=(10, 3))
for i, (s, a, c) in enumerate(zip(df.columns, axs, cm)):
sns.heatmap(np.array([df[s].values]).T, yticklabels=df.index, xticklabels=[s], annot=True, fmt='.2f', ax=a, cmap=c)
if i>0:
a.yaxis.set_ticks([])
f.tight_layout()
然而,话虽如此 - 我敢怀疑这是否是您数据的最佳可视化效果。当然,我不知道你想用这些图说、看或发现什么,但这就是重点:如果可视化类型符合需要,我想我会知道(或至少可以想象)。
举个例子:
一个简单的 df.plot()
结果是
我觉得这比热图更能在十分之一秒内说明列的不同特征。
或者您是否明确地关注了每列均值的差异?
(df - df.mean()).plot()
...或者他们周围各列的分布情况?
(df - df.mean()).boxplot()
我想说的是:在你begin/have解释任何事情之前,当一个情节开始讲述底层数据时,数据可视化就会变得强大...
我想这个问题可以分为几个部分。
将不同颜色图的多个热图合并到同一张图片中。这可以通过按列屏蔽整个数组,通过 imshow
分别绘制每个屏蔽数组并应用不同的颜色图来完成。形象化概念:
获取不同数量的不同颜色图。 Matplotlib 提供了大量的颜色图,但是,它们在亮度和饱和度方面通常非常不同。在这里似乎希望有不同色调的色彩映射,但在其他方面具有相同的饱和度和亮度。
一个选项是动态创建颜色图,选择 n
不同(且间距相等)的色调,并使用相同的饱和度和亮度创建颜色图。
为每列获取不同的颜色条。由于列中的值可能采用完全不同的比例,因此需要为每列设置一个颜色条来了解显示的值,例如在第一列中,最亮的颜色可能对应于值 1,而在第二列中,它可能对应于值 100。可以在 GridSpec
的轴内创建多个颜色条,该 GridSpec
位于实际的热图轴。该网格规范的列数和行数将取决于数据框中的列数。
总的来说,这可能如下所示。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib.gridspec import GridSpec
def get_hsvcmap(i, N, rot=0.):
nsc = 24
chsv = mcolors.rgb_to_hsv(plt.cm.hsv(((np.arange(N)/N)+rot) % 1.)[i,:3])
rhsv = mcolors.rgb_to_hsv(plt.cm.Reds(np.linspace(.2,1,nsc))[:,:3])
arhsv = np.tile(chsv,nsc).reshape(nsc,3)
arhsv[:,1:] = rhsv[:,1:]
rgb = mcolors.hsv_to_rgb(arhsv)
return mcolors.LinearSegmentedColormap.from_list("",rgb)
def columnwise_heatmap(array, ax=None, **kw):
ax = ax or plt.gca()
premask = np.tile(np.arange(array.shape[1]), array.shape[0]).reshape(array.shape)
images = []
for i in range(array.shape[1]):
col = np.ma.array(array, mask = premask != i)
im = ax.imshow(col, cmap=get_hsvcmap(i, array.shape[1], rot=0.5), **kw)
images.append(im)
return images
### Create some dataset
ind = list("ABCDEFGHIJKLM")
m = len(ind)
n = 8
df = pd.DataFrame(np.random.randn(m,n) + np.random.randint(20,70,n),
index=ind, columns=[f"Step {i}" for i in range(2,2+n)])
### Plot data
fig, ax = plt.subplots(figsize=(8,4.5))
ims = columnwise_heatmap(df.values, ax=ax, aspect="auto")
ax.set(xticks=np.arange(len(df.columns)), yticks=np.arange(len(df)),
xticklabels=df.columns, yticklabels=df.index)
ax.tick_params(bottom=False, top=False,
labelbottom=False, labeltop=True, left=False)
### Optionally add colorbars.
fig.subplots_adjust(left=0.06, right=0.65)
rows = 3
cols = len(df.columns) // rows + int(len(df.columns)%rows > 0)
gs = GridSpec(rows, cols)
gs.update(left=0.7, right=0.95, wspace=1, hspace=0.3)
for i, im in enumerate(ims):
cax = fig.add_subplot(gs[i//cols, i % cols])
fig.colorbar(im, cax = cax)
cax.set_title(df.columns[i], fontsize=10)
plt.show()