离散颜色条缺少一种颜色

Discrete colorbar is missing a color

Matplotlibs 离散颜色条在我的颜色图中缺少一种颜色定义,并且也在绘图中使用。

在我的示例代码中,我有七种颜色,但颜色条只显示六种颜色,尽管创建颜色图和颜色条的代码似乎与我在 Internet 上找到的示例相同。缺少带有标签“180”的红色。即使我更改了边界并勾选了颜色栏中的米色或浅蓝色。

import matplotlib.pyplot as plt
import matplotlib
import numpy as np
import pandas as pd

# 4 marker
# 7 color
n=100
c = np.random.randint(1,8,size=n)
m = np.random.randint(1,5,size=n)
x = np.random.uniform(size=n)
y = np.random.uniform(size=n)

d_data = {'P':x, 'f':y, 'node':c, 'arch':m}
df = pd.DataFrame(d_data)

# Creating a unique list of elements
l_arch = df.arch.unique() 
l_node = df.node.unique()  

# Sorting is needd for good colormap
l_arch.sort()
l_node.sort()

# Creating a markers dictionary
zti_markers = ["v","^","s","o","x","+","D"]
d_marker = dict(zip(l_arch,zti_markers[:len(l_arch)] ))

# Creating a colormap and a color dictionary; A little cheat here: I know how 
many different colors I need.
color_list = ['#a6cee3','#1f78b4','#b2df8a','#33a02c','#fb9a99','#e31a1c','#fdbf6f']
cmap = matplotlib.colors.ListedColormap(color_list)
norm = matplotlib.colors.BoundaryNorm(l_node, cmap.N)
d_color = dict(zip(l_node, color_list))

fig, ax = plt.subplots()

df['color']  = df['node'].apply(lambda x: d_color[x])
df['marker'] = df['arch'].apply(lambda x: d_marker[x])

for idx, row in df.iterrows():
    ax.scatter(row['P'], row['f'], color=row['color'], marker=row['marker'])

cax, _ = matplotlib.colorbar.make_axes(ax)
cb     = matplotlib.colorbar.ColorbarBase(cax, cmap=cmap, norm=norm, 
spacing='uniform', orientation='vertical', extend='neither') #, ticks=l_node, 
boundaries=l_node)
# cb     = matplotlib.colorbar.ColorbarBase(cax, cmap=cmap, norm=norm, 
spacing='uniform', orientation='vertical', extend='neither', ticks=l_node, boundaries=l_node)
# cb     = matplotlib.colorbar.ColorbarBase(cax, cmap=cmap, norm=norm, 
spacing='uniform', orientation='vertical', extend='neither', boundaries=[i-0.5 for i in l_node])
cb.set_ticklabels(['22','38','45','65','90','130','180'])
cb.set_ticks([0.5,1.5,2.5,3.5,4.5,5.5,6.5],update_ticks=True)
# cb.update_ticks()
cb.set_label('colorbar', rotation=90)
print(plt.gci()) # --> None
# gci(): Get the current colorable artist. Specifically, returns the current ScalarMappable instance (image or patch collection), or None if no images or patch collections have been defined.
plt.show()    

如何修复颜色栏以包含缺失的红色?

问题是您需要用一个额外的值 0:

来定义 BoundaryNorm
norm = matplotlib.colors.BoundaryNorm([0] + list(l_node), cmap.N)

(如果你有7种颜色,你需要8个边界)。或者更一般地说:

norm = matplotlib.colors.BoundaryNorm([l_node[0]-1] + list(l_node), cmap.N)

输出:

顾名思义,BoundaryNorm定义了颜色映射的边界。您需要比颜色多一个边界。例如,如果您希望将 20 到 50 之间的所有值映射到颜色图的第一种颜色,并将 50 到 60 之间的所有值映射到颜色图的第二种颜色,则需要 BoundaryNorm([20,50,60], 2).

在您的情况下,您实际上并未执行任何映射,因此您需要做的就是确保边界数比颜色数多 1。

norm = matplotlib.colors.BoundaryNorm(np.arange(len(l_node)+1), cmap.N)

如果你想在某处实际使用映射,你可以定义

norm = matplotlib.colors.BoundaryNorm(np.arange(len(l_node)+1)-0.5, cmap.N)

