Mapa de colores inverso en matplotlib

253

Me gustaría saber cómo invertir simplemente el orden de color de un mapa de colores determinado para usarlo con plot_surface.

Mermoz
fuente

Respuestas:

464

Los mapas de color estándar también tienen versiones invertidas. Tienen los mismos nombres con _rtachuelas hasta el final. ( Documentación aquí. )

ptomato
fuente
Esto no funciona con "amfhot": "ValueError: mapa de colores amfhot_r no se reconoce". Supongo que "hot_r" tendrá que ser suficiente.
shockburner
Del mismo modo, "ValueError: Colormap red_r no se reconoce".
Alex Willison
18

En matplotlib, un mapa de colores no es una lista, pero contiene la lista de sus colores como colormap.colors. Y el módulo matplotlib.colorsproporciona una función ListedColormap()para generar un mapa de color a partir de una lista. Para que pueda revertir cualquier mapa de color haciendo

colormap_r = ListedColormap(colormap.colors[::-1])
Gilles
fuente
77
+1. Sin embargo, esto no revertirá genéricamente ningún mapa de colores. Solo ListedColormaps (es decir, discreto, en lugar de interpolado) tienen un colorsatributo. Revertir LinearSegmentedColormapses un poco más complejo. (Debe revertir cada elemento en el _segmentdatadict.)
Joe Kington
3
Con respecto a la inversión LinearSegmentedColormaps, acabo de hacer esto para algunos mapas de colores. Aquí hay un cuaderno de IPython al respecto.
kwinkunks
@kwinkunks, creo que la función en su computadora portátil no es correcta, vea la respuesta a continuación
Mattijn
14

La solución es bastante sencilla. Supongamos que desea utilizar el esquema de mapa de colores "otoño". La versión estándar:

cmap = matplotlib.cm.autumn

Para invertir el espectro de colores del mapa de colores, use la función get_cmap () y agregue '_r' al título del mapa de colores de esta manera:

cmap_reversed = matplotlib.cm.get_cmap('autumn_r')
Jm M
fuente
¿podría proporcionar un enlace de documentación de donde obtuvo el .autumn?
Xitcod13
Esto puede romperse más tarde ... matplotlib.org/3.1.1/gallery/color/colormap_reference.html , pero estoy seguro de que cualquier persona interesada podrá encontrar esto mediante la búsqueda de todos modos.
Jlanger
13

Como LinearSegmentedColormapsse basa en un diccionario de rojo, verde y azul, es necesario revertir cada elemento:

import matplotlib.pyplot as plt
import matplotlib as mpl
def reverse_colourmap(cmap, name = 'my_cmap_r'):
    """
    In: 
    cmap, name 
    Out:
    my_cmap_r

    Explanation:
    t[0] goes from 0 to 1
    row i:   x  y0  y1 -> t[0] t[1] t[2]
                   /
                  /
    row i+1: x  y0  y1 -> t[n] t[1] t[2]

    so the inverse should do the same:
    row i+1: x  y1  y0 -> 1-t[0] t[2] t[1]
                   /
                  /
    row i:   x  y1  y0 -> 1-t[n] t[2] t[1]
    """        
    reverse = []
    k = []   

    for key in cmap._segmentdata:    
        k.append(key)
        channel = cmap._segmentdata[key]
        data = []

        for t in channel:                    
            data.append((1-t[0],t[2],t[1]))            
        reverse.append(sorted(data))    

    LinearL = dict(zip(k,reverse))
    my_cmap_r = mpl.colors.LinearSegmentedColormap(name, LinearL) 
    return my_cmap_r

Ver que funciona:

my_cmap        
<matplotlib.colors.LinearSegmentedColormap at 0xd5a0518>

my_cmap_r = reverse_colourmap(my_cmap)

fig = plt.figure(figsize=(8, 2))
ax1 = fig.add_axes([0.05, 0.80, 0.9, 0.15])
ax2 = fig.add_axes([0.05, 0.475, 0.9, 0.15])
norm = mpl.colors.Normalize(vmin=0, vmax=1)
cb1 = mpl.colorbar.ColorbarBase(ax1, cmap = my_cmap, norm=norm,orientation='horizontal')
cb2 = mpl.colorbar.ColorbarBase(ax2, cmap = my_cmap_r, norm=norm, orientation='horizontal')

ingrese la descripción de la imagen aquí

EDITAR


No recibo el comentario del usuario3445587. Funciona bien en el mapa de colores del arco iris:

cmap = mpl.cm.jet
cmap_r = reverse_colourmap(cmap)

fig = plt.figure(figsize=(8, 2))
ax1 = fig.add_axes([0.05, 0.80, 0.9, 0.15])
ax2 = fig.add_axes([0.05, 0.475, 0.9, 0.15])
norm = mpl.colors.Normalize(vmin=0, vmax=1)
cb1 = mpl.colorbar.ColorbarBase(ax1, cmap = cmap, norm=norm,orientation='horizontal')
cb2 = mpl.colorbar.ColorbarBase(ax2, cmap = cmap_r, norm=norm, orientation='horizontal')

ingrese la descripción de la imagen aquí

Pero funciona especialmente bien para mapas de color declarados personalizados, ya que no hay un valor predeterminado _rpara mapas de color declarados personalizados. Siguiendo el ejemplo tomado de http://matplotlib.org/examples/pylab_examples/custom_cmap.html :

