Création de palettes de couleurs dans Matplotlib #

Matplotlib a un certain nombre de palettes de couleurs intégrées accessibles via matplotlib.colormaps. Il existe également des bibliothèques externes comme palettable qui ont de nombreuses palettes de couleurs supplémentaires.

Cependant, nous voulons souvent créer ou manipuler des palettes de couleurs dans Matplotlib. Cela peut être fait en utilisant la classe ListedColormapou LinearSegmentedColormap. Vues de l'extérieur, les deux classes de palette de couleurs mappent des valeurs comprises entre 0 et 1 sur un ensemble de couleurs. Il existe cependant de légères différences, dont certaines sont présentées ci-après.

Avant de créer ou de manipuler manuellement des palettes de couleurs, voyons d'abord comment obtenir des palettes de couleurs et leurs couleurs à partir de classes de palettes de couleurs existantes.

Obtenir des palettes de couleurs et accéder à leurs valeurs #

Tout d'abord, l'obtention d'une palette de couleurs nommée, dont la plupart sont répertoriées dans Choisir des palettes de couleurs dans Matplotlib , peut être effectuée à l'aide de matplotlib.colormaps, qui renvoie un objet de palette de couleurs. La longueur de la liste des couleurs utilisées en interne pour définir la palette de couleurs peut être ajustée via Colormap.resampled. Ci-dessous, nous utilisons une valeur modeste de 8, il n'y a donc pas beaucoup de valeurs à examiner.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
from matplotlib.colors import ListedColormap, LinearSegmentedColormap

viridis = mpl.colormaps['viridis'].resampled(8)

L'objet viridisest un appelable qui, lorsqu'il est passé à un flottant entre 0 et 1, renvoie une valeur RVBA à partir de la palette de couleurs :

print(viridis(0.56))
(0.122312, 0.633153, 0.530398, 1.0)

ListedColormap #

ListedColormaps stockent leurs valeurs de couleur dans un .colorsattribut. La liste des couleurs qui composent la palette de couleurs est accessible directement à l'aide de la colorspropriété, ou indirectement en appelant viridisavec un tableau de valeurs correspondant à la longueur de la palette de couleurs. Notez que la liste renvoyée se présente sous la forme d'un tableau RGBA Nx4, où N est la longueur de la palette de couleurs.

print('viridis.colors', viridis.colors)
print('viridis(range(8))', viridis(range(8)))
print('viridis(np.linspace(0, 1, 8))', viridis(np.linspace(0, 1, 8)))
viridis.colors [[0.267004 0.004874 0.329415 1.      ]
 [0.275191 0.194905 0.496005 1.      ]
 [0.212395 0.359683 0.55171  1.      ]
 [0.153364 0.497    0.557724 1.      ]
 [0.122312 0.633153 0.530398 1.      ]
 [0.288921 0.758394 0.428426 1.      ]
 [0.626579 0.854645 0.223353 1.      ]
 [0.993248 0.906157 0.143936 1.      ]]
viridis(range(8)) [[0.267004 0.004874 0.329415 1.      ]
 [0.275191 0.194905 0.496005 1.      ]
 [0.212395 0.359683 0.55171  1.      ]
 [0.153364 0.497    0.557724 1.      ]
 [0.122312 0.633153 0.530398 1.      ]
 [0.288921 0.758394 0.428426 1.      ]
 [0.626579 0.854645 0.223353 1.      ]
 [0.993248 0.906157 0.143936 1.      ]]
viridis(np.linspace(0, 1, 8)) [[0.267004 0.004874 0.329415 1.      ]
 [0.275191 0.194905 0.496005 1.      ]
 [0.212395 0.359683 0.55171  1.      ]
 [0.153364 0.497    0.557724 1.      ]
 [0.122312 0.633153 0.530398 1.      ]
 [0.288921 0.758394 0.428426 1.      ]
 [0.626579 0.854645 0.223353 1.      ]
 [0.993248 0.906157 0.143936 1.      ]]

La palette de couleurs est une table de recherche, donc le "suréchantillonnage" de la palette de couleurs renvoie l'interpolation du voisin le plus proche (notez les couleurs répétées dans la liste ci-dessous)

print('viridis(np.linspace(0, 1, 12))', viridis(np.linspace(0, 1, 12)))
viridis(np.linspace(0, 1, 12)) [[0.267004 0.004874 0.329415 1.      ]
 [0.267004 0.004874 0.329415 1.      ]
 [0.275191 0.194905 0.496005 1.      ]
 [0.212395 0.359683 0.55171  1.      ]
 [0.212395 0.359683 0.55171  1.      ]
 [0.153364 0.497    0.557724 1.      ]
 [0.122312 0.633153 0.530398 1.      ]
 [0.288921 0.758394 0.428426 1.      ]
 [0.288921 0.758394 0.428426 1.      ]
 [0.626579 0.854645 0.223353 1.      ]
 [0.993248 0.906157 0.143936 1.      ]
 [0.993248 0.906157 0.143936 1.      ]]

