Escolhendo mapas de cores no Matplotlib #

O Matplotlib possui vários mapas de cores integrados acessíveis via matplotlib.colormaps. Também existem bibliotecas externas que possuem muitos mapas de cores extras, que podem ser visualizados na seção Mapas de cores de terceiros da documentação do Matplotlib. Aqui, discutimos brevemente como escolher entre as muitas opções. Para obter ajuda sobre como criar seus próprios mapas de cores, consulte Criando mapas de cores no Matplotlib .

Visão geral #

A ideia por trás da escolha de um bom mapa de cores é encontrar uma boa representação no espaço de cores 3D para o seu conjunto de dados. O melhor mapa de cores para qualquer conjunto de dados depende de muitas coisas, incluindo:

  • Seja representando formulário ou dados métricos ( [Ware] )

  • Seu conhecimento do conjunto de dados ( por exemplo , existe um valor crítico do qual os outros valores se desviam?)

  • Se houver um esquema de cores intuitivo para o parâmetro que você está plotando

  • Se houver um padrão no campo, o público pode esperar

Para muitas aplicações, um mapa de cores perceptivamente uniforme é a melhor escolha; ou seja, um mapa de cores no qual etapas iguais nos dados são percebidas como etapas iguais no espaço de cores. Os pesquisadores descobriram que o cérebro humano percebe as mudanças no parâmetro de luminosidade como mudanças nos dados muito melhor do que, por exemplo, mudanças na tonalidade. Portanto, os mapas de cores que têm brilho monotonicamente crescente através do mapa de cores serão melhor interpretados pelo visualizador. Exemplos maravilhosos de mapas de cores perceptivamente uniformes também podem ser encontrados na seção Mapas de cores de terceiros .

A cor pode ser representada no espaço 3D de várias maneiras. Uma maneira de representar a cor é usando o CIELAB. No CIELAB, o espaço de cores é representado pela leveza, \(L^*\); vermelho verde,\(a^*\); e amarelo-azul,\(b^*\). O parâmetro de luminosidade\(L^*\)pode então ser usado para aprender mais sobre como os mapas de cores matplotlib serão percebidos pelos visualizadores.

Um excelente recurso inicial para aprender sobre a percepção humana de mapas de cores é da [IBM] .

Classes de mapas de cores #

Os mapas de cores geralmente são divididos em várias categorias com base em sua função (consulte, por exemplo , [Moreland] ):

  1. Sequencial: mudança na claridade e frequentemente saturação de cor de forma incremental, geralmente usando um único matiz; deve ser usado para representar informações que possuem ordenação.

  2. Divergente: mudança na claridade e possivelmente saturação de duas cores diferentes que se encontram no meio em uma cor não saturada; deve ser usado quando a informação que está sendo plotada tem um valor médio crítico, como topografia ou quando os dados se desviam em torno de zero.

  3. Cíclica: mudança na claridade de duas cores diferentes que se encontram no meio e começam/acabam em uma cor não saturada; deve ser usado para valores que envolvem os pontos finais, como ângulo de fase, direção do vento ou hora do dia.

  4. Qualitativo: muitas vezes são cores diversas; deve ser usado para representar informações que não possuem ordenação ou relacionamentos.

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from colorspacious import cspace_converter

Primeiro, mostraremos o intervalo de cada mapa de cores. Observe que alguns parecem mudar mais "rapidamente" do que outros.

cmaps = {}

gradient = np.linspace(0, 1, 256)
gradient = np.vstack((gradient, gradient))


def plot_color_gradients(category, cmap_list):
    # Create figure and adjust figure height to number of colormaps
    nrows = len(cmap_list)
    figh = 0.35 + 0.15 + (nrows + (nrows - 1) * 0.1) * 0.22
    fig, axs = plt.subplots(nrows=nrows + 1, figsize=(6.4, figh))
    fig.subplots_adjust(top=1 - 0.35 / figh, bottom=0.15 / figh,
                        left=0.2, right=0.99)
    axs[0].set_title(f'{category} colormaps', fontsize=14)

    for ax, name in zip(axs, cmap_list):
        ax.imshow(gradient, aspect='auto', cmap=mpl.colormaps[name])
        ax.text(-0.01, 0.5, name, va='center', ha='right', fontsize=10,
                transform=ax.transAxes)

    # Turn off *all* ticks & spines, not just the ones with colormaps.
    for ax in axs:
        ax.set_axis_off()

    # Save colormap list for later.
    cmaps[category] = cmap_list

Sequencial #

Para as plotagens sequenciais, o valor de luminosidade aumenta monotonicamente através dos mapas de cores. Isso é bom. Alguns dos\(L^*\)os valores nos mapas de cores variam de 0 a 100 (binário e a outra escala de cinza) e outros começam em torno de \(L^*=20\). Aqueles que têm uma gama menor de\(L^*\)terá, portanto, um alcance perceptivo menor. Note também que o\(L^*\)função varia entre os mapas de cores: alguns são aproximadamente lineares em\(L^*\)e outros são mais curvos.

