Définition d'une couleur différente pour chaque série dans le nuage de points sur matplotlib

162

Supposons que j'ai trois ensembles de données:

X = [1,2,3,4]
Y1 = [4,8,12,16]
Y2 = [1,4,9,16]

Je peux disperser le diagramme ceci:

from matplotlib import pyplot as plt
plt.scatter(X,Y1,color='red')
plt.scatter(X,Y2,color='blue')
plt.show()

Comment puis-je faire cela avec 10 ensembles?

J'ai cherché ceci et j'ai pu trouver n'importe quelle référence à ce que je demande.

Edit: clarifier (j'espère) ma question

Si j'appelle scatter plusieurs fois, je ne peux définir la même couleur que sur chaque scatter. De plus, je sais que je peux définir un tableau de couleurs manuellement, mais je suis sûr qu'il existe une meilleure façon de le faire. Ma question est alors: «Comment puis-je automatiquement disperser mes différents ensembles de données, chacun avec une couleur différente.

Si cela aide, je peux facilement attribuer un numéro unique à chaque ensemble de données.

Yotam
la source
1
Quelle est la question ici? La couleur peut également être un tableau, mais que ne pouvez-vous pas résoudre en appelant simplement scatter plusieurs fois?
seberg le
1
Si j'appelle scatter plusieurs fois, j'obtiens les mêmes couleurs. Je vais mettre à jour ma question.
Yotam le

Réponses:

269

Je ne sais pas ce que vous entendez par «manuellement». Vous pouvez choisir une carte de couleurs et créer un tableau de couleurs assez facilement:

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

x = np.arange(10)
ys = [i+x+(i*x)**2 for i in range(10)]

colors = cm.rainbow(np.linspace(0, 1, len(ys)))
for y, c in zip(ys, colors):
    plt.scatter(x, y, color=c)

Graphique Matplotlib avec différentes couleurs

Ou vous pouvez créer votre propre cycleur de couleurs en utilisant itertools.cycleet en spécifiant les couleurs sur lesquelles vous voulez boucler, en utilisant nextpour obtenir celle que vous voulez. Par exemple, avec 3 couleurs:

import itertools

colors = itertools.cycle(["r", "b", "g"])
for y in ys:
    plt.scatter(x, y, color=next(colors))

Graphique Matplotlib avec seulement 3 couleurs

À bien y penser, il est peut-être plus propre de ne pas non plus utiliser ziple premier:

colors = iter(cm.rainbow(np.linspace(0, 1, len(ys))))
for y in ys:
    plt.scatter(x, y, color=next(colors))
DSM
la source
1
+1. Un cycle itertools n'est probablement pas une bonne idée dans cette situation, car il aboutirait à plusieurs ensembles de données ayant la même couleur.
David Robinson
1
@DavidRobinson: pas si vous spécifiez tous les dix, même si je suis d'accord que le cyclisme bat en quelque sorte l'objectif là-bas ..: ^)
DSM
Justement - alors ce n'est pas un cycle :)
David Robinson
4
@macrocosme: fonctionne pour moi. Ajouter plt.legend(['c{}'.format(i) for i in range(len(ys))], loc=2, bbox_to_anchor=(1.05, 1), borderaxespad=0., fontsize=11)en bas ce qui précède me donne une légende avec des couleurs.
DSM
la solution itertools est idéale lorsque vous souhaitez éviter certaines couleurs. Dans mon cas puisque le fond est noir je veux éviter le noir.
Fabrizio le
50

La manière normale de tracer des tracés avec des points de différentes couleurs dans matplotlib est de passer une liste de couleurs comme paramètre.

Par exemple:

import matplotlib.pyplot
matplotlib.pyplot.scatter([1,2,3],[4,5,6],color=['red','green','blue'])

3 couleurs

Lorsque vous avez une liste de listes et que vous voulez les colorer par liste. Je pense que le moyen le plus élégant est celui suggéré par @DSM, il suffit de faire une boucle en effectuant plusieurs appels pour se disperser.

Mais si pour une raison quelconque vous vouliez le faire avec un seul appel, vous pouvez faire une grande liste de couleurs, avec une compréhension de la liste et un peu de division du revêtement de sol:

import matplotlib
import numpy as np

X = [1,2,3,4]
Ys = np.array([[4,8,12,16],
      [1,4,9,16],
      [17, 10, 13, 18],
      [9, 10, 18, 11],
      [4, 15, 17, 6],
      [7, 10, 8, 7],
      [9, 0, 10, 11],
      [14, 1, 15, 5],
      [8, 15, 9, 14],
       [20, 7, 1, 5]])
nCols = len(X)  
nRows = Ys.shape[0]

colors = matplotlib.cm.rainbow(np.linspace(0, 1, len(Ys)))

