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 ListedColormapou 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 #

ListedColormaps armazenam seus valores de cor em um .colorsatributo. A lista de cores que compõem o mapa de cores pode ser acessada diretamente usando a colorspropriedade ou pode ser acessada indiretamente chamando viridiscom 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 #

LinearSegmentedColormaps não têm um .colorsatributo. 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 ListedColormappara 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])
manipulação de mapa de cores

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])
manipulação de mapa de cores

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])
manipulação de mapa de cores

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])
manipulação de mapa de cores

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])
manipulação de mapa de cores

Criando mapas de cores segmentados lineares #

A LinearSegmentedColormapclasse 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)
manipulação de mapa de cores

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]

cdict['red'] = [[0.0,  0.0, 0.3],
                [0.5,  1.0, 0.9],
                [1.0,  1.0, 1.0]]
plot_linearmap(cdict)
manipulação de mapa de cores

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_listpode 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.

nodes = [0.0, 0.4, 0.8, 1.0]
cmap2 = LinearSegmentedColormap.from_list("mycmap", list(zip(nodes, colors)))

plot_examples([cmap1, cmap2])
manipulação de mapa de cores

Tempo total de execução do script: ( 0 minutos 4.802 segundos)

Galeria gerada por Sphinx-Gallery