valores discrepantes específicos em um mapa de calor - matplotlib
-
24-12-2019 - |
Pergunta
Estou gerando um mapa de calor com dados que possuem um número discrepante fixo e preciso mostrar esses valores discrepantes como uma cor da paleta de cores do cmap que uso, que é "quente".Com o uso de cmap.set_bad('green') e np.ma.masked_values(data, outlier), obtenho um gráfico que parece correto, mas a barra de cores não está sendo sincronizada corretamente com os dados, mesmo se eu usar cmap.set_over ('verde').Aqui está o código que estou tentando:
plt.xlim(0,35)
plt.ylim(0,35)
img=plt.imshow(data, interpolation='none',norm=norm, cmap=cmap,vmax=outlier)
cb_ax=fig.add_axes([0.85, 0.1, 0.03, 0.8])
cb=mpl.colorbar.ColorbarBase(cb_ax,cmap=cmap,norm=norm,extend='both',spacing='uniform')
cmap.set_over('green')
cmap.set_under('green')
Aqui estão os dados (o valor discrepante é 1,69, obviamente):
Data;A;B;C;D;E;F;G;H;I;J;K
A;1.2;0;0;0;0;1.69;0;0;1.69;1.69;0
B;0;0;0;0;0;1.69;0;0;1.69;1.69;0
C;0;0;0;0;0;1.69;0;0.45;1.69;1.69;0.92
D;1;0;-0.7;-1.2;0;1.69;0;0;1.69;1.69;0
E;0;0;0;0;0;1.69;0;0;1.69;1.69;0
F;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69
G;0;0;0;0;0;1.69;0;0;1.69;1.69;0
H;0;0;0;0;0;1.69;0;0;1.69;1.69;0
I;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69
J;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69
K;0;0;0;0;0;1.69;0;0;1.69;1.69;0
Agradeço qualquer ajuda
Solução
O que está acontecendo é que você está usando um array mascarado onde os valores discrepantes estão mascarados.
Portanto, eles não aparecem na barra de cores como "acabados".(ou seja,no que diz respeito ao matplotlib, os valores mascarados são inválidos, não ultrapassam o limite)
Como exemplo independente para reproduzir seu problema:
import numpy as np
import matplotlib.pyplot as plt
threshold = 0.8
data = np.random.random((10,10))
data = np.ma.masked_greater(data, threshold)
fig, ax = plt.subplots()
im = ax.imshow(data, cmap=plt.cm.hot, interpolation='none')
cbar = fig.colorbar(im, extend='max')
cbar.cmap.set_over('green')
plt.show()
Se simplesmente não transformarmos isso em um array mascarado e, em vez disso, especificarmos o vmax
kwarg para imshow
:
import numpy as np
import matplotlib.pyplot as plt
threshold = 0.8
data = np.random.random((10,10))
fig, ax = plt.subplots()
im = ax.imshow(data, cmap=plt.cm.hot, interpolation='none', vmax=threshold)
cbar = fig.colorbar(im, extend='max')
cbar.cmap.set_over('green')
plt.show()
Basicamente, esta é a diferença entre set_over
(ou abaixo) e set_bad
.
Se você ainda quiser usar um array mascarado, basta ligar cbar.cmap.set_bad('green')
assim como set_over
, e você obteria o efeito desejado (embora todos os valores "ruins", e não apenas aqueles acima do limite, fossem verdes).Se você seguir esse caminho, precisará especificar manualmente o vmax
.Caso contrário, será considerado o máximo das porções não mascaradas do array.
Outras dicas
Eu acho que você precisa definir extend
para "both"
e alimentar em um Normalize
objeto:
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import pandas
from io import StringIO # python 3
#from StringIO import StringIO # python 2
datastring = StringIO("""\
Data;A;B;C;D;E;F;G;H;I;J;K
A;1.2;0;0;0;0;1.69;0;0;1.69;1.69;0
B;0;0;0;0;0;1.69;0;0;1.69;1.69;0
C;0;0;0;0;0;1.69;0;0.45;1.69;1.69;0.92
D;1;0;-0.7;-1.2;0;1.69;0;0;1.69;1.69;0
E;0;0;0;0;0;1.69;0;0;1.69;1.69;0
F;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69
G;0;0;0;0;0;1.69;0;0;1.69;1.69;0
H;0;0;0;0;0;1.69;0;0;1.69;1.69;0
I;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69
J;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69;1.69
K;0;0;0;0;0;1.69;0;0;1.69;1.69;0
""")
threshold = 1.68
data = pandas.read_table(datastring, sep=';', index_col='Data')
cmap = mpl.cm.coolwarm
norm = mpl.colors.Normalize(vmin=-1 * threshold, vmax=threshold)
cmap.set_over('slategray')
cmap.set_under('forestgreen')
fig, ax = plt.subplots()
ax.set_aspect('equal')
cb_ax=fig.add_axes([0.85, 0.1, 0.03, 0.8])
img = ax.imshow(data, cmap=cmap, norm=norm, interpolation='none')
cb = mpl.colorbar.ColorbarBase(cb_ax, cmap=cmap, norm=norm, extend='both')
Me dá: