Establecer un color diferente para cada serie en un diagrama de dispersión en matplotlib


162

Supongamos que tengo tres conjuntos de datos:

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

Puedo dispersar esto:

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

¿Cómo puedo hacer esto con 10 juegos?

Busqué esto y pude encontrar alguna referencia a lo que estoy preguntando.

Editar: aclarando (con suerte) mi pregunta

Si llamo a dispersión varias veces, solo puedo establecer el mismo color en cada dispersión. Además, sé que puedo configurar una matriz de colores manualmente, pero estoy seguro de que hay una mejor manera de hacerlo. Mi pregunta es, entonces, "¿Cómo puedo dispersar automáticamente mis diversos conjuntos de datos, cada uno con un color diferente?

Si eso ayuda, puedo asignar fácilmente un número único a cada conjunto de datos.


1
¿Cuál es la pregunta aquí? El color también puede ser una matriz, pero ¿qué no puede resolver con solo llamar a dispersión varias veces?
seberg

1
Si llamo a dispersión varias veces, obtengo los mismos colores. Actualizaré mi pregunta.
Yotam

Respuestas:


269

No sé a qué te refieres con "manualmente". Puede elegir un mapa de colores y hacer una matriz de colores con bastante facilidad:

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)

Gráfico de Matplotlib con diferentes colores.

O puede hacer su propio ciclador de color usando itertools.cycley especificando los colores que desea recorrer, usando nextpara obtener el que desea. Por ejemplo, con 3 colores:

import itertools

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

Gráfico Matplotlib con solo 3 colores.

Ahora que lo pienso, tal vez sea más limpio no usar zipcon el primero tampoco:

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

1
+1. Sin embargo, un ciclo de itertools probablemente no sea una buena idea en esta situación, ya que terminaría con múltiples conjuntos de datos que tienen el mismo color.
David Robinson

1
@DavidRobinson: no si especificas los diez, aunque estoy de acuerdo con que el ciclismo derrota el propósito allí ...: ^)
DSM

Precisamente, entonces no es un ciclo :)
David Robinson

44
@macrocosme: funciona para mí. Agregando plt.legend(['c{}'.format(i) for i in range(len(ys))], loc=2, bbox_to_anchor=(1.05, 1), borderaxespad=0., fontsize=11)al fondo lo anterior me da una leyenda con colores.
DSM

La solución itertools es excelente cuando desea evitar algunos colores. En mi caso, dado que el fondo es negro, quiero evitar el negro.
Fabrizio

50

La forma normal de trazar trazados con puntos en diferentes colores en matplotlib es pasar una lista de colores como parámetro.

P.ej:

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

3 colores

Cuando tienes una lista de listas y quieres que estén coloreadas por lista. Creo que la forma más elegante es la sugerida por @DSM, solo haz un bucle haciendo múltiples llamadas para dispersar.

Pero si por alguna razón desea hacerlo con solo una llamada, puede hacer una gran lista de colores, con una comprensión de la lista y un poco de división del piso:

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)

Todo trazado

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 solución fácil

Si solo tiene un tipo de colecciones (por ejemplo, dispersión sin barras de error), también puede cambiar los colores después de haberlos trazado, esto a veces es más fácil de realizar.

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)

El único código que necesitas:

#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 salida le ofrece diferentes colores incluso cuando tiene muchos diagramas de dispersión diferentes en la misma subtrama.

ingrese la descripción de la imagen aquí


eso es genial, pero ¿cómo agregaría, por ejemplo, barras de error con el mismo color con esta función? @GM
PEBKAC

1
Hola @PEBKAC, gracias por señalarlo, he intentado esta tarde hacer que funcione también en ese caso, pero no pude encontrar ninguna solución, así que edité la pregunta y advertí a los otros usuarios. ¡Gracias!
GM

Hola @GM, lo siento, publiqué algunos comentarios antes de finalizar la solución, que se describe aquí: stackoverflow.com/q/51444364/7541421
PEBKAC

1
Usé otro método para asignar los colores para cada serie en un diagrama de dispersión. Ahora funciona, desafortunadamente no pude continuar con su elegante solución cuando se trataba de barras de error, ¡aún así estoy realmente agradecido por su publicación súper útil! ¡Salud!
PEBKAC

7

Siempre puedes usar la plot()función así:

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

trazar como dispersión pero cambia de color


6

Esta pregunta es un poco complicada antes de enero de 2013 y matplotlib 1.3.1 (agosto de 2013), que es la versión estable más antigua que puedes encontrar en el sitio web de matpplotlib. Pero después de eso es bastante trivial.

Porque la versión actual de matplotlib.pylab.scatter asignación soporte: matriz de cadena de nombre de color, matriz de número flotante con mapa de color, matriz de RGB o RGBA.

Esta respuesta está dedicada a la pasión infinita de @ Oxinabox por corregir la versión 2013 de mí mismo en 2015.


Tiene dos opciones para usar el comando de dispersión con múltiples colores en una sola llamada.

  1. como pylab.scattersoporte de comandos, use la matriz RGBA para hacer el color que desee;

  2. A principios de 2013, no hay forma de hacerlo, ya que el comando solo admite un solo color para toda la colección de puntos de dispersión. Cuando estaba haciendo mi proyecto de 10000 líneas, descubrí una solución general para evitarlo. es muy pegajoso, pero puedo hacerlo en cualquier forma, color, tamaño y transparencia. Este truco también podría aplicarse a la colección de trazado de dibujo, colección de líneas ...

el código también está inspirado en el código fuente de pyplot.scatter , acabo de duplicar lo que hace la dispersión sin activarlo para dibujar.

el comando pyplot.scatterdevuelve un PatchCollectionObject, en el archivo "matplotlib / collections.py", una variable privada _facecolorsen Collectionclase y un método set_facecolors.

así que cada vez que tenga puntos de dispersión para dibujar, puede hacer esto:

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

es un poco complicado de leer y en 2013 usé python durante 1 año. Entonces, ¿por qué la gente querría saber cómo hacerlo? después de hacerlo funcionar, nunca me molesto en volver a mirarlo. mi proyecto consistía en dibujar mucha visualización, con el código anterior, el flujo de trabajo se simplificó.
Hualin

1

Esto funciona para mi:

para cada serie, use un generador de color rgb aleatorio

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

No sé cuál es su color variable, pero utilizando el enfoque es posible hacer algo como: plt.scatter(your values to the graph, color= (np.random.random_sample(), np.random.random_sample(), np.random.random_sample()) ). Usted mencionó un generador RGB y declaró una lista RGB, los generadores se declaran entre '()'
Joel Carneiro

0

Una solución MUCHO más rápida para un gran conjunto de datos y un número limitado de colores es el uso de Pandas y la función 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()
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.