Mapa de cores reverso no matplotlib

252

Gostaria de saber como simplesmente reverter a ordem das cores de um determinado mapa de cores para usá-lo com plot_surface.

Mermoz
fonte

Respostas:

463

Os mapas de cores padrão também têm versões invertidas. Eles têm os mesmos nomes com _rtacheados até o fim. ( Documentação aqui. )

ptomato
fonte
Isso não funciona com "amfhot": "ValueError: Colormap amfhot_r não é reconhecido". Suponho que "hot_r" terá que ser suficiente.
Shockburner
Da mesma forma, "ValueError: Colormap red_r não é reconhecido."
Alex Willison #
18

No matplotlib, um mapa de cores não é uma lista, mas contém a lista de suas cores como colormap.colors. E o módulo matplotlib.colorsfornece uma função ListedColormap()para gerar um mapa de cores a partir de uma lista. Então você pode reverter qualquer mapa de cores fazendo

colormap_r = ListedColormap(colormap.colors[::-1])
Gilles
fonte
7
+1. No entanto, isso não reverterá genericamente nenhum mapa de cores. Somente ListedColormaps (ou seja, discretos, em vez de interpolados) têm um colorsatributo. A reversão LinearSegmentedColormapsé um pouco mais complexa. (Você precisa de reverter todos os itens do _segmentdatadict.)
Joe Kington
3
Em relação à reversão LinearSegmentedColormaps, eu fiz isso em alguns mapas de cores. Aqui está um caderno IPython sobre isso.
precisa saber é o seguinte
@kwinkunks eu acho que a função em seu notebook não está certo, veja resposta abaixo
Mattijn
14

A solução é bem direta. Suponha que você queira usar o esquema de mapa de cores "outono". A versão padrão:

cmap = matplotlib.cm.autumn

Para reverter o espectro de cores do mapa de cores, use a função get_cmap () e adicione '_r' ao título do mapa de cores da seguinte maneira:

cmap_reversed = matplotlib.cm.get_cmap('autumn_r')
Jm M
fonte
você poderia fornecer o link da documentação de onde obteve o outono?
Xitcod13 3/03
Isso pode ser interrompido mais tarde ... matplotlib.org/3.1.1/gallery/color/colormap_reference.html , mas tenho certeza de que qualquer pessoa interessada poderá encontrar isso pela pesquisa de qualquer maneira.
Jlanger 24/04
13

Como a LinearSegmentedColormapsé baseado em um dicionário de vermelho, verde e azul, é necessário reverter cada item:

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

Veja 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')

insira a descrição da imagem aqui

EDITAR


Não recebo o comentário de user3445587. Funciona bem no mapa de cores do arco-íris:

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')

insira a descrição da imagem aqui

Mas funciona especialmente bem para mapas de cores declarados personalizados, pois não há um padrão _rpara mapas de cores declarados personalizados. Exemplo a seguir, retirado 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')

insira a descrição da imagem aqui

Mattijn
fonte
Este exemplo não está completo no sentido de que os dados do segmento não estão nas listas, portanto não são necessariamente reversíveis (por exemplo, mapa de cores padrão do arco-íris). Eu acho que, em princípio, todos os LinearSegmentedColormaps deveriam, em princípio, ser reversíveis usando uma função lambda como no mapa de cores do arco-íris?
no exterior
@ user3445587 eu adicionar mais alguns exemplos, mas eu acho que funciona muito bem no mapa de cores do arco-íris padrão
Mattijn
Como foi muito longo, adicionei uma nova resposta, que deve funcionar para todos os tipos de LinearSegmentData. O problema é que, para o rainbow, _segmentdata é implementado de maneira diferente. Portanto, seu código - pelo menos na minha máquina - não funciona com o mapa de cores do arco-íris.
no exterior
12

A partir do Matplotlib 2.0, existe um reversed()método para ListedColormape LinearSegmentedColorMapobjetos, para que você possa fazer

cmap_reversed = cmap.reversed()

Aqui está a documentação.

David Stansby
fonte
1

Existem dois tipos de LinearSegmentedColormaps. Em alguns, os _segmentdata são dados explicitamente, por exemplo, 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 é fornecido da seguinte maneira:

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

Podemos encontrar as funções na fonte do matplotlib, onde são dadas 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)
}

Tudo o que você deseja já está pronto no matplotlib, basta chamar cm.revcmap, que reverte os dois tipos de dados do segmento, portanto

cm.revcmap(cm.rainbow._segmentdata)

deve fazer o trabalho - você pode simplesmente criar um novo LinearSegmentData a partir disso. No revcmap, a reversão de SegmentData baseada em função é feita com

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

enquanto as outras listas são invertidas como de costume

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

Então, na verdade, tudo o que você quer é

def reverse_colourmap(cmap, name = 'my_cmap_r'):
     return mpl.colors.LinearSegmentedColormap(name, cm.revcmap(cmap._segmentdata)) 
no exterior
fonte
1

Ainda não existe uma maneira interna de reverter mapas de cores arbitrários, mas uma solução simples é realmente não modificar a barra de cores, mas criar um objeto Normalize invertido:

from matplotlib.colors import Normalize

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

Você pode usá-lo com plot_surfacee com outras funções de plotagem do Matplotlib, executando

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

Isso funcionará com qualquer mapa de cores Matplotlib.

astrofrog
fonte
Existe agora! matplotlib.org/api/_as_gen/...
David Stansby