На дискретном цветовом шкале отсутствует цвет - PullRequest
1 голос
/ 05 июня 2019

На дискретной цветовой панели Matplotlibs отсутствует один цвет, определенный в моей карте цветов, а также используемый на графике.

В моем примере кода у меня есть семь цветов, но цветовая панель показывает только шесть цветов, хотя код для создания цветовой карты и цветовой полосы кажется идентичным примерам, которые я нашел в Интернете.Красный цвет с меткой «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()    

Как исправить цветную полосу, чтобы включить также отсутствующий красный цвет?

Ответы [ 2 ]

2 голосов
/ 05 июня 2019

BoundaryNorm, как следует из названия, определяет границы для цветовой карты. Вам нужно иметь еще одну границу, чем цвета. Например, если вы хотите, чтобы все значения от 20 до 50 были сопоставлены первому цвету цветовой карты, а все значения от 50 до 60 - второму цвету цветовой карты, вам потребуется BoundaryNorm([20,50,60], 2).

В вашем случае вы фактически не выполняете никакого отображения, поэтому все, что вам нужно сделать, это убедиться, что количество границ на единицу больше, чем количество цветов.

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

enter image description here

Если вместо этого вы действительно хотите использовать отображение где-то, вы можете определить

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()
1 голос
/ 05 июня 2019

Проблема в том, что вам нужно определить BoundaryNorm с одним дополнительным значением 0:

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)

Выход:

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...