Установка разного цвета для каждой серии в точечной диаграмме на matplotlib

162

Предположим, у меня есть три набора данных:

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

Я могу разбросать сюжет так:

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

Как я могу сделать это с 10 сетами?

Я искал это и мог найти любую ссылку на то, что я спрашиваю.

Изменить: прояснить (надеюсь) мой вопрос

Если я вызываю Scatter несколько раз, я могу установить только один и тот же цвет для каждого Scatter. Кроме того, я знаю, что могу установить цветовой массив вручную, но я уверен, что есть лучший способ сделать это. Тогда мой вопрос: «Как я могу автоматически распределить и построить несколько наборов данных, каждый из которых имеет свой цвет?

Если это поможет, я могу легко присвоить уникальный номер каждому набору данных.

Yotam
источник
1
Что за вопрос здесь? Цвет также может быть массивом, но что вы не можете решить, просто вызвав scatter несколько раз?
Себерг
1
Если я называю Scatter несколько раз, я получаю одинаковые цвета. Я обновлю свой вопрос.
Йотам

Ответы:

269

Я не знаю, что вы подразумеваете под «вручную». Вы можете выбрать цветовую карту и создать массив цветов достаточно легко:

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)

Граф Matplotlib с разными цветами

Или вы можете сделать свой собственный цветовой цикл, используя itertools.cycleи задавая цвета, которые вы хотите зациклить, используя, nextчтобы получить тот, который вы хотите. Например, с 3 цветами:

import itertools

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

Граф Matplotlib только с 3 цветами

Если подумать, может быть, zipс первым не будет чище :

colors = iter(cm.rainbow(np.linspace(0, 1, len(ys))))
for y in ys:
    plt.scatter(x, y, color=next(colors))
DSM
источник
1
+1. Цикл itertools, вероятно, не является хорошей идеей в этой ситуации, так как он будет иметь несколько наборов данных, имеющих один и тот же цвет.
Дэвид Робинсон
1
@DavidRobinson: нет, если вы укажете все десять, хотя я согласен, что циклический вид поражает цель там ..: ^)
DSM
Точно - тогда это не цикл :)
Дэвид Робинсон
4
@Macrocosme: работает для меня. Добавление plt.legend(['c{}'.format(i) for i in range(len(ys))], loc=2, bbox_to_anchor=(1.05, 1), borderaxespad=0., fontsize=11)к основанию вышеизложенного дает мне легенду с цветами.
DSM
Решение itertools отлично подходит, если вы хотите избежать некоторых цветов. В моем случае, поскольку фон черный, я хочу избежать черного.
Фабрицио
50

Обычный способ построения графиков с точками разных цветов в matplotlib - передать список цветов в качестве параметра.

Например:

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

3 цвета

Когда у вас есть список списков, и вы хотите, чтобы они были цветными для каждого списка. Я думаю, что самый элегантный способ - это то, что предложено @DSM, просто сделать цикл, делающий множественные вызовы для разброса.

Но если по какой-то причине вы хотели сделать это всего одним вызовом, вы можете составить большой список цветов с пониманием списка и небольшим разделением по полу:

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)

Все построено

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

Легко исправить

Если у вас есть только один тип коллекций (например, разброс без полос ошибок), вы также можете изменить цвета после того, как вы нанесете их на график, это иногда проще выполнить.

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)

Единственный фрагмент кода, который вам нужен:

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

Вывод дает различные цвета, даже если у вас есть много разных графиков рассеяния в одном и том же участке.

введите описание изображения здесь

GM
источник
это здорово, но как бы вы, например, добавили панели ошибок с тем же цветом с этой функцией? @GM
PEBKAC
1
Привет @PEBKAC, спасибо за указание на это, сегодня я очень старался, чтобы это работало и в этом случае, но я не мог найти никакого решения, поэтому я отредактировал вопрос и предупредил других пользователей. Спасибо!
ГМ
Привет @GM, извините, я опубликовал несколько комментариев, прежде чем завершить решение, которое описано здесь: stackoverflow.com/q/51444364/7541421
PEBKAC
1
Я использовал другой метод, чтобы назначить цвета для каждой серии в точечной диаграмме. Теперь это работает, к сожалению, я не смог продолжить ваше элегантное решение, когда дело дошло до панелей ошибок, тем не менее, я очень благодарен за ваш супер полезный пост! Ура!
PEBKAC
7

Вы всегда можете использовать plot()функцию так:

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

заговор, как разброс, но меняет цвета

Офир Карми
источник
6

Этот вопрос немного сложен до января 2013 года и matplotlib 1.3.1 (август 2013 года), который является самой старой стабильной версией, которую вы можете найти на веб-сайте matpplotlib. Но после этого это довольно тривиально.

Потому что настоящая версия matplotlib.pylab.scatter поддерживает присваивание: массив строки имени цвета, массив числа с плавающей точкой с картой цветов, массив RGB или RGBA.

Этот ответ посвящен бесконечной страсти @ Oxinabox к исправлению своей версии 2013 года в 2015 году.


у вас есть два варианта использования команды разброса с несколькими цветами в одном вызове.

  1. в качестве pylab.scatterподдержки команд используйте массив RGBA, чтобы сделать любой цвет, который вы хотите;

  2. в начале 2013 года сделать это невозможно, поскольку команда поддерживает только один цвет для всей совокупности точек разброса. Когда я делал свой проект на 10000 строк, я придумал общее решение, чтобы обойти его. так что это очень липко, но я могу сделать это в любой форме, цвете, размере и прозрачности. этот трюк также может быть применен для рисования коллекции пути, линии строки ....

код также вдохновлен исходным кодом pyplot.scatter , я просто продублировал то, что делает Scatter, не вызывая его для рисования.

команда pyplot.scatterвозвращает PatchCollectionобъект, в файле "matplotlib / collection.py" частная переменная _facecolorsв Collectionклассе и метод set_facecolors.

поэтому, когда у вас есть точки разброса, вы можете сделать это:

# 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
источник
так что это довольно сложно читать, и в 2013 году я использовал Python в течение 1 года. так почему люди хотят знать, как это сделать? после того, как это сработало, я никогда не буду смотреть на это снова. Мой проект должен был нарисовать много визуализации, с кодом выше, рабочий процесс был упорядочен.
Хуалин
1

Это работает для меня:

для каждой серии используйте генератор случайных цветов RGB

c = color[np.random.random_sample(), np.random.random_sample(), np.random.random_sample()]
bracoo
источник
Я не знаю , что это ваша переменная цвет, но используя свой подход, можно сделать что - то вроде: plt.scatter(your values to the graph, color= (np.random.random_sample(), np.random.random_sample(), np.random.random_sample()) ). Вы упомянули генератор RGB и объявили список RGB, генераторы объявлены между '()'
Джоэл Карнейру
0

НАМНОГО более быстрое решение для большого набора данных и ограниченного количества цветов - это использование Pandas и функции 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
источник