plot_color_gradients('Perceptually Uniform Sequential',
                     ['viridis', 'plasma', 'inferno', 'magma', 'cividis'])
Mapas de cores sequenciais perceptivamente uniformes
plot_color_gradients('Sequential',
                     ['Greys', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds',
                      'YlOrBr', 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu',
                      'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 'YlGn'])
Mapas de cores sequenciais

Sequencial2 #

Muitos dos\(L^*\)os valores dos gráficos Sequential2 aumentam monotonicamente, mas alguns (outono, frio, primavera e inverno) se estabilizam ou até sobem e descem em\(L^*\)espaço. Outros (afmhot, cobre, gist_heat e hot) têm dobras no\(L^*\)funções. Os dados que estão sendo representados em uma região do mapa de cores que está em um platô ou torção levarão a uma percepção de bandas dos dados nesses valores no mapa de cores (consulte [mycarta-banding] para um excelente exemplo disso).

plot_color_gradients('Sequential (2)',
                     ['binary', 'gist_yarg', 'gist_gray', 'gray', 'bone',
                      'pink', 'spring', 'summer', 'autumn', 'winter', 'cool',
                      'Wistia', 'hot', 'afmhot', 'gist_heat', 'copper'])
Mapas de cores sequenciais (2)

Divergente #

Para os mapas Divergentes, queremos ter um aumento monotônico\(L^*\) valores até um máximo, que deve estar próximo de\(L^*=100\), seguido por uma diminuição monotônica\(L^*\)valores. Estamos procurando um mínimo aproximadamente igual\(L^*\)valores em extremidades opostas do mapa de cores. Por essas medidas, BrBG e RdBu são boas opções. coolwarm é uma boa opção, mas não abrange uma ampla gama de\(L^*\)valores (consulte a seção de tons de cinza abaixo).

plot_color_gradients('Diverging',
                     ['PiYG', 'PRGn', 'BrBG', 'PuOr', 'RdGy', 'RdBu', 'RdYlBu',
                      'RdYlGn', 'Spectral', 'coolwarm', 'bwr', 'seismic'])
Mapas de cores divergentes

Cíclico #

Para mapas cíclicos, queremos começar e terminar na mesma cor e encontrar um ponto central simétrico no meio.\(L^*\)deve mudar monotonicamente do início ao meio e inversamente do meio ao fim. Deve ser simétrico no lado crescente e decrescente e diferir apenas na tonalidade. Nas extremidades e no meio,\(L^*\)inverterá a direção, que deve ser suavizada em \(L^*\)espaço para reduzir artefatos. Consulte [kovesi-colormaps] para obter mais informações sobre o design de mapas cíclicos.

O mapa de cores HSV frequentemente usado está incluído neste conjunto de mapas de cores, embora não seja simétrico a um ponto central. Além disso, o\(L^*\)os valores variam amplamente em todo o mapa de cores, tornando-o uma escolha ruim para representar dados para que os visualizadores vejam de forma perceptiva. Veja uma extensão dessa ideia em [mycarta-jet] .

plot_color_gradients('Cyclic', ['twilight', 'twilight_shifted', 'hsv'])
Mapas de cores cíclicos

Qualitativo #

Os mapas de cores qualitativos não visam ser mapas perceptivos, mas observar o parâmetro de luminosidade pode verificar isso para nós. o\(L^*\)os valores se movem por todo o mapa de cores e claramente não estão aumentando monotonicamente. Essas não seriam boas opções para uso como mapas de cores perceptivos.

plot_color_gradients('Qualitative',
                     ['Pastel1', 'Pastel2', 'Paired', 'Accent', 'Dark2',
                      'Set1', 'Set2', 'Set3', 'tab10', 'tab20', 'tab20b',
                      'tab20c'])
Mapas de cores qualitativos

Diversos #

Alguns dos diversos mapas de cores têm usos específicos para os quais foram criados. Por exemplo, gist_earth, oceano e terreno parecem ter sido criados para plotar topografia (verde/marrom) e profundidades de água (azul) juntos. Esperamos ver uma divergência nesses mapas de cores, mas várias torções podem não ser ideais, como em gist_earth e no terreno. CMRmap foi criado para converter bem em escala de cinza, embora pareça ter algumas pequenas dobras em \(L^*\). O cubehelix foi criado para variar suavemente em luminosidade e matiz, mas parece ter uma pequena protuberância na área de tonalidade verde. turbo foi criado para exibir dados de profundidade e disparidade.

O mapa de cores de jato frequentemente usado está incluído neste conjunto de mapas de cores. Podemos ver que o\(L^*\)os valores variam amplamente em todo o mapa de cores, tornando-o uma escolha ruim para representar dados para que os visualizadores vejam de forma perceptiva. Veja uma extensão dessa ideia em [mycarta-jet] e [turbo] .

plot_color_gradients('Miscellaneous',
                     ['flag', 'prism', 'ocean', 'gist_earth', 'terrain',
                      'gist_stern', 'gnuplot', 'gnuplot2', 'CMRmap',
                      'cubehelix', 'brg', 'gist_rainbow', 'rainbow', 'jet',
                      'turbo', 'nipy_spectral', 'gist_ncar'])

plt.show()
Mapas de cores diversos

Luminosidade dos mapas de cores Matplotlib #

Aqui examinamos os valores de luminosidade dos mapas de cores matplotlib. Observe que alguma documentação sobre mapas de cores está disponível ( [list-colormaps] ).

mpl.rcParams.update({'font.size': 12})

# Number of colormap per subplot for particular cmap categories
_DSUBS = {'Perceptually Uniform Sequential': 5, 'Sequential': 6,
          'Sequential (2)': 6, 'Diverging': 6, 'Cyclic': 3,
          'Qualitative': 4, 'Miscellaneous': 6}

# Spacing between the colormaps of a subplot
_DC = {'Perceptually Uniform Sequential': 1.4, 'Sequential': 0.7,
       'Sequential (2)': 1.4, 'Diverging': 1.4, 'Cyclic': 1.4,
       'Qualitative': 1.4, 'Miscellaneous': 1.4}

# Indices to step through colormap
x = np.linspace(0.0, 1.0, 100)

# Do plot
for cmap_category, cmap_list in cmaps.items():

    # Do subplots so that colormaps have enough space.
    # Default is 6 colormaps per subplot.
    dsub = _DSUBS.get(cmap_category, 6)
    nsubplots = int(np.ceil(len(cmap_list) / dsub))

    # squeeze=False to handle similarly the case of a single subplot
    fig, axs = plt.subplots(nrows=nsubplots, squeeze=False,
                            figsize=(7, 2.6*nsubplots))

    for i, ax in enumerate(axs.flat):

        locs = []  # locations for text labels

        for j, cmap in enumerate(cmap_list[i*dsub:(i+1)*dsub]):

            # Get RGB values for colormap and convert the colormap in
            # CAM02-UCS colorspace.  lab[0, :, 0] is the lightness.
            rgb = mpl.colormaps[cmap](x)[np.newaxis, :, :3]
            lab = cspace_converter("sRGB1", "CAM02-UCS")(rgb)

            # Plot colormap L values.  Do separately for each category
            # so each plot can be pretty.  To make scatter markers change
            # color along plot:
            # https://stackoverflow.com/q/8202605/

            if cmap_category == 'Sequential':
                # These colormaps all start at high lightness but we want them
                # reversed to look nice in the plot, so reverse the order.
                y_ = lab[0, ::-1, 0]
                c_ = x[::-1]
            else:
                y_ = lab[0, :, 0]
                c_ = x

            dc = _DC.get(cmap_category, 1.4)  # cmaps horizontal spacing
            ax.scatter(x + j*dc, y_, c=c_, cmap=cmap, s=300, linewidths=0.0)

            # Store locations for colormap labels
            if cmap_category in ('Perceptually Uniform Sequential',
                                 'Sequential'):
                locs.append(x[-1] + j*dc)
            elif cmap_category in ('Diverging', 'Qualitative', 'Cyclic',
                                   'Miscellaneous', 'Sequential (2)'):
                locs.append(x[int(x.size/2.)] + j*dc)

        # Set up the axis limits:
        #   * the 1st subplot is used as a reference for the x-axis limits
        #   * lightness values goes from 0 to 100 (y-axis limits)
        ax.set_xlim(axs[0, 0].get_xlim())
        ax.set_ylim(0.0, 100.0)

        # Set up labels for colormaps
        ax.xaxis.set_ticks_position('top')
        ticker = mpl.ticker.FixedLocator(locs)
        ax.xaxis.set_major_locator(ticker)
        formatter = mpl.ticker.FixedFormatter(cmap_list[i*dsub:(i+1)*dsub])
        ax.xaxis.set_major_formatter(formatter)
        ax.xaxis.set_tick_params(rotation=50)
        ax.set_ylabel('Lightness $L^*$', fontsize=12)

    ax.set_xlabel(cmap_category + ' colormaps', fontsize=14)

    fig.tight_layout(h_pad=0.0, pad=1.5)
    plt.show()
  • mapas de cores
  • mapas de cores
  • mapas de cores
  • mapas de cores
  • mapas de cores
  • mapas de cores
  • mapas de cores

Conversão em escala de cinza #

É importante ficar atento à conversão para tons de cinza para plotagens coloridas, pois elas podem ser impressas em impressoras preto e branco. Se não for considerado com cuidado, seus leitores podem acabar com gráficos indecifráveis ​​porque a escala de cinza muda de forma imprevisível através do mapa de cores.

A conversão para tons de cinza é feita de várias maneiras diferentes [bw] . Alguns dos melhores usam uma combinação linear dos valores rgb de um pixel, mas ponderados de acordo com a forma como percebemos a intensidade da cor. Um método não linear de conversão para tons de cinza é usar o\(L^*\)valores dos pixels. Em geral, aplicam-se a esta questão princípios semelhantes aos da apresentação perceptual da informação; isto é, se for escolhido um mapa de cores que aumenta monotonicamente em\(L^*\)valores, ele imprimirá de maneira razoável em escala de cinza.

Pensando nisso, vemos que os mapas de cores Sequenciais possuem representações razoáveis ​​em escala de cinza. Alguns dos mapas de cores Sequential2 têm representações em escala de cinza decentes o suficiente, embora alguns (outono, primavera, verão, inverno) tenham muito pouca alteração na escala de cinza. Se um mapa de cores como este foi usado em um gráfico e, em seguida, o gráfico foi impresso em escala de cinza, muitas informações podem ser mapeadas para os mesmos valores de cinza. Os mapas de cores divergentes variam principalmente de cinza mais escuro nas bordas externas a branco no meio. Alguns (PuOr e sísmica) têm um cinza visivelmente mais escuro de um lado do que do outro e, portanto, não são muito simétricos. coolwarm tem pouca escala de cinza e imprimiria em uma plotagem mais uniforme, perdendo muitos detalhes. Observe que os contornos sobrepostos e rotulados podem ajudar a diferenciar entre um lado do mapa de cores vs. o outro, uma vez que a cor não pode ser usada depois que um gráfico é impresso em escala de cinza. Muitos dos mapas de cores Qualitativo e Diversos, como Accent, hsv, jet e turbo, mudam de mais escuro para mais claro e de volta para cinza mais escuro em todo o mapa de cores. Isso tornaria impossível para um visualizador interpretar as informações em um gráfico depois de impresso em escala de cinza.

mpl.rcParams.update({'font.size': 14})

# Indices to step through colormap.
x = np.linspace(0.0, 1.0, 100)

gradient = np.linspace(0, 1, 256)
gradient = np.vstack((gradient, gradient))


def plot_color_gradients(cmap_category, cmap_list):
    fig, axs = plt.subplots(nrows=len(cmap_list), ncols=2)
    fig.subplots_adjust(top=0.95, bottom=0.01, left=0.2, right=0.99,
                        wspace=0.05)
    fig.suptitle(cmap_category + ' colormaps', fontsize=14, y=1.0, x=0.6)

    for ax, name in zip(axs, cmap_list):

        # Get RGB values for colormap.
        rgb = mpl.colormaps[name](x)[np.newaxis, :, :3]

        # Get colormap in CAM02-UCS colorspace. We want the lightness.
        lab = cspace_converter("sRGB1", "CAM02-UCS")(rgb)
        L = lab[0, :, 0]
        L = np.float32(np.vstack((L, L, L)))

        ax[0].imshow(gradient, aspect='auto', cmap=mpl.colormaps[name])
        ax[1].imshow(L, aspect='auto', cmap='binary_r', vmin=0., vmax=100.)
        pos = list(ax[0].get_position().bounds)
        x_text = pos[0] - 0.01
        y_text = pos[1] + pos[3]/2.
        fig.text(x_text, y_text, name, va='center', ha='right', fontsize=10)

    # Turn off *all* ticks & spines, not just the ones with colormaps.
    for ax in axs.flat:
        ax.set_axis_off()

    plt.show()


for cmap_category, cmap_list in cmaps.items():

    plot_color_gradients(cmap_category, cmap_list)
  • Mapas de cores sequenciais perceptivamente uniformes
  • Mapas de cores sequenciais
  • Mapas de cores sequenciais (2)
  • Mapas de cores divergentes
  • Mapas de cores cíclicos
  • Mapas de cores qualitativos
  • Mapas de cores diversos

Deficiências de visão de cores #

Há muitas informações disponíveis sobre o daltonismo ( por exemplo , [colorblindness] ). Além disso, existem ferramentas disponíveis para converter imagens em como elas se parecem para diferentes tipos de deficiências de visão de cores.

A forma mais comum de deficiência de visão de cores envolve a diferenciação entre vermelho e verde. Portanto, evitar mapas de cores com vermelho e verde evitará muitos problemas em geral.

Referências #

Tempo total de execução do script: (0 minutos 14,251 segundos)

Galeria gerada por Sphinx-Gallery