Creé un mapa de calor matplotlib simple donde cada celda será roja o verde si el valor de la celda es mayor que 2.3.

Ahora, en lugar de tener el color simplemente rojo o verde, me gustaría tenerlo más oscuro o más claro de acuerdo a cuánto difiere el valor de la celda de 2.3, por ejemplo 1 tendrá un rojo más oscuro que 2.1.

¿Hay alguna forma de hacer esto? Hasta ahora, solo pude hacerlo binario, gracias a esta pregunta.

ax1 = fig.add_subplot(111)

a = np.array([[0.8, 2.4, 2.5, 3.9],
              [2.4, 0.0, 4.0, 1.0],
              [1.1, 2.4, 0.8, 4.3],
              [0.6, 0.0, 0.3, 0.0],
              [0.7, 1.7, 0.6, 2.6]])

cmap = matplotlib.colors.ListedColormap(['#ff3d3d', '#74ff52'])

bounds = [np.amin(a), 2.3, np.amax(a)]

norm = matplotlib.colors.BoundaryNorm(bounds, cmap.N)

ax1.imshow(a, interpolation='none', cmap=cmap, norm=norm)
0
JayK23 27 ago. 2020 a las 00:35

2 respuestas

La mejor respuesta

Un LinearSegmentedColormap puede crear un mapa de colores de una lista de colores. Ayuda establecer explícitamente un color en el medio (por ejemplo, 'amarillo') para crear una distinción.

Un TwoSlopeNorm puede identificar un valor exacto para el centro.

import matplotlib.pyplot as plt
import matplotlib
from matplotlib.ticker import MultipleLocator
import numpy as np

a = np.array([[0.8, 2.4, 2.5, 3.9],
              [2.4, 0.0, 4.0, 1.0],
              [1.1, 2.4, 0.8, 4.3],
              [0.6, 0.0, 0.3, 0.0],
              [0.7, 1.7, 0.6, 2.6]])
cmap = matplotlib.colors.LinearSegmentedColormap.from_list('', ['#ff3d3d', 'yellow', '#74ff52'])
norm = matplotlib.colors.TwoSlopeNorm(vcenter=2.3, vmin=a.min(), vmax=a.max())

fig, ax = plt.subplots()
img = ax.imshow(a, interpolation='none', cmap=cmap, norm=norm)
ax.xaxis.set_major_locator(MultipleLocator(1))
ax.yaxis.set_major_locator(MultipleLocator(1))
plt.colorbar(img, ax=ax)
plt.tight_layout()
plt.show()

A la izquierda un ejemplo con amarillo como color central, en el centro con blanco y a la derecha sin establecer un color central explícito.

resulting plot

PD: Si el valor central no estuviera entre el mínimo y el máximo, toda la imagen sería roja o verde. En ese caso, podría crear la norma como:

bounds = sorted([2.3,  a.min(), a.max()])
norm = matplotlib.colors.TwoSlopeNorm(vcenter=bounds[1], vmin=bounds[0], vmax=bounds[2])
1
JohanC 26 ago. 2020 a las 22:39

Aquí está mi solución usando la biblioteca seaborn.
(Idealmente, tenga instalado matplotlib 3.1.0 en su PC, ya que matplotlib 3.1.1 puede causar problemas al mostrar el mapa de calor).

import seaborn 
import numpy as np
import matplotlib.pyplot as plt

a = np.array([[0.8, 2.4, 2.5, 3.9],
              [2.4, 0.0, 4.0, 1.0],
              [1.1, 2.4, 0.8, 4.3],
              [0.6, 0.0, 0.3, 0.0],
              [0.7, 1.7, 0.6, 2.6]])

#b = np.array([[1,2],[3,4]])
minVal = np.amin(a)
maxVal = np.amax(a)
diffs = abs(2.3 - a)

seaborn.heatmap(a, annot=True, linewidths=.5, square=True, vmin=np.amin(a), vmax=np.amax(a), cmap='RdYlGn')
plt.figure()
seaborn.heatmap(diffs, annot=True, linewidths=.5, square=True, vmin=np.amin(diffs), vmax=np.amax(diffs), cmap='Reds')

El primer mapa de calor es su matriz 'a' original, que se verá así:
asdf

El segundo mapa de calor representa los valores a difiere de 2.3, que se verá así: asdf

1
Jacob K 26 ago. 2020 a las 22:10