并在

中使用它
ax.scatter(..., color=cmap(norm(row['node'])), )

我会在这里提供后者的完整代码,其中我也简化了一些东西,

import matplotlib.pyplot as plt
import matplotlib
import numpy as np
import pandas as pd

# 4 marker
# 7 color
n=100
c = np.random.randint(1,8,size=n)
m = np.random.randint(1,5,size=n)
x = np.random.uniform(size=n)
y = np.random.uniform(size=n)

d_data = {'P':x, 'f':y, 'node':c, 'arch':m}
df = pd.DataFrame(d_data)

# Creating a unique list of elements
l_arch = df.arch.unique() 
l_node = df.node.unique()

# Sorting is needd for good colormap
l_arch.sort()
l_node.sort()

# Creating a markers dictionary
zti_markers = ["v","^","s","o","x","+","D"]
d_marker = dict(zip(l_arch,zti_markers[:len(l_arch)] ))

# Creating a colormap and a color dictionary; A little cheat here: I know how 
#many different colors I need.
color_list = ['#a6cee3','#1f78b4','#b2df8a','#33a02c','#fb9a99','#e31a1c','#fdbf6f']
cmap = matplotlib.colors.ListedColormap(color_list)
norm = matplotlib.colors.BoundaryNorm(np.arange(len(l_node)+1)-0.5, cmap.N)
d_color = dict(zip(l_node, color_list))

fig, ax = plt.subplots()

df['marker'] = df['arch'].apply(lambda x: d_marker[x])

for idx, row in df.iterrows():
    ax.scatter(row['P'], row['f'], color=cmap(norm(row['node'])), marker=row['marker'])

sm = matplotlib.cm.ScalarMappable(cmap=cmap, norm=norm)
cb = fig.colorbar(sm, spacing='uniform', extend='neither')

cb.set_ticklabels(['22','38','45','65','90','130','180'])
cb.set_ticks(np.arange(len(l_node)), update_ticks=True)

cb.set_label('colorbar', rotation=90)

plt.show()

以上假定 "nodes" 是从 0 开始的后续整数。如果不是这种情况,则定义边界会稍微复杂一些,例如取中间值,

import matplotlib.pyplot as plt
import matplotlib
import numpy as np
import pandas as pd

# 4 marker
# 7 color
n=100
c = np.random.choice([5,8,19,23,44,61,87], size=n)
m = np.random.randint(1,5,size=n)
x = np.random.uniform(size=n)
y = np.random.uniform(size=n)

d_data = {'P':x, 'f':y, 'node':c, 'arch':m}
df = pd.DataFrame(d_data)

# Creating a unique list of elements
l_arch = df.arch.unique() 
l_node = df.node.unique()

# Sorting is needd for good colormap
l_arch.sort()
l_node.sort()

# Creating a markers dictionary
zti_markers = ["v","^","s","o","x","+","D"]
d_marker = dict(zip(l_arch,zti_markers[:len(l_arch)] ))

# Creating a colormap and a color dictionary; A little cheat here: I know how 
#many different colors I need.
color_list = ['#a6cee3','#1f78b4','#b2df8a','#33a02c','#fb9a99','#e31a1c','#fdbf6f']
cmap = matplotlib.colors.ListedColormap(color_list)
bounds = np.concatenate(([l_node[0]-1], l_node[:-1] + np.diff(l_node)/2,[l_node[-1]+1] ))
norm = matplotlib.colors.BoundaryNorm(bounds, cmap.N)
d_color = dict(zip(l_node, color_list))

fig, ax = plt.subplots()

df['marker'] = df['arch'].apply(lambda x: d_marker[x])

for idx, row in df.iterrows():
    ax.scatter(row['P'], row['f'], color=cmap(norm(row['node'])), marker=row['marker'])

sm = matplotlib.cm.ScalarMappable(cmap=cmap, norm=norm)
cb = fig.colorbar(sm, spacing='uniform', extend='neither')

cb.set_ticklabels(['22','38','45','65','90','130','180'])
cb.set_ticks(bounds[:-1]+np.diff(bounds)/2, update_ticks=True)

cb.set_label('colorbar', rotation=90)

plt.show()