web-dev-qa-db-fra.com

Créer sa propre palette de couleurs à l'aide de matplotlib et d'une échelle de couleur de tracé

J'ai le problème suivant, je veux créer ma propre palette de couleurs (rouge-mélange-violet-mélange-bleu) qui mappe sur des valeurs comprises entre -2 et +2 et je veux l'utiliser pour colorer des points dans mon tracé. L'intrigue devrait alors avoir les couleurs à droite.

C’est ainsi que j’ai créé la carte jusqu’à présent. Mais je ne suis pas vraiment sûr que ça mélange les couleurs.

cmap = matplotlib.colors.ListedColormap(["red","Violet","blue"], name='from_list', N=None)
m = cm.ScalarMappable(norm=norm, cmap=cmap)


Ainsi, je mappe les couleurs sur les valeurs.

colors = itertools.cycle([m.to_rgba(1.22), ..])


Puis je fais le tracé:

for i in range(0, len(array_dg)):
  plt.plot(array_dg[i], markers.next(),alpha=alpha[i], c=colors.next())


Mes problèmes sont:
1. Je ne peux pas tracer l'échelle de couleur.
2. Je ne suis pas tout à fait sûr que mon échelle crée une échelle de couleurs continue (lisse).

52
Trollbrot

Il existe un exemple illustratif de comment créer des tables de couleurs personnalisées ici . La docstring est essentielle pour comprendre le sens de cdict. Une fois que vous obtenez cela sous votre ceinture, vous pouvez utiliser un cdict comme ceci:

cdict = {'red':   ((0.0, 1.0, 1.0), 
                   (0.1, 1.0, 1.0),  # red 
                   (0.4, 1.0, 1.0),  # Violet
                   (1.0, 0.0, 0.0)), # blue

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

         'blue':  ((0.0, 0.0, 0.0),
                   (0.1, 0.0, 0.0),  # red
                   (0.4, 1.0, 1.0),  # Violet
                   (1.0, 1.0, 0.0))  # blue
          }

Bien que le format cdict vous apporte beaucoup de souplesse, je trouve que son format est plutôt peu intuitif pour les gradients simples. Voici une fonction utilitaire permettant de générer des LinearSegmentedColormaps simples:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors


def make_colormap(seq):
    """Return a LinearSegmentedColormap
    seq: a sequence of floats and RGB-tuples. The floats should be increasing
    and in the interval (0,1).
    """
    seq = [(None,) * 3, 0.0] + list(seq) + [1.0, (None,) * 3]
    cdict = {'red': [], 'green': [], 'blue': []}
    for i, item in enumerate(seq):
        if isinstance(item, float):
            r1, g1, b1 = seq[i - 1]
            r2, g2, b2 = seq[i + 1]
            cdict['red'].append([item, r1, r2])
            cdict['green'].append([item, g1, g2])
            cdict['blue'].append([item, b1, b2])
    return mcolors.LinearSegmentedColormap('CustomMap', cdict)


c = mcolors.ColorConverter().to_rgb
rvb = make_colormap(
    [c('red'), c('Violet'), 0.33, c('Violet'), c('blue'), 0.66, c('blue')])
N = 1000
array_dg = np.random.uniform(0, 10, size=(N, 2))
colors = np.random.uniform(-2, 2, size=(N,))
plt.scatter(array_dg[:, 0], array_dg[:, 1], c=colors, cmap=rvb)
plt.colorbar()
plt.show()

enter image description here


Au fait, le for-loop

for i in range(0, len(array_dg)):
  plt.plot(array_dg[i], markers.next(),alpha=alpha[i], c=colors.next())

trace un point pour chaque appel à plt.plot. Cela fonctionnera pour un petit nombre de points, mais deviendra extrêmement lent pour de nombreux points. plt.plot ne peut dessiner qu’en une couleur, mais plt.scatter peut attribuer une couleur différente à chaque point. Ainsi, plt.scatter est la voie à suivre.

75
unutbu

Puisque les méthodes utilisées dans les autres réponses semblent assez compliquées pour une tâche aussi facile, voici une nouvelle réponse:

Au lieu d'un ListedColormap, qui produit une palette de couleurs discrète, vous pouvez utiliser un LinearSegmentedColormap. Ceci peut facilement être créé à partir d'une liste en utilisant le from_list méthode.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors

x,y,c = Zip(*np.random.Rand(30,3)*4-2)

norm=plt.Normalize(-2,2)
cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", ["red","Violet","blue"])

plt.scatter(x,y,c=c, cmap=cmap, norm=norm)
plt.colorbar()
plt.show()

enter image description here


Plus généralement, si vous avez une liste de valeurs (par exemple, [-2., -1, 2]) et les couleurs correspondantes (par exemple ["red","Violet","blue"]), de sorte que la valeur ne corresponde à la couleur n, vous pouvez normaliser les valeurs et les fournir sous forme de n-uplets à la commande from_list méthode.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors

x,y,c = Zip(*np.random.Rand(30,3)*4-2)

cvals  = [-2., -1, 2]
colors = ["red","Violet","blue"]

norm=plt.Normalize(min(cvals),max(cvals))
tuples = list(Zip(map(norm,cvals), colors))
cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", tuples)

plt.scatter(x,y,c=c, cmap=cmap, norm=norm)
plt.colorbar()
plt.show()

enter image description here

34

Si vous souhaitez automatiser la création d'une palette de couleurs divergente personnalisée couramment utilisée pour tracés de surface , ce module associé à la méthode @unutbu a bien fonctionné pour moi.

def diverge_map(high=(0.565, 0.392, 0.173), low=(0.094, 0.310, 0.635)):
    '''
    low and high are colors that will be used for the two
    ends of the spectrum. they can be either color strings
    or rgb color tuples
    '''
    c = mcolors.ColorConverter().to_rgb
    if isinstance(low, basestring): low = c(low)
    if isinstance(high, basestring): high = c(high)
    return make_colormap([low, c('white'), 0.5, c('white'), high])

Les valeurs hautes et basses peuvent être des noms de couleur de chaîne ou des nuplets rgb. C’est le résultat obtenu avec le démo de surfaceenter image description here

13
Steven C. Howell