Elegir mapas de colores en Matplotlib #

Matplotlib tiene una serie de mapas de colores integrados accesibles a través de matplotlib.colormaps. También hay bibliotecas externas que tienen muchos mapas de colores adicionales, que se pueden ver en la sección de mapas de colores de terceros de la documentación de Matplotlib. Aquí discutimos brevemente cómo elegir entre las muchas opciones. Para obtener ayuda sobre cómo crear sus propios mapas de colores, consulte Creación de mapas de colores en Matplotlib .

Resumen #

La idea detrás de elegir un buen mapa de color es encontrar una buena representación en el espacio de color 3D para su conjunto de datos. El mejor mapa de colores para cualquier conjunto de datos depende de muchas cosas, entre ellas:

  • Ya sea que represente datos de forma o métricos ( [Ware] )

  • Su conocimiento del conjunto de datos ( p. ej ., ¿hay un valor crítico del cual se desvían los otros valores?)

  • Si hay un esquema de color intuitivo para el parámetro que está trazando

  • Si hay un estándar en el campo, la audiencia puede estar esperando

Para muchas aplicaciones, un mapa de colores perceptivamente uniforme es la mejor opción; es decir, un mapa de colores en el que los pasos iguales en los datos se perciben como pasos iguales en el espacio de color. Los investigadores han descubierto que el cerebro humano percibe los cambios en el parámetro de luminosidad como cambios en los datos mucho mejor que, por ejemplo, los cambios en el tono. Por lo tanto, el espectador interpretará mejor los mapas de color que tienen una luminosidad que aumenta monótonamente a través del mapa de color. También se pueden encontrar maravillosos ejemplos de mapas de colores perceptivamente uniformes en la sección Mapas de colores de terceros .

El color se puede representar en el espacio 3D de varias maneras. Una forma de representar el color es usando CIELAB. En CIELAB, el espacio de color está representado por la luminosidad, \(L^*\); rojo verde,\(a^*\); y amarillo-azul,\(b^*\). El parámetro de luminosidad\(L^*\)luego se puede usar para obtener más información sobre cómo los espectadores percibirán los mapas de color de matplotlib.

Un excelente recurso inicial para aprender sobre la percepción humana de los mapas de color es de [IBM] .

Clases de mapas de colores #

Los mapas de colores a menudo se dividen en varias categorías según su función (ver, por ejemplo , [Moreland] ):

  1. Secuencial: cambio en la luminosidad y, a menudo, saturación del color de forma incremental, a menudo utilizando un solo tono; debe usarse para representar información que tiene orden.

  2. Divergente: cambio en la luminosidad y posiblemente saturación de dos colores diferentes que se encuentran en el medio en un color no saturado; se debe usar cuando la información que se grafica tiene un valor medio crítico, como la topografía o cuando los datos se desvían alrededor de cero.

  3. Cíclico: cambio en la luminosidad de dos colores diferentes que se encuentran en el medio y comienzan/terminan en un color no saturado; debe usarse para valores que se envuelven en los puntos finales, como el ángulo de fase, la dirección del viento o la hora del día.

  4. Cualitativos: a menudo son colores misceláneos; debe usarse para representar información que no tiene órdenes ni relaciones.

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

Primero, mostraremos el rango de cada mapa de colores. Tenga en cuenta que algunos parecen cambiar más "rápidamente" que otros.

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

# secuencial

Para los gráficos secuenciales, el valor de luminosidad aumenta monótonamente a través de los mapas de color. Esto es bueno. Algunos de los\(L^*\)los valores en los mapas de colores van de 0 a 100 (binario y el otro en escala de grises), y otros comienzan alrededor \(L^*=20\). Los que tienen un rango más pequeño de\(L^*\)en consecuencia, tendrá un rango de percepción más pequeño. Tenga en cuenta también que el\(L^*\)función varía entre los mapas de color: algunos son aproximadamente lineales en\(L^*\)y otros son más curvos.

plot_color_gradients('Perceptually Uniform Sequential',
                     ['viridis', 'plasma', 'inferno', 'magma', 'cividis'])
Mapas de colores secuenciales perceptualmente 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 colores secuenciales

Secuencial2 #

Mucho de\(L^*\)los valores de las gráficas Sequential2 aumentan monótonamente, pero algunos (otoño, frío, primavera e invierno) se estabilizan o incluso suben y bajan en\(L^*\)espacio. Otros (afmhot, copper, gist_heat y hot) tienen problemas en el\(L^*\)funciones Los datos que se representan en una región del mapa de colores que se encuentra en una meseta o torcedura darán lugar a una percepción de bandas de los datos en esos valores en el mapa de colores (consulte [mycarta-banding] para obtener un excelente ejemplo de esto).

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 color secuenciales (2)

# divergente

Para los mapas divergentes, queremos tener un aumento monótono\(L^*\) valores hasta un máximo, que debe estar cerca de\(L^*=100\), seguido de monótonamente decreciente\(L^*\)valores. Estamos buscando un mínimo aproximadamente igual\(L^*\)valores en los extremos opuestos del mapa de colores. Según estas medidas, BrBG y RdBu son buenas opciones. coolwarm es una buena opción, pero no abarca una amplia gama de\(L^*\)valores (consulte la sección de escala de grises a continuación).

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

cíclico #