cs = [colors[i//len(X)] for i in range(len(Ys)*len(X))] #could be done with numpy's repmat
Xs=X*nRows #use list multiplication for repetition
matplotlib.pyplot.scatter(Xs,Ys.flatten(),color=cs)

Tout tracé

cs = [array([ 0.5,  0. ,  1. ,  1. ]),
 array([ 0.5,  0. ,  1. ,  1. ]),
 array([ 0.5,  0. ,  1. ,  1. ]),
 array([ 0.5,  0. ,  1. ,  1. ]),
 array([ 0.28039216,  0.33815827,  0.98516223,  1.        ]),
 array([ 0.28039216,  0.33815827,  0.98516223,  1.        ]),
 array([ 0.28039216,  0.33815827,  0.98516223,  1.        ]),
 array([ 0.28039216,  0.33815827,  0.98516223,  1.        ]),
 ...
 array([  1.00000000e+00,   1.22464680e-16,   6.12323400e-17,
          1.00000000e+00]),
 array([  1.00000000e+00,   1.22464680e-16,   6.12323400e-17,
          1.00000000e+00]),
 array([  1.00000000e+00,   1.22464680e-16,   6.12323400e-17,
          1.00000000e+00]),
 array([  1.00000000e+00,   1.22464680e-16,   6.12323400e-17,
          1.00000000e+00])]
Lyndon White
la source
19

Une solution facile

Si vous n'avez qu'un seul type de collections (par exemple, dispersion sans barres d'erreur), vous pouvez également changer les couleurs après les avoir tracées, c'est parfois plus facile à réaliser.

import matplotlib.pyplot as plt
from random import randint
import numpy as np

#Let's generate some random X, Y data X = [ [frst group],[second group] ...]
X = [ [randint(0,50) for i in range(0,5)] for i in range(0,24)]
Y = [ [randint(0,50) for i in range(0,5)] for i in range(0,24)]
labels = range(1,len(X)+1)

fig = plt.figure()
ax = fig.add_subplot(111)
for x,y,lab in zip(X,Y,labels):
        ax.scatter(x,y,label=lab)

Le seul morceau de code dont vous avez besoin:

#Now this is actually the code that you need, an easy fix your colors just cut and paste not you need ax.
colormap = plt.cm.gist_ncar #nipy_spectral, Set1,Paired  
colorst = [colormap(i) for i in np.linspace(0, 0.9,len(ax.collections))]       
for t,j1 in enumerate(ax.collections):
    j1.set_color(colorst[t])


ax.legend(fontsize='small')

La sortie vous donne des couleurs différentes même lorsque vous avez de nombreux nuages ​​de points différents dans le même sous-diagramme.

entrez la description de l'image ici

GM
la source
c'est super mais comment ajouterais-tu par exemple des barres d'erreurs de la même couleur avec cette fonction? @GM
PEBKAC
1
Bonjour @PEBKAC, merci de l'avoir signalé, j'ai essayé cet après-midi de le faire fonctionner également dans ce cas, mais je n'ai pas trouvé de solution, j'ai donc édité la question et averti les autres utilisateurs. Merci!
GM
Salut @GM, désolé j'ai posté quelques commentaires avant d'avoir finalisé la solution, qui est décrite ici: stackoverflow.com/q/51444364/7541421
PEBKAC
1
J'ai utilisé une autre méthode pour attribuer les couleurs de chaque série dans un nuage de points. Maintenant, cela fonctionne, malheureusement, je n'ai pas pu continuer avec votre solution élégante en ce qui concerne les barres d'erreurs, mais je suis vraiment reconnaissant pour votre message très utile! À votre santé!
PEBKAC
7

Vous pouvez toujours utiliser la plot()fonction comme ceci:

import matplotlib.pyplot as plt

import numpy as np

x = np.arange(10)
ys = [i+x+(i*x)**2 for i in range(10)]
plt.figure()
for y in ys:
    plt.plot(x, y, 'o')
plt.show()

tracer comme nuage de points mais change de couleur

Ophir Carmi
la source
6

Cette question est un peu délicate avant janvier 2013 et matplotlib 1.3.1 (août 2013), qui est la plus ancienne version stable que vous pouvez trouver sur le site matpplotlib. Mais après cela, c'est assez trivial.

Parce que la version actuelle du matplotlib.pylab.scattersupport attribue: tableau de chaîne de nom de couleur, tableau de nombre flottant avec carte de couleur, tableau de RVB ou RVBA.

cette réponse est dédiée à la passion sans fin de @ Oxinabox pour corriger la version 2013 de moi-même en 2015.


vous avez deux options pour utiliser la commande scatter avec plusieurs couleurs en un seul appel.

  1. comme pylab.scattersupport de commande, utilisez le tableau RGBA pour faire la couleur que vous voulez;

  2. au début de 2013, il n'y a aucun moyen de le faire, car la commande ne prend en charge qu'une seule couleur pour toute la collection de points de dispersion. Lorsque je faisais mon projet de 10000 lignes, j'ai trouvé une solution générale pour le contourner. donc c'est très collant, mais je peux le faire dans n'importe quelle forme, couleur, taille et transparent. cette astuce pourrait également être appliquée pour dessiner une collection de chemins, une collection de lignes ...

le code est également inspiré du code source de pyplot.scatter, je viens de dupliquer ce que fait scatter sans le déclencher pour dessiner.

la commande pyplot.scatterretourne un PatchCollectionObject, dans le fichier "matplotlib / collections.py" une variable privée _facecolorsen Collectionclasse et une méthode set_facecolors.

Ainsi, chaque fois que vous avez un point de dispersion à dessiner, vous pouvez le faire:

# rgbaArr is a N*4 array of float numbers you know what I mean
# X is a N*2 array of coordinates
# axx is the axes object that current draw, you get it from
# axx = fig.gca()

# also import these, to recreate the within env of scatter command 
import matplotlib.markers as mmarkers
import matplotlib.transforms as mtransforms
from matplotlib.collections import PatchCollection
import matplotlib.markers as mmarkers
import matplotlib.patches as mpatches


# define this function
# m is a string of scatter marker, it could be 'o', 's' etc..
# s is the size of the point, use 1.0
# dpi, get it from axx.figure.dpi
def addPatch_point(m, s, dpi):
    marker_obj = mmarkers.MarkerStyle(m)
    path = marker_obj.get_path()
    trans = mtransforms.Affine2D().scale(np.sqrt(s*5)*dpi/72.0)
    ptch = mpatches.PathPatch(path, fill = True, transform = trans)
    return ptch

patches = []
# markerArr is an array of maker string, ['o', 's'. 'o'...]
# sizeArr is an array of size float, [1.0, 1.0. 0.5...]

for m, s in zip(markerArr, sizeArr):
    patches.append(addPatch_point(m, s, axx.figure.dpi))

pclt = PatchCollection(
                patches,
                offsets = zip(X[:,0], X[:,1]),
                transOffset = axx.transData)

pclt.set_transform(mtransforms.IdentityTransform())
pclt.set_edgecolors('none') # it's up to you
pclt._facecolors = rgbaArr

# in the end, when you decide to draw
axx.add_collection(pclt)
# and call axx's parent to draw_idle()
Hualin
la source
donc c'est un peu compliqué à lire et en 2013 j'ai utilisé python pendant 1 an. alors pourquoi les gens voudraient-ils savoir comment le faire? après l'avoir fait fonctionner, je n'ai plus jamais pris la peine de le revoir. mon projet était de dessiner beaucoup de visualisation, avec le code ci-dessus, le flux de travail a été rationalisé.
Hualin
1

Cela fonctionne pour moi:

pour chaque série, utilisez un générateur de couleurs RVB aléatoire

c = color[np.random.random_sample(), np.random.random_sample(), np.random.random_sample()]
bracoo
la source
Je ne sais pas quelle est votre couleur variable, mais en utilisant votre approche , il est possible de faire quelque chose comme: plt.scatter(your values to the graph, color= (np.random.random_sample(), np.random.random_sample(), np.random.random_sample()) ). Vous avez mentionné un générateur RVB et vous avez déclaré une liste RVB, les générateurs sont déclarés entre '()'
Joel Carneiro
0

Une solution BEAUCOUP plus rapide pour un grand ensemble de données et un nombre limité de couleurs est l'utilisation de Pandas et de la fonction groupby:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import time


# a generic set of data with associated colors
nsamples=1000
x=np.random.uniform(0,10,nsamples)
y=np.random.uniform(0,10,nsamples)
colors={0:'r',1:'g',2:'b',3:'k'}
c=[colors[i] for i in np.round(np.random.uniform(0,3,nsamples),0)]

plt.close('all')

# "Fast" Scatter plotting
starttime=time.time()
# 1) make a dataframe
df=pd.DataFrame()
df['x']=x
df['y']=y
df['c']=c
plt.figure()
# 2) group the dataframe by color and loop
for g,b in df.groupby(by='c'):
    plt.scatter(b['x'],b['y'],color=g)
print('Fast execution time:', time.time()-starttime)

# "Slow" Scatter plotting
starttime=time.time()
plt.figure()
# 2) group the dataframe by color and loop
for i in range(len(x)):
    plt.scatter(x[i],y[i],color=c[i])
print('Slow execution time:', time.time()-starttime)

plt.show()
MdM
la source