Impostazione di colori diversi per ogni serie nel grafico a dispersione su matplotlib


162

Supponiamo che io abbia tre set di dati:

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

Posso spargere questo diagramma:

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

Come posso farlo con 10 set?

Ho cercato questo e ho trovato qualche riferimento a ciò che sto chiedendo.

Modifica: chiarendo (si spera) la mia domanda

Se chiamo scatter più volte, posso solo impostare lo stesso colore su ogni scatter. Inoltre, so di poter impostare manualmente una matrice di colori, ma sono sicuro che esiste un modo migliore per farlo. La mia domanda è quindi: "Come posso spargere automaticamente i miei diversi set di dati, ognuno con un colore diverso.

Se ciò aiuta, posso facilmente assegnare un numero univoco a ciascun set di dati.


1
Qual è il quesiton qui? Anche il colore può essere un array, ma cosa non puoi risolvere chiamando semplicemente scatter più volte?
seberg,

1
Se chiamo scatter più volte, ottengo gli stessi colori. Aggiornerò la mia domanda.
Yotam,

Risposte:


269

Non so cosa intendi per "manualmente". Puoi scegliere una colormap e creare un array di colori abbastanza facilmente:

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)

Grafico a matplotlib con colori diversi

Oppure puoi creare il tuo ciclatore di colori usando itertools.cyclee specificando i colori su cui vuoi passare, usando nextper ottenere quello che desideri. Ad esempio, con 3 colori:

import itertools

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

Matplotlib grafico con solo 3 colori

Pensaci, forse è più pulito non usarlo nemmeno zipcon il primo:

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

1
+1. Un ciclo itertools probabilmente non è una buona idea in questa situazione, poiché finirebbe con più set di dati dello stesso colore.
David Robinson,

1
@DavidRobinson: non se si specificano tutti e dieci, anche se concordo sul fatto che il ciclismo in qualche modo sconfigge lo scopo lì:: ^)
DSM

Precisamente, quindi non è un ciclo :)
David Robinson,

4
@macrocosme: funziona per me. Aggiungendo plt.legend(['c{}'.format(i) for i in range(len(ys))], loc=2, bbox_to_anchor=(1.05, 1), borderaxespad=0., fontsize=11)in basso quanto sopra mi dà una leggenda con i colori.
DSM,

la soluzione itertools è ottima quando vuoi evitare alcuni colori. Nel mio caso, poiché lo sfondo è nero, voglio evitare il nero.
Fabrizio,

50

Il modo normale di tracciare grafici con punti in colori diversi in matplotlib è passare un elenco di colori come parametro.

Per esempio:

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

3 colori

Quando si dispone di un elenco di elenchi e si desidera che siano colorati per elenco. Penso che il modo più elegante sia quello suggerito da @DSM, basta fare un loop per fare più chiamate da spargere.

Ma se per qualche motivo volessi farlo con una sola chiamata, puoi creare un grande elenco di colori, con una comprensione dell'elenco e un po 'di divisione del pavimento:

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)

Tutto tracciato

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

19

Una soluzione semplice

Se hai un solo tipo di raccolte (ad es. Scatter senza barre di errore) puoi anche cambiare i colori dopo che li hai tracciati, a volte questo è più facile da eseguire.

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)

L'unico pezzo di codice che ti serve:

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

L'output offre colori diversi anche quando si hanno molti grafici a dispersione diversi nella stessa trama secondaria.

inserisci qui la descrizione dell'immagine


è fantastico, ma come potresti aggiungere errorbar dello stesso colore con questa funzione? @GM
PEBKAC,

1
Ciao @PEBKAC, grazie per averlo sottolineato, questo pomeriggio ho provato a farlo funzionare anche in quel caso, ma non sono riuscito a trovare alcuna soluzione, quindi ho modificato la domanda e avvisato gli altri utenti. Grazie!
GM

Ciao @GM, mi dispiace di aver pubblicato alcuni commenti prima di aver finalizzato la soluzione, che è descritta qui: stackoverflow.com/q/51444364/7541421
PEBKAC

1
Ho usato un altro metodo per assegnare i colori per ogni serie in un diagramma a dispersione. Ora funziona, sfortunatamente non ho potuto procedere con la tua soluzione elegante quando si trattava di errorbar, tuttavia sono davvero grato per il tuo post super utile! Saluti!
PEBKAC,

7

Puoi sempre usare la plot()funzione in questo modo:

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

trama come dispersione ma cambia colore


6

Questa domanda è un po 'complicata prima di gennaio 2013 e matplotlib 1.3.1 (agosto 2013), che è la versione stabile più vecchia che puoi trovare sul sito Web matpplotlib. Ma dopo è abbastanza banale.

Perché la versione attuale di matplotlib.pylab.scatter supporta: matrice di stringhe nome colore, matrice di numero float con mappa colori, matrice di RGB o RGBA.

questa risposta è dedicata alla passione infinita di @ Oxinabox per la correzione della versione 2013 di me stesso nel 2015.


hai due possibilità di usare il comando scatter con più colori in una singola chiamata.

  1. come pylab.scattersupporto comandi usa l'array RGBA per fare qualsiasi colore tu voglia;

  2. all'inizio del 2013, non c'è modo di farlo, poiché il comando supporta solo il colore singolo per l'intera raccolta dei punti di dispersione. Quando stavo facendo il mio progetto a 10000 linee, ho trovato una soluzione generale per aggirarlo. quindi è molto appiccicoso, ma posso farlo in qualsiasi forma, colore, dimensione e trasparente. questo trucco potrebbe essere applicato anche per disegnare la raccolta di percorsi, la raccolta di linee ....

il codice si ispira anche al codice sorgente di pyplot.scatter , ho appena duplicato ciò che scatter fa senza attivarlo per disegnare.

il comando pyplot.scatterrestituisce un PatchCollectionoggetto, nel file "matplotlib / collections.py" una variabile privata _facecolorsin Collectionclasse e un metodo set_facecolors.

quindi ogni volta che hai punti scatter da disegnare puoi farlo:

# 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()

quindi è un po 'complicato da leggere e nel 2013 ho usato Python per 1 anno. quindi perché la gente vorrebbe sapere come farlo? dopo averlo fatto funzionare, non mi preoccupo mai di guardarlo di nuovo. il mio progetto era di disegnare molta visualizzazione, con il codice sopra, il flusso di lavoro è stato semplificato.
Hualin,

1

Questo funziona per me:

per ogni serie, utilizzare un generatore di colori rgb casuale

c = color[np.random.random_sample(), np.random.random_sample(), np.random.random_sample()]

Non so qual è il tuo colore variabile, ma usando il tuo approccio è possibile fare qualcosa di simile: plt.scatter(your values to the graph, color= (np.random.random_sample(), np.random.random_sample(), np.random.random_sample()) ). Hai citato un generatore RGB e hai dichiarato un elenco RGB, i generatori sono dichiarati tra '()'
Joel Carneiro

0

Una soluzione MOLTO più veloce per un set di dati di grandi dimensioni e un numero limitato di colori è l'uso di Panda e la funzione 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()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.