Para los mapas cíclicos, queremos comenzar y terminar en el mismo color y encontrar un punto central simétrico en el medio.\(L^*\)debe cambiar monótonamente desde el principio hasta el medio, e inversamente desde el medio hasta el final. Debe ser simétrico en el lado creciente y decreciente, y solo debe diferir en el tono. En los extremos y en el medio,\(L^*\)invertirá la dirección, que debe suavizarse en \(L^*\)espacio para reducir los artefactos. Consulte [kovesi-colormaps] para obtener más información sobre el diseño de mapas cíclicos.

El mapa de colores HSV de uso frecuente se incluye en este conjunto de mapas de colores, aunque no es simétrico a un punto central. Además, el\(L^*\)los valores varían ampliamente a lo largo del mapa de colores, lo que lo convierte en una mala elección para representar datos para que los espectadores los vean perceptivamente. Vea una extensión de esta idea en [mycarta-jet] .

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

Cualitativo #

Los mapas de color cualitativos no pretenden ser mapas de percepción, pero mirar el parámetro de luminosidad puede verificarlo para nosotros. los\(L^*\)los valores se mueven por todo el lugar a lo largo del mapa de colores y claramente no aumentan de forma monótona. Estas no serían buenas opciones para usar como mapas de colores perceptivos.

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

Varios #

Algunos de los mapas de colores misceláneos tienen usos particulares para los que han sido creados. Por ejemplo, gist_earth, océano y terreno parecen creados para trazar juntos la topografía (verde/marrón) y las profundidades del agua (azul). Entonces, esperaríamos ver una divergencia en estos mapas de colores, pero múltiples torceduras pueden no ser ideales, como en gist_earth y el terreno. CMRmap se creó para convertir bien a escala de grises, aunque parece tener algunos pequeños problemas en \(L^*\). cubehelix fue creado para variar suavemente tanto en luminosidad como en tono, pero parece tener una pequeña joroba en el área del tono verde. turbo fue creado para mostrar datos de profundidad y disparidad.

El mapa de colores jet de uso frecuente se incluye en este conjunto de mapas de colores. Podemos ver que el\(L^*\)los valores varían ampliamente a lo largo del mapa de colores, lo que lo convierte en una mala elección para representar datos para que los espectadores los vean perceptivamente. Vea una extensión de esta idea en [mycarta-jet] y [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()
Varios mapas de colores

Ligereza de los mapas de color de Matplotlib #

Aquí examinamos los valores de luminosidad de los mapas de color de matplotlib. Tenga en cuenta que hay disponible alguna documentación sobre los mapas de colores ( [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 colores
  • mapas de colores
  • mapas de colores
  • mapas de colores
  • mapas de colores
  • mapas de colores
  • mapas de colores

Conversión en escala de grises #

Es importante prestar atención a la conversión a escala de grises para los gráficos en color, ya que pueden imprimirse en impresoras en blanco y negro. Si no se considera cuidadosamente, sus lectores pueden terminar con tramas indescifrables porque la escala de grises cambia de manera impredecible a través del mapa de colores.

La conversión a escala de grises se realiza de muchas maneras diferentes [bw] . Algunos de los mejores utilizan una combinación lineal de los valores rgb de un píxel, pero ponderados según cómo percibimos la intensidad del color. Un método no lineal de conversión a escala de grises es usar el\(L^*\)valores de los píxeles. En general, para esta pregunta se aplican principios similares a los que se aplican para presentar la información de uno de manera perceptual; es decir, si se elige un mapa de colores que aumenta monótonamente en\(L^*\)valores, se imprimirá de manera razonable en escala de grises.

Con esto en mente, vemos que los mapas de colores secuenciales tienen representaciones razonables en escala de grises. Algunos de los mapas de color Sequential2 tienen representaciones en escala de grises lo suficientemente decentes, aunque algunos (otoño, primavera, verano, invierno) tienen muy pocos cambios en la escala de grises. Si se usó un mapa de colores como este en un gráfico y luego el gráfico se imprimió en escala de grises, gran parte de la información puede asignarse a los mismos valores de gris. Los mapas de colores divergentes varían principalmente de un gris más oscuro en los bordes exteriores al blanco en el medio. Algunos (PuOr y sísmicos) tienen un gris notablemente más oscuro en un lado que en el otro y, por lo tanto, no son muy simétricos. coolwarm tiene poco rango de escala de grises y se imprimiría en una trama más uniforme, perdiendo muchos detalles. Tenga en cuenta que los contornos superpuestos y etiquetados podrían ayudar a diferenciar entre un lado del mapa de colores y otro. el otro, ya que el color no se puede usar una vez que se imprime un gráfico en escala de grises. Muchos de los mapas de colores Cualitativos y Misceláneos, como Accent, hsv, jet y turbo, cambian de más oscuro a más claro y nuevamente a un gris más oscuro en todo el mapa de colores. Esto haría imposible que un espectador interpretara la información en un gráfico una vez que se imprimió en escala de grises.

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 colores secuenciales perceptualmente uniformes
  • Mapas de colores secuenciales
  • Mapas de color secuenciales (2)
  • Mapas de color divergentes
  • Mapas de colores cíclicos
  • Mapas de color cualitativos
  • Varios mapas de colores

Deficiencias en la visión del color #

Hay mucha información disponible sobre el daltonismo ( p. ej ., [daltonismo] ). Además, hay herramientas disponibles para convertir imágenes a su aspecto para diferentes tipos de deficiencias de la visión del color.

La forma más común de deficiencia de la visión del color consiste en diferenciar entre rojo y verde. Por lo tanto, evitar mapas de colores con rojo y verde evitará muchos problemas en general.

Referencias #

Tiempo total de ejecución del script: (0 minutos 14.251 segundos)

Galería generada por Sphinx-Gallery