cdict1 = {'red':   ((0.0, 0.0, 0.0),
                   (0.5, 0.0, 0.1),
                   (1.0, 1.0, 1.0)),

         'green': ((0.0, 0.0, 0.0),
                   (1.0, 0.0, 0.0)),

         'blue':  ((0.0, 0.0, 1.0),
                   (0.5, 0.1, 0.0),
                   (1.0, 0.0, 0.0))
         }

blue_red1 = mpl.colors.LinearSegmentedColormap('BlueRed1', cdict1)
blue_red1_r = reverse_colourmap(blue_red1)

fig = plt.figure(figsize=(8, 2))
ax1 = fig.add_axes([0.05, 0.80, 0.9, 0.15])
ax2 = fig.add_axes([0.05, 0.475, 0.9, 0.15])

norm = mpl.colors.Normalize(vmin=0, vmax=1)
cb1 = mpl.colorbar.ColorbarBase(ax1, cmap = blue_red1, norm=norm,orientation='horizontal')
cb2 = mpl.colorbar.ColorbarBase(ax2, cmap = blue_red1_r, norm=norm, orientation='horizontal')

ingrese la descripción de la imagen aquí

Mattijn
fuente
Este ejemplo no está completo en el sentido de que los datos del segmento no deben estar en listas, por lo que no es necesariamente reversible (por ejemplo, mapa de colores estándar del arco iris). Creo que, en principio, todos los LinearSegmentedColormaps deberían, en principio, ser reversibles utilizando una función lambda como en el mapa de colores del arco iris.
extranjero el
@ user3445587 Agrego algunos ejemplos más, pero creo que funciona bien en el mapa de
colores
Como era demasiado largo, agregué una nueva respuesta, que debería funcionar para todo tipo de LinearSegmentData. El problema es que para rainbow, _segmentdata se implementa de manera diferente. Por lo tanto, su código, al menos en mi máquina, no funciona con el mapa de colores del arco iris.
extranjero el
12

A partir de Matplotlib 2.0, hay un reversed()método para ListedColormapy LinearSegmentedColorMapobjetos, por lo que puede hacer

cmap_reversed = cmap.reversed()

Aquí está la documentación.

David Stansby
fuente
1

Hay dos tipos de LinearSegmentedColormaps. En algunos, los _segmentdata se dan explícitamente, por ejemplo, para jet:

>>> cm.jet._segmentdata
{'blue': ((0.0, 0.5, 0.5), (0.11, 1, 1), (0.34, 1, 1), (0.65, 0, 0), (1, 0, 0)), 'red': ((0.0, 0, 0), (0.35, 0, 0), (0.66, 1, 1), (0.89, 1, 1), (1, 0.5, 0.5)), 'green': ((0.0, 0, 0), (0.125, 0, 0), (0.375, 1, 1), (0.64, 1, 1), (0.91, 0, 0), (1, 0, 0))}

Para rainbow, _segmentdata se da de la siguiente manera:

>>> cm.rainbow._segmentdata
{'blue': <function <lambda> at 0x7fac32ac2b70>, 'red': <function <lambda> at 0x7fac32ac7840>, 'green': <function <lambda> at 0x7fac32ac2d08>}

Podemos encontrar las funciones en la fuente de matplotlib, donde se dan como

_rainbow_data = {
        'red': gfunc[33],   # 33: lambda x: np.abs(2 * x - 0.5),
        'green': gfunc[13], # 13: lambda x: np.sin(x * np.pi),
        'blue': gfunc[10],  # 10: lambda x: np.cos(x * np.pi / 2)
}

Todo lo que desea ya está hecho en matplotlib, solo llame a cm.revcmap, que invierte ambos tipos de datos de segmento, por lo que

cm.revcmap(cm.rainbow._segmentdata)

debería hacer el trabajo: simplemente puede crear un nuevo LinearSegmentData a partir de eso. En revcmap, la inversión de SegmentData basada en funciones se realiza con

def _reverser(f):
    def freversed(x):
        return f(1 - x)
    return freversed

mientras que las otras listas se invierten como de costumbre

valnew = [(1.0 - x, y1, y0) for x, y0, y1 in reversed(val)] 

Entonces, en realidad, todo lo que quieres es

def reverse_colourmap(cmap, name = 'my_cmap_r'):
     return mpl.colors.LinearSegmentedColormap(name, cm.revcmap(cmap._segmentdata)) 
de ultramar
fuente
1

No existe una forma integrada (todavía) de invertir mapas de colores arbitrarios, pero una solución simple es no modificar la barra de colores sino crear un objeto Normalizar invertido:

from matplotlib.colors import Normalize

class InvertedNormalize(Normalize):
    def __call__(self, *args, **kwargs):
        return 1 - super(InvertedNormalize, self).__call__(*args, **kwargs)

Luego puede usar esto con plot_surfacey otras funciones de trazado de Matplotlib haciendo, por ejemplo,

inverted_norm = InvertedNormalize(vmin=10, vmax=100)
ax.plot_surface(..., cmap=<your colormap>, norm=inverted_norm)

Esto funcionará con cualquier mapa de colores Matplotlib.

astrofrog
fuente
Hay ahora! matplotlib.org/api/_as_gen/…
David Stansby