Carte de couleurs inversée dans matplotlib

253

Je voudrais savoir comment inverser simplement l'ordre des couleurs d'une palette de couleurs donnée afin de l'utiliser avec plot_surface.

Mermoz
la source

Réponses:

464

Les cartes de couleurs standard ont également toutes des versions inversées. Ils ont les mêmes noms avec _rcloués à la fin. ( Documentation ici. )

ptomato
la source
Cela ne fonctionne pas avec "amfhot": "ValueError: la palette de couleurs amfhot_r n'est pas reconnue". Je suppose que "hot_r" devra suffire.
shockburner
De même, "ValueError: Colormap red_r n'est pas reconnu."
Alex Willison
18

Dans matplotlib, une carte de couleurs n'est pas une liste, mais elle contient la liste de ses couleurs en tant que colormap.colors. Et le module matplotlib.colorsfournit une fonction ListedColormap()pour générer une carte de couleurs à partir d'une liste. Vous pouvez donc inverser n'importe quelle carte de couleur en faisant

colormap_r = ListedColormap(colormap.colors[::-1])
Gilles
la source
7
+1. Cependant, cela n'inversera génériquement aucune carte de couleurs. Seuls ListedColormaps (c'est-à-dire discrets, plutôt qu'interpolés) ont un colorsattribut. L'inversion LinearSegmentedColormapsest un peu plus complexe. (Vous devez inverser chaque élément du _segmentdatadicton.)
Joe Kington
3
En ce qui concerne l'inversion LinearSegmentedColormaps, je viens de le faire pour certaines cartes de couleurs. Voici un bloc-notes IPython à ce sujet.
kwinkunks
@kwinkunks je pense que la fonction de votre ordinateur portable n'est pas correcte, voir la réponse ci
Mattijn
14

La solution est assez simple. Supposons que vous souhaitiez utiliser le schéma de couleurs "automne". La version standard:

cmap = matplotlib.cm.autumn

Pour inverser le spectre de couleurs de la palette de couleurs, utilisez la fonction get_cmap () et ajoutez «_r» au titre de la palette de couleurs comme ceci:

cmap_reversed = matplotlib.cm.get_cmap('autumn_r')
Jm M
la source
pourriez-vous fournir un lien de documentation d'où vous avez obtenu le .autumn?
Xitcod13
Cela peut se casser plus tard ... matplotlib.org/3.1.1/gallery/color/colormap_reference.html , mais je suis sûr que toute personne intéressée sera en mesure de trouver cela par la recherche de toute façon.
Jlanger
13

Comme un LinearSegmentedColormapsest basé sur un dictionnaire de rouge, vert et bleu, il est nécessaire d'inverser chaque élément:

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

Assurez-vous que cela fonctionne:

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

entrez la description de l'image ici

ÉDITER


Je ne reçois pas le commentaire de user3445587. Cela fonctionne bien sur la palette de couleurs arc-en-ciel:

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

entrez la description de l'image ici

Mais cela fonctionne particulièrement bien pour les cartes de couleurs déclarées personnalisées, car il n'y a pas de valeur _rpar défaut pour les cartes de couleurs déclarées personnalisées. Exemple suivant tiré 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')

entrez la description de l'image ici

Mattijn
la source
Cet exemple n'est pas complet dans le sens où les données de segment ne doivent pas figurer dans des listes et ne sont donc pas nécessairement réversibles (par exemple, la palette de couleurs arc-en-ciel standard). Je pense qu'en principe tous les LinearSegmentedColormaps devraient en principe être réversibles en utilisant une fonction lambda comme dans la palette de couleurs arc-en-ciel?
outre
@ user3445587 J'ajoute quelques exemples supplémentaires, mais je pense que cela fonctionne très bien sur la palette de couleurs arc-en-ciel standard
Mattijn
Comme c'était trop long, j'ai ajouté une nouvelle réponse, qui devrait fonctionner pour toutes sortes de LinearSegmentData. Le problème est que pour rainbow, _segmentdata est implémenté différemment. Votre code - du moins sur ma machine - ne fonctionne donc pas avec la palette de couleurs arc-en-ciel.
outre
12

Depuis Matplotlib 2.0, il existe une reversed()méthode pour ListedColormapet des LinearSegmentedColorMapobjets, vous pouvez donc simplement faire

cmap_reversed = cmap.reversed()

Voici la documentation.

David Stansby
la source
1

Il existe deux types de LinearSegmentedColormaps. Dans certains, les _segmentdata sont donnés explicitement, par exemple, pour 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))}

Pour arc-en-ciel, _segmentdata est donné comme suit:

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

Nous pouvons trouver les fonctions dans la source de matplotlib, où elles sont données comme

_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)
}

Tout ce que vous voulez est déjà fait dans matplotlib, appelez simplement cm.revcmap, qui inverse les deux types de données de segment, donc

cm.revcmap(cm.rainbow._segmentdata)

devrait faire le travail - vous pouvez simplement créer un nouveau LinearSegmentData à partir de cela. Dans revcmap, l'inversion de SegmentData basée sur une fonction se fait avec

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

tandis que les autres listes sont inversées comme d'habitude

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

Donc en fait, tout ce que vous voulez, c'est

def reverse_colourmap(cmap, name = 'my_cmap_r'):
     return mpl.colors.LinearSegmentedColormap(name, cm.revcmap(cmap._segmentdata)) 
étranger
la source
1

Il n'y a pas (encore) de méthode intégrée pour inverser les palettes de couleurs arbitraires, mais une solution simple consiste en fait à ne pas modifier la barre de couleurs mais à créer un objet Normalize inverseur:

from matplotlib.colors import Normalize

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

Vous pouvez ensuite l'utiliser avec d' plot_surfaceautres fonctions de traçage de Matplotlib en faisant par exemple

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

Cela fonctionnera avec n'importe quelle palette de couleurs Matplotlib.

astrofrog
la source
Il y a maintenant! matplotlib.org/api/_as_gen/…
David Stansby