Definindo cores diferentes para cada série no gráfico de dispersão no matplotlib

162

Suponha que eu tenho três conjuntos de dados:

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

Eu posso espalhar o seguinte:

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

Como posso fazer isso com 10 conjuntos?

Eu procurei por isso e poderia encontrar qualquer referência ao que estou perguntando.

Edit: esclarecendo (espero) minha pergunta

Se eu chamar dispersão várias vezes, só posso definir a mesma cor em cada dispersão. Além disso, sei que posso definir uma matriz de cores manualmente, mas tenho certeza de que há uma maneira melhor de fazer isso. Minha pergunta é: "Como posso plotar automaticamente os vários conjuntos de dados, cada um com uma cor diferente?

Se isso ajudar, posso atribuir facilmente um número único a cada conjunto de dados.

Yotam
fonte
1
Qual é o problema aqui? A cor também pode ser uma matriz, mas o que você não pode resolver com apenas chamar a dispersão várias vezes?
Seberg
1
Se eu chamar dispersão várias vezes, obterá as mesmas cores. Vou atualizar minha pergunta.
Yotam

Respostas:

269

Não sei o que você quer dizer com 'manualmente'. Você pode escolher um mapa de cores e criar uma matriz de cores com bastante facilidade:

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 Matplotlib com cores diferentes

Ou você pode criar seu próprio ciclador de cores usando itertools.cyclee especificando as cores que você deseja aplicar o loop, usando nextpara obter o que deseja. Por exemplo, com 3 cores:

import itertools

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

Gráfico Matplotlib com apenas 3 cores

Pense bem, talvez seja mais limpo não usar zipcom o primeiro:

colors = iter(cm.rainbow(np.linspace(0, 1, len(ys))))
for y in ys:
    plt.scatter(x, y, color=next(colors))
DSM
fonte
1
+1. Um ciclo de ferramentas provavelmente não é uma boa ideia nessa situação, pois acabaria com vários conjuntos de dados da mesma cor.
David Robinson
1
@DavidRobinson: não se especificar todos os dez, embora CONCORDO ciclismo tipo de derrotas o propósito lá ..: ^)
DSM
Precisely- então não é um ciclo :)
David Robinson
4
@macrocosme: funciona para mim. Adicionar plt.legend(['c{}'.format(i) for i in range(len(ys))], loc=2, bbox_to_anchor=(1.05, 1), borderaxespad=0., fontsize=11)ao fundo o texto acima me dá uma legenda com cores.
DSM
a solução itertools é ótima quando você deseja evitar algumas cores. No meu caso, como o fundo é preto, quero evitar o preto.
Fabrizio
50

A maneira normal de plotar plotagens com pontos em cores diferentes no matplotlib é passar uma lista de cores como parâmetro.

Por exemplo:

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

3 cores

Quando você tem uma lista de listas e deseja que elas sejam coloridas por lista. Eu acho que a maneira mais elegante é sugerida pelo @DSM, basta fazer um loop fazendo várias chamadas para dispersar.

Mas se, por algum motivo, você quiser fazer isso com apenas uma chamada, poderá fazer uma grande lista de cores, com uma lista de compreensão e um pouco da divisão de pisos:

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)

Todos plotados

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
fonte
19

Uma solução fácil

Se você tiver apenas um tipo de coleção (por exemplo, dispersão sem barras de erro), também poderá alterar as cores depois de plotá-las, às vezes isso é mais fácil de executar.

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)

O único pedaço de código que você precisa:

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

A saída fornece cores diferentes, mesmo quando você tem muitos gráficos de dispersão diferentes na mesma sub-plot.

insira a descrição da imagem aqui

GM
fonte
isso é ótimo, mas como, por exemplo, você adicionaria barras de erro da mesma cor com essa função? @GM
PEBKAC
1
Olá @PEBKAC, obrigado por apontar, tentei muito esta tarde para fazê-lo funcionar também nesse caso, mas como não encontrei nenhuma solução, editei a pergunta e avisei os outros usuários. Obrigado!
GM
Oi @GM, desculpe eu postei alguns comentários antes de ter finalizado a solução, que é descrito aqui: stackoverflow.com/q/51444364/7541421
PEBKAC
1
Eu usei outro método para atribuir as cores para cada série em um gráfico de dispersão. Agora funciona, infelizmente não pude prosseguir com sua solução elegante quando se tratava de barras de erro, ainda assim estou muito agradecido por sua postagem super útil! Felicidades!
PEBKAC
7

Você sempre pode usar a plot()função da seguinte maneira:

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

plotar como dispersão, mas muda de cor

Ophir Carmi
fonte
6

Essa pergunta é um pouco complicada antes de janeiro de 2013 e do matplotlib 1.3.1 (agosto de 2013), que é a versão estável mais antiga que você pode encontrar no site matpplotlib. Mas depois disso é bastante trivial.

Como a versão atual do matplotlib.pylab.scattersuporte é atribuída: matriz da string do nome da cor, matriz do número flutuante com mapa de cores, matriz de RGB ou RGBA.

esta resposta é dedicada à paixão sem fim da @ Oxinabox por corrigir a versão 2013 de mim em 2015.


você tem duas opções de usar o comando de dispersão com várias cores em uma única chamada.

  1. como pylab.scattersuporte a comandos, use a matriz RGBA para fazer a cor que desejar;

  2. No início de 2013, não há como fazê-lo, pois o comando suporta apenas uma cor para toda a coleção de pontos de dispersão. Quando eu estava fazendo meu projeto de 10000 linhas, descobri uma solução geral para contorná-lo. por isso é muito brega, mas posso fazer de qualquer forma, cor, tamanho e transparente. esse truque também pode ser aplicado para desenhar coleção de caminhos, coleção de linhas ....

o código também é inspirado no código fonte de pyplot.scatter, eu apenas dupliquei o que a dispersão faz sem acioná-la para desenhar.

o comando pyplot.scatterretorna um PatchCollectionObject, no arquivo "matplotlib / collections.py", uma variável privada _facecolorsna Collectionclasse e um método set_facecolors.

portanto, sempre que você tiver pontos de dispersão para desenhar, faça o seguinte:

# 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
fonte
por isso é meio complicado de ler e em 2013 eu usei python por 1 ano. então por que as pessoas querem saber como fazer isso? depois de funcionar, nunca mais me incomodo em olhar para ele novamente. meu projeto era desenhar muita visualização, com o código acima, o fluxo de trabalho foi otimizado.
Hualin 18/09/2015
1

Isso funciona para mim:

para cada série, use um gerador de cores rgb aleatório

c = color[np.random.random_sample(), np.random.random_sample(), np.random.random_sample()]
bracoo
fonte
Eu não sei o que é a variável cor, mas usando a sua abordagem é possível fazer algo como: plt.scatter(your values to the graph, color= (np.random.random_sample(), np.random.random_sample(), np.random.random_sample()) ). Você mencionou um gerador RGB e declarou uma lista RGB, os geradores são declarados entre '()'
Joel Carneiro
0

Uma solução MUITO mais rápida para grandes conjuntos de dados e número limitado de cores é o uso do Pandas e a função 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
fonte