Observação
Clique aqui para baixar o código de exemplo completo
Criando mapas de cores no Matplotlib #
O Matplotlib possui vários mapas de cores integrados acessíveis via
matplotlib.colormaps
. Existem também bibliotecas externas como
palettable que possuem muitos mapas de cores extras.
No entanto, geralmente queremos criar ou manipular mapas de cores no Matplotlib. Isso pode ser feito usando a classe ListedColormap
ou
LinearSegmentedColormap
. Visto de fora, ambas as classes de mapa de cores mapeiam valores entre 0 e 1 para um monte de cores. Existem, no entanto, pequenas diferenças, algumas das quais são mostradas a seguir.
Antes de criar ou manipular mapas de cores manualmente, vamos primeiro ver como podemos obter mapas de cores e suas cores de classes de mapas de cores existentes.
Obtendo mapas de cores e acessando seus valores #
Primeiro, obter um mapa de cores nomeado, a maioria listado em
Escolhendo mapas de cores no Matplotlib , pode ser feito usando matplotlib.colormaps
, que retorna um objeto de mapa de cores. O comprimento da lista de cores usada internamente para definir o mapa de cores pode ser ajustado via Colormap.resampled
. Abaixo, usamos um valor modesto de 8, portanto, não há muitos valores a serem observados.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from matplotlib.colors import ListedColormap, LinearSegmentedColormap
viridis = mpl.colormaps['viridis'].resampled(8)
O objeto viridis
é um callable, que ao passar um float entre 0 e 1 retorna um valor RGBA do mapa de cores:
print(viridis(0.56))
(0.122312, 0.633153, 0.530398, 1.0)
ListedColormap #
ListedColormap
s armazenam seus valores de cor em um .colors
atributo. A lista de cores que compõem o mapa de cores pode ser acessada diretamente usando a colors
propriedade ou pode ser acessada indiretamente chamando viridis
com uma matriz de valores correspondentes ao comprimento do mapa de cores. Observe que a lista retornada está na forma de uma matriz RGBA Nx4, onde N é o comprimento do mapa de cores.
print('viridis.colors', viridis.colors)
print('viridis(range(8))', viridis(range(8)))
print('viridis(np.linspace(0, 1, 8))', viridis(np.linspace(0, 1, 8)))
viridis.colors [[0.267004 0.004874 0.329415 1. ]
[0.275191 0.194905 0.496005 1. ]
[0.212395 0.359683 0.55171 1. ]
[0.153364 0.497 0.557724 1. ]
[0.122312 0.633153 0.530398 1. ]
[0.288921 0.758394 0.428426 1. ]
[0.626579 0.854645 0.223353 1. ]
[0.993248 0.906157 0.143936 1. ]]
viridis(range(8)) [[0.267004 0.004874 0.329415 1. ]
[0.275191 0.194905 0.496005 1. ]
[0.212395 0.359683 0.55171 1. ]
[0.153364 0.497 0.557724 1. ]
[0.122312 0.633153 0.530398 1. ]
[0.288921 0.758394 0.428426 1. ]
[0.626579 0.854645 0.223353 1. ]
[0.993248 0.906157 0.143936 1. ]]
viridis(np.linspace(0, 1, 8)) [[0.267004 0.004874 0.329415 1. ]
[0.275191 0.194905 0.496005 1. ]
[0.212395 0.359683 0.55171 1. ]
[0.153364 0.497 0.557724 1. ]
[0.122312 0.633153 0.530398 1. ]
[0.288921 0.758394 0.428426 1. ]
[0.626579 0.854645 0.223353 1. ]
[0.993248 0.906157 0.143936 1. ]]
O mapa de cores é uma tabela de pesquisa, então "oversampling" o mapa de cores retorna a interpolação do vizinho mais próximo (observe as cores repetidas na lista abaixo)
print('viridis(np.linspace(0, 1, 12))', viridis(np.linspace(0, 1, 12)))
viridis(np.linspace(0, 1, 12)) [[0.267004 0.004874 0.329415 1. ]
[0.267004 0.004874 0.329415 1. ]
[0.275191 0.194905 0.496005 1. ]
[0.212395 0.359683 0.55171 1. ]
[0.212395 0.359683 0.55171 1. ]
[0.153364 0.497 0.557724 1. ]
[0.122312 0.633153 0.530398 1. ]
[0.288921 0.758394 0.428426 1. ]
[0.288921 0.758394 0.428426 1. ]
[0.626579 0.854645 0.223353 1. ]
[0.993248 0.906157 0.143936 1. ]
[0.993248 0.906157 0.143936 1. ]]
LinearSegmentedColormap #
LinearSegmentedColormap
s não têm um .colors
atributo. No entanto, ainda é possível chamar o mapa de cores com uma matriz inteira ou com uma matriz flutuante entre 0 e 1.
copper = mpl.colormaps['copper'].resampled(8)
print('copper(range(8))', copper(range(8)))
print('copper(np.linspace(0, 1, 8))', copper(np.linspace(0, 1, 8)))
copper(range(8)) [[0. 0. 0. 1. ]
[0.17647055 0.1116 0.07107143 1. ]
[0.35294109 0.2232 0.14214286 1. ]
[0.52941164 0.3348 0.21321429 1. ]
[0.70588219 0.4464 0.28428571 1. ]
[0.88235273 0.558 0.35535714 1. ]
[1. 0.6696 0.42642857 1. ]
[1. 0.7812 0.4975 1. ]]
copper(np.linspace(0, 1, 8)) [[0. 0. 0. 1. ]
[0.17647055 0.1116 0.07107143 1. ]
[0.35294109 0.2232 0.14214286 1. ]
[0.52941164 0.3348 0.21321429 1. ]
[0.70588219 0.4464 0.28428571 1. ]
[0.88235273 0.558 0.35535714 1. ]
[1. 0.6696 0.42642857 1. ]
[1. 0.7812 0.4975 1. ]]
Criando mapas de cores listados #
A criação de um mapa de cores é essencialmente a operação inversa da anterior, onde fornecemos uma lista ou matriz de especificações de cores ListedColormap
para fazer um novo mapa de cores.
Antes de continuar com o tutorial, vamos definir uma função auxiliar que recebe um ou mais mapas de cores como entrada, cria alguns dados aleatórios e aplica o(s) mapa(s) de cores a um gráfico de imagem desse conjunto de dados.
def plot_examples(colormaps):
"""
Helper function to plot data with associated colormap.
"""
np.random.seed(19680801)
data = np.random.randn(30, 30)
n = len(colormaps)
fig, axs = plt.subplots(1, n, figsize=(n * 2 + 2, 3),
constrained_layout=True, squeeze=False)
for [ax, cmap] in zip(axs.flat, colormaps):
psm = ax.pcolormesh(data, cmap=cmap, rasterized=True, vmin=-4, vmax=4)
fig.colorbar(psm, ax=ax)
plt.show()
No caso mais simples, podemos digitar uma lista de nomes de cores para criar um mapa de cores a partir deles.
cmap = ListedColormap(["darkorange", "gold", "lawngreen", "lightseagreen"])
plot_examples([cmap])
Na verdade, essa lista pode conter qualquer especificação de cores Matplotlib válida . Particularmente úteis para criar mapas de cores personalizados são os arrays numpy Nx4. Porque com a variedade de operações numpy que podemos fazer em tal array, a carpintaria de novos mapas de cores de mapas de cores existentes torna-se bastante simples.
Por exemplo, suponha que queremos tornar as primeiras 25 entradas de um mapa de cores "viridis" de 256 comprimentos rosa por algum motivo:
viridis = mpl.colormaps['viridis'].resampled(256)
newcolors = viridis(np.linspace(0, 1, 256))
pink = np.array([248/256, 24/256, 148/256, 1])
newcolors[:25, :] = pink
newcmp = ListedColormap(newcolors)
plot_examples([viridis, newcmp])
Podemos reduzir a faixa dinâmica de um mapa de cores; aqui escolhemos a metade central do mapa de cores. Observe, no entanto, que como viridis é um mapa de cores listado, terminaremos com 128 valores discretos em vez dos 256 valores que estavam no mapa de cores original. Este método não interpola no espaço de cores para adicionar novas cores.
viridis_big = mpl.colormaps['viridis']
newcmp = ListedColormap(viridis_big(np.linspace(0.25, 0.75, 128)))
plot_examples([viridis, newcmp])
e podemos facilmente concatenar dois mapas de cores:
top = mpl.colormaps['Oranges_r'].resampled(128)
bottom = mpl.colormaps['Blues'].resampled(128)
newcolors = np.vstack((top(np.linspace(0, 1, 128)),
bottom(np.linspace(0, 1, 128))))
newcmp = ListedColormap(newcolors, name='OrangeBlue')
plot_examples([viridis, newcmp])
Claro que não precisamos começar de um mapa de cores nomeado, só precisamos criar o array Nx4 para passar para ListedColormap
. Aqui criamos um mapa de cores que vai do marrom (RGB: 90, 40, 40) ao branco (RGB: 255, 255, 255).
N = 256
vals = np.ones((N, 4))
vals[:, 0] = np.linspace(90/256, 1, N)
vals[:, 1] = np.linspace(40/256, 1, N)
vals[:, 2] = np.linspace(40/256, 1, N)
newcmp = ListedColormap(vals)
plot_examples([viridis, newcmp])
Criando mapas de cores segmentados lineares #
A LinearSegmentedColormap
classe especifica mapas de cores usando pontos de ancoragem entre os quais os valores RGB(A) são interpolados.
O formato para especificar esses mapas de cores permite descontinuidades nos pontos de ancoragem. Cada ponto de ancoragem é especificado como uma linha em uma matriz do formulário , onde é a âncora e
e são os valores da cor em cada lado do ponto de ancoragem.[x[i] yleft[i] yright[i]]
x[i]
yleft[i]
yright[i]
Se não houver descontinuidades, então :yleft[i] == yright[i]
cdict = {'red': [[0.0, 0.0, 0.0],
[0.5, 1.0, 1.0],
[1.0, 1.0, 1.0]],
'green': [[0.0, 0.0, 0.0],
[0.25, 0.0, 0.0],
[0.75, 1.0, 1.0],
[1.0, 1.0, 1.0]],
'blue': [[0.0, 0.0, 0.0],
[0.5, 0.0, 0.0],
[1.0, 1.0, 1.0]]}
def plot_linearmap(cdict):
newcmp = LinearSegmentedColormap('testCmap', segmentdata=cdict, N=256)
rgba = newcmp(np.linspace(0, 1, 256))
fig, ax = plt.subplots(figsize=(4, 3), constrained_layout=True)
col = ['r', 'g', 'b']
for xx in [0.25, 0.5, 0.75]:
ax.axvline(xx, color='0.7', linestyle='--')
for i in range(3):
ax.plot(np.arange(256)/256, rgba[:, i], color=col[i])
ax.set_xlabel('index')
ax.set_ylabel('RGB')
plt.show()
plot_linearmap(cdict)
Para fazer uma descontinuidade em um ponto de ancoragem, a terceira coluna é diferente da segunda. A matriz para cada um de "vermelho", "verde", "azul" e, opcionalmente, "alfa" é configurada como:
cdict['red'] = [...
[x[i] yleft[i] yright[i]],
[x[i+1] yleft[i+1] yright[i+1]],
...]
e para valores passados para o mapa de cores entre x[i]
e x[i+1]
, a interpolação é entre yright[i]
e yleft[i+1]
.
No exemplo abaixo há uma descontinuidade em vermelho em 0,5. A interpolação entre 0 e 0,5 vai de 0,3 para 1, e entre 0,5 e 1 vai de 0,9 para 1. Observe que , e são supérfluos para a interpolação porque (ou seja, ) é o valor à esquerda de 0, e ( ou seja, ) é o valor à direita de 1, que está fora do domínio do mapeamento de cores.red[0, 1]
red[2, 2]
red[0, 1]
yleft[0]
red[2, 2]
yright[2]
Criando diretamente um mapa de cores segmentado de uma lista #
A abordagem descrita acima é muito versátil, mas reconhecidamente um pouco complicada de implementar. Para alguns casos básicos, o uso de
LinearSegmentedColormap.from_list
pode ser mais fácil. Isso cria um mapa de cores segmentado com espaçamentos iguais de uma lista de cores fornecida.
colors = ["darkorange", "gold", "lawngreen", "lightseagreen"]
cmap1 = LinearSegmentedColormap.from_list("mycmap", colors)
Se desejado, os nós do mapa de cores podem ser dados como números entre 0 e 1. Por exemplo, pode-se fazer com que a parte avermelhada ocupe mais espaço no mapa de cores.
Referências
O uso das seguintes funções, métodos, classes e módulos é mostrado neste exemplo:
Tempo total de execução do script: ( 0 minutos 4.802 segundos)