LinearSegmentedColormap #

LinearSegmentedColormaps n'ont pas d' .colorsattribut. Cependant, on peut toujours appeler la palette de couleurs avec un tableau d'entiers, ou avec un tableau flottant entre 0 et 1.

copper = mpl.colormaps['copper'].resampled(8)

print('copper(range(8))', copper(range(8)))
print('copper(np.linspace(0, 1, 8))', copper(np.linspace(0, 1, 8)))
copper(range(8)) [[0.         0.         0.         1.        ]
 [0.17647055 0.1116     0.07107143 1.        ]
 [0.35294109 0.2232     0.14214286 1.        ]
 [0.52941164 0.3348     0.21321429 1.        ]
 [0.70588219 0.4464     0.28428571 1.        ]
 [0.88235273 0.558      0.35535714 1.        ]
 [1.         0.6696     0.42642857 1.        ]
 [1.         0.7812     0.4975     1.        ]]
copper(np.linspace(0, 1, 8)) [[0.         0.         0.         1.        ]
 [0.17647055 0.1116     0.07107143 1.        ]
 [0.35294109 0.2232     0.14214286 1.        ]
 [0.52941164 0.3348     0.21321429 1.        ]
 [0.70588219 0.4464     0.28428571 1.        ]
 [0.88235273 0.558      0.35535714 1.        ]
 [1.         0.6696     0.42642857 1.        ]
 [1.         0.7812     0.4975     1.        ]]

Création de palettes de couleurs répertoriées #

La création d'une palette de couleurs est essentiellement l'opération inverse de ce qui précède où nous fournissons une liste ou un tableau de spécifications de couleurs ListedColormappour créer une nouvelle palette de couleurs.

Avant de poursuivre le didacticiel, définissons une fonction d'assistance qui prend une ou plusieurs palettes de couleurs en entrée, crée des données aléatoires et applique la ou les palettes de couleurs à un tracé d'image de cet ensemble de données.

def plot_examples(colormaps):
    """
    Helper function to plot data with associated colormap.
    """
    np.random.seed(19680801)
    data = np.random.randn(30, 30)
    n = len(colormaps)
    fig, axs = plt.subplots(1, n, figsize=(n * 2 + 2, 3),
                            constrained_layout=True, squeeze=False)
    for [ax, cmap] in zip(axs.flat, colormaps):
        psm = ax.pcolormesh(data, cmap=cmap, rasterized=True, vmin=-4, vmax=4)
        fig.colorbar(psm, ax=ax)
    plt.show()

Dans le cas le plus simple, nous pourrions taper une liste de noms de couleurs pour créer une palette de couleurs à partir de ceux-ci.

cmap = ListedColormap(["darkorange", "gold", "lawngreen", "lightseagreen"])
plot_examples([cmap])
manipulation de la palette de couleurs

En fait, cette liste peut contenir n'importe quelle spécification de couleur Matplotlib valide . Les tableaux numpy Nx4 sont particulièrement utiles pour créer des palettes de couleurs personnalisées. Parce qu'avec la variété d'opérations numpy que nous pouvons faire sur un tel tableau, la menuiserie de nouvelles palettes de couleurs à partir de palettes de couleurs existantes devient assez simple.

Par exemple, supposons que nous voulions rendre roses les 25 premières entrées d'une palette de couleurs "viridis" de 256 longueurs pour une raison quelconque :

viridis = mpl.colormaps['viridis'].resampled(256)
newcolors = viridis(np.linspace(0, 1, 256))
pink = np.array([248/256, 24/256, 148/256, 1])
newcolors[:25, :] = pink
newcmp = ListedColormap(newcolors)

plot_examples([viridis, newcmp])
manipulation de la palette de couleurs

Nous pouvons réduire la plage dynamique d'une palette de couleurs ; ici, nous choisissons la moitié centrale de la palette de couleurs. Notez cependant que viridis étant une palette de couleurs répertoriée, nous nous retrouverons avec 128 valeurs discrètes au lieu des 256 valeurs qui figuraient dans la palette de couleurs d'origine. Cette méthode n'interpole pas dans l'espace colorimétrique pour ajouter de nouvelles couleurs.

viridis_big = mpl.colormaps['viridis']
newcmp = ListedColormap(viridis_big(np.linspace(0.25, 0.75, 128)))
plot_examples([viridis, newcmp])
manipulation de la palette de couleurs

et nous pouvons facilement concaténer deux palettes de couleurs :

top = mpl.colormaps['Oranges_r'].resampled(128)
bottom = mpl.colormaps['Blues'].resampled(128)

newcolors = np.vstack((top(np.linspace(0, 1, 128)),
                       bottom(np.linspace(0, 1, 128))))
newcmp = ListedColormap(newcolors, name='OrangeBlue')
plot_examples([viridis, newcmp])
manipulation de la palette de couleurs

Bien sûr, nous n'avons pas besoin de partir d'une palette de couleurs nommée, nous avons juste besoin de créer le tableau Nx4 pour passer à ListedColormap. Ici, nous créons une palette de couleurs allant du marron (RVB : 90, 40, 40) au blanc (RVB : 255, 255, 255).

N = 256
vals = np.ones((N, 4))
vals[:, 0] = np.linspace(90/256, 1, N)
vals[:, 1] = np.linspace(40/256, 1, N)
vals[:, 2] = np.linspace(40/256, 1, N)
newcmp = ListedColormap(vals)
plot_examples([viridis, newcmp])
manipulation de la palette de couleurs

Création de palettes de couleurs segmentées linéaires #

La LinearSegmentedColormapclasse spécifie les palettes de couleurs à l'aide de points d'ancrage entre lesquels les valeurs RVB(A) sont interpolées.

Le format pour spécifier ces palettes de couleurs permet des discontinuités aux points d'ancrage. Chaque point d'ancrage est spécifié sous la forme d'une ligne dans une matrice de la forme , où est l'ancre, et et sont les valeurs de la couleur de chaque côté du point d'ancrage.[x[i] yleft[i] yright[i]]x[i]yleft[i]yright[i]

S'il n'y a pas de discontinuités, alors :yleft[i] == yright[i]

cdict = {'red':   [[0.0,  0.0, 0.0],
                   [0.5,  1.0, 1.0],
                   [1.0,  1.0, 1.0]],
         'green': [[0.0,  0.0, 0.0],
                   [0.25, 0.0, 0.0],
                   [0.75, 1.0, 1.0],
                   [1.0,  1.0, 1.0]],
         'blue':  [[0.0,  0.0, 0.0],
                   [0.5,  0.0, 0.0],
                   [1.0,  1.0, 1.0]]}


def plot_linearmap(cdict):
    newcmp = LinearSegmentedColormap('testCmap', segmentdata=cdict, N=256)
    rgba = newcmp(np.linspace(0, 1, 256))
    fig, ax = plt.subplots(figsize=(4, 3), constrained_layout=True)
    col = ['r', 'g', 'b']
    for xx in [0.25, 0.5, 0.75]:
        ax.axvline(xx, color='0.7', linestyle='--')
    for i in range(3):
        ax.plot(np.arange(256)/256, rgba[:, i], color=col[i])
    ax.set_xlabel('index')
    ax.set_ylabel('RGB')
    plt.show()

plot_linearmap(cdict)
manipulation de la palette de couleurs

Afin de faire une discontinuité à un point d'ancrage, la troisième colonne est différente de la seconde. La matrice pour chacun des "rouge", "vert", "bleu" et éventuellement "alpha" est configurée comme suit :

cdict['red'] = [...
                [x[i]      yleft[i]     yright[i]],
                [x[i+1]    yleft[i+1]   yright[i+1]],
               ...]

et pour les valeurs passées à la palette de couleurs entre x[i]et x[i+1], l'interpolation est entre yright[i]et yleft[i+1].

Dans l'exemple ci-dessous il y a une discontinuité en rouge à 0.5. L'interpolation entre 0 et 0,5 passe de 0,3 à 1, et entre 0,5 et 1 de 0,9 à 1. Notez que , et sont tous deux superflus pour l'interpolation car (c'est-à-dire ) est la valeur à gauche de 0, et ( c'est-à-dire, ) est la valeur à droite de 1, qui sont en dehors du domaine de mappage des couleurs.red[0, 1]red[2, 2]red[0, 1]yleft[0]red[2, 2]yright[2]

cdict['red'] = [[0.0,  0.0, 0.3],
                [0.5,  1.0, 0.9],
                [1.0,  1.0, 1.0]]
plot_linearmap(cdict)
manipulation de la palette de couleurs

Créer directement une palette de couleurs segmentée à partir d'une liste #

L'approche décrite ci-dessus est très polyvalente, mais certes un peu lourde à mettre en œuvre. Pour certains cas de base, l'utilisation de LinearSegmentedColormap.from_listpeut être plus simple. Cela crée une palette de couleurs segmentée avec des espacements égaux à partir d'une liste de couleurs fournie.

colors = ["darkorange", "gold", "lawngreen", "lightseagreen"]
cmap1 = LinearSegmentedColormap.from_list("mycmap", colors)

Si vous le souhaitez, les nœuds de la palette de couleurs peuvent être donnés sous forme de nombres compris entre 0 et 1. Par exemple, on pourrait faire en sorte que la partie rougeâtre prenne plus de place dans la palette de couleurs.

nodes = [0.0, 0.4, 0.8, 1.0]
cmap2 = LinearSegmentedColormap.from_list("mycmap", list(zip(nodes, colors)))

plot_examples([cmap1, cmap2])
manipulation de la palette de couleurs

Durée totale d'exécution du script : (0 minutes 4,802 secondes)

Galerie générée par Sphinx-Gallery