BoundaryNorm
, как следует из названия, определяет границы для цветовой карты. Вам нужно иметь еще одну границу, чем цвета. Например, если вы хотите, чтобы все значения от 20 до 50 были сопоставлены первому цвету цветовой карты, а все значения от 50 до 60 - второму цвету цветовой карты, вам потребуется BoundaryNorm([20,50,60], 2)
.
В вашем случае вы фактически не выполняете никакого отображения, поэтому все, что вам нужно сделать, это убедиться, что количество границ на единицу больше, чем количество цветов.
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()
Выше предполагается, что "узлы" являются последующими целыми числами, начиная с 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()