Jak generować losowe kolory w matplotlib?

87

Jaki jest trywialny przykład generowania losowych kolorów do przekazania do funkcji kreślących?

Nazywam rozproszenie wewnątrz pętli i chcę, aby każdy wykres miał inny kolor.

for X,Y in data:
   scatter(X, Y, c=??)

c: kolor. c może być pojedynczym ciągiem formatu koloru, sekwencją specyfikacji kolorów o długości N lub sekwencją N liczb, które mają być odwzorowane na kolory przy użyciu cmap i normy określonych za pomocą kwargs (patrz poniżej). Zwróć uwagę, że c nie powinno być pojedynczą numeryczną sekwencją RGB lub RGBA, ponieważ jest ona nie do odróżnienia od tablicy wartości, które mają zostać zmapowane. c może być jednak tablicą 2-D, w której wiersze to RGB lub RGBA.

John Mee
źródło
1
Losowo wybrany z czego? Jeśli wybierzesz losowo spośród wszystkich dostępnych kolorów, możesz otrzymać dziwną mieszankę bardzo różnych kolorów, a niektóre są tak podobne, że trudno je odróżnić.
BrenBarn

Odpowiedzi:

141

Nazywam rozproszenie wewnątrz pętli i chcę, aby każdy wykres miał inny kolor.

Na tej podstawie i na twojej odpowiedzi: Wydaje mi się, że faktycznie chcesz mieć n różne kolory dla swoich zbiorów danych; chcesz odwzorować indeksy liczb całkowitych 0, 1, ..., n-1na różne kolory RGB. Coś jak:

mapowanie indeksu na kolor

Oto funkcja, aby to zrobić:

import matplotlib.pyplot as plt

def get_cmap(n, name='hsv'):
    '''Returns a function that maps each index in 0, 1, ..., n-1 to a distinct 
    RGB color; the keyword argument name must be a standard mpl colormap name.'''
    return plt.cm.get_cmap(name, n)

Wykorzystanie we fragmencie pseudokodu w pytaniu:

cmap = get_cmap(len(data))
for i, (X, Y) in enumerate(data):
   scatter(X, Y, c=cmap(i))

Wygenerowałem liczbę w mojej odpowiedzi za pomocą następującego kodu:

import matplotlib.pyplot as plt

def get_cmap(n, name='hsv'):
    '''Returns a function that maps each index in 0, 1, ..., n-1 to a distinct 
    RGB color; the keyword argument name must be a standard mpl colormap name.'''
    return plt.cm.get_cmap(name, n)

def main():
    N = 30
    fig=plt.figure()
    ax=fig.add_subplot(111)   
    plt.axis('scaled')
    ax.set_xlim([ 0, N])
    ax.set_ylim([-0.5, 0.5])
    cmap = get_cmap(N)
    for i in range(N):
        rect = plt.Rectangle((i, -0.5), 1, 1, facecolor=cmap(i))
        ax.add_artist(rect)
    ax.set_yticks([])
    plt.show()

if __name__=='__main__':
    main()

Przetestowano zarówno z Pythonem 2.7 i matplotlib 1.5, jak iz Pythonem 3.5 i matplotlib 2.0. Działa zgodnie z oczekiwaniami.

Ali
źródło
1
@ user1941407 Dzięki! :) Chciałbym wiedzieć, dlaczego ktoś anonimowo zlekceważył odpowiedź.
Ali
7
może to skomplikowane
ingrid
1
nie wydaje się działać? nie wydaje się w ogóle podłączać do konsoli Pythona.
mjwrazor
@mjwrazor Przepraszamy, nie śledzę. Czy możesz wyjaśnić, co „nie działa”?
Ali
Próbowałem umieścić metodę w konsoli Pythona, konsola nigdy jej nie wczytuje. Również logika na końcu metody nie ma sensu. Po co zwracać metodę, która wywołuje inną metodę, która zwraca metodę wykonania. Dlaczego po prostu nie zwrócić wykonanej metody?
mjwrazor
76
for X,Y in data:
   scatter(X, Y, c=numpy.random.rand(3,))
Charles Brunet
źródło
1
A jeśli są trzy wartości do wykreślenia?
panda-34
1
Czy 3 oznacza 3 wartości składowych R, G i B?
Kshitij Bajracharya
bez numpy, możesz użyćcolor=(random.uniform(0, 1), random.uniform(0, 1), random.uniform(0, 1))
azzamsa
2
Bardziej wydajne i mniej wpisujące:scatter(X,Y, c=numpy.random.rand(len(X),3)
Qualia
31

opracowując odpowiedź @ john-mee, jeśli masz dowolnie długie dane, ale nie potrzebujesz ściśle unikalnych kolorów:

dla pythona 2:

from itertools import cycle
cycol = cycle('bgrcmk')

for X,Y in data:
    scatter(X, Y, c=cycol.next())

dla pythona 3:

from itertools import cycle
cycol = cycle('bgrcmk')

for X,Y in data:
    scatter(X, Y, c=next(cycol))

Ma to tę zaletę, że kolory są łatwe do kontrolowania i krótkie.

user3240588
źródło
27

Przez pewien czas byłem naprawdę zirytowany faktem, że matplotlib nie generuje map kolorów z przypadkowymi kolorami, ponieważ jest to powszechna potrzeba segmentacji i grupowania zadań.

Po prostu generując przypadkowe kolory, możemy zakończyć z niektórymi, które są zbyt jasne lub zbyt ciemne, co utrudnia wizualizację. Ponadto zwykle potrzebujemy, aby pierwszy lub ostatni kolor był czarny, reprezentujący tło lub wartości odstające. Napisałem więc małą funkcję do mojej codziennej pracy

Oto jego zachowanie:

new_cmap = rand_cmap(100, type='bright', first_color_black=True, last_color_black=False, verbose=True)

Wygenerowana mapa kolorów

Niż po prostu użyj new_cmap jako mapy kolorów na matplotlib:

ax.scatter(X,Y, c=label, cmap=new_cmap, vmin=0, vmax=num_labels)

Kod jest tutaj:

def rand_cmap(nlabels, type='bright', first_color_black=True, last_color_black=False, verbose=True):
    """
    Creates a random colormap to be used together with matplotlib. Useful for segmentation tasks
    :param nlabels: Number of labels (size of colormap)
    :param type: 'bright' for strong colors, 'soft' for pastel colors
    :param first_color_black: Option to use first color as black, True or False
    :param last_color_black: Option to use last color as black, True or False
    :param verbose: Prints the number of labels and shows the colormap. True or False
    :return: colormap for matplotlib
    """
    from matplotlib.colors import LinearSegmentedColormap
    import colorsys
    import numpy as np


    if type not in ('bright', 'soft'):
        print ('Please choose "bright" or "soft" for type')
        return

    if verbose:
        print('Number of labels: ' + str(nlabels))

    # Generate color map for bright colors, based on hsv
    if type == 'bright':
        randHSVcolors = [(np.random.uniform(low=0.0, high=1),
                          np.random.uniform(low=0.2, high=1),
                          np.random.uniform(low=0.9, high=1)) for i in xrange(nlabels)]

        # Convert HSV list to RGB
        randRGBcolors = []
        for HSVcolor in randHSVcolors:
            randRGBcolors.append(colorsys.hsv_to_rgb(HSVcolor[0], HSVcolor[1], HSVcolor[2]))

        if first_color_black:
            randRGBcolors[0] = [0, 0, 0]

        if last_color_black:
            randRGBcolors[-1] = [0, 0, 0]

        random_colormap = LinearSegmentedColormap.from_list('new_map', randRGBcolors, N=nlabels)

    # Generate soft pastel colors, by limiting the RGB spectrum
    if type == 'soft':
        low = 0.6
        high = 0.95
        randRGBcolors = [(np.random.uniform(low=low, high=high),
                          np.random.uniform(low=low, high=high),
                          np.random.uniform(low=low, high=high)) for i in xrange(nlabels)]

        if first_color_black:
            randRGBcolors[0] = [0, 0, 0]

        if last_color_black:
            randRGBcolors[-1] = [0, 0, 0]
        random_colormap = LinearSegmentedColormap.from_list('new_map', randRGBcolors, N=nlabels)

    # Display colorbar
    if verbose:
        from matplotlib import colors, colorbar
        from matplotlib import pyplot as plt
        fig, ax = plt.subplots(1, 1, figsize=(15, 0.5))

        bounds = np.linspace(0, nlabels, nlabels + 1)
        norm = colors.BoundaryNorm(bounds, nlabels)

        cb = colorbar.ColorbarBase(ax, cmap=random_colormap, norm=norm, spacing='proportional', ticks=None,
                                   boundaries=bounds, format='%1i', orientation=u'horizontal')

    return random_colormap

Jest również na github: https://github.com/delestro/rand_cmap

Delestro
źródło
2
Dzięki. To było bardzo przydatne.
Ash
15

Gdy mniej niż 9 zbiorów danych:

colors = "bgrcmykw"
color_index = 0

for X,Y in data:
    scatter(X,Y, c=colors[color_index])
    color_index += 1
John Mee
źródło
12

Skoro pytanie brzmi How to generate random colors in matplotlib?i jak szukałem odpowiedzi w sprawie pie plots, myślę, że warto tu umieścić odpowiedź (dla pies)

import numpy as np
from random import sample
import matplotlib.pyplot as plt
import matplotlib.colors as pltc
all_colors = [k for k,v in pltc.cnames.items()]

fracs = np.array([600, 179, 154, 139, 126, 1185])
labels = ["label1", "label2", "label3", "label4", "label5", "label6"]
explode = ((fracs == max(fracs)).astype(int) / 20).tolist()

for val in range(2):
    colors = sample(all_colors, len(fracs))
    plt.figure(figsize=(8,8))
    plt.pie(fracs, labels=labels, autopct='%1.1f%%', 
            shadow=True, explode=explode, colors=colors)
    plt.legend(labels, loc=(1.05, 0.7), shadow=True)
    plt.show()

Wynik

wprowadź opis obrazu tutaj

wprowadź opis obrazu tutaj

J. Doe
źródło
1
Hej, właśnie tego szukam. Jednak na twoim drugim zdjęciu (mi się to również zdarza) masz prawie takie same kolory (beżowy / biały). Czy można zastosować to podejście, ale próbkować w sposób, który pozwala na uzyskanie bardziej wyrazistych kolorów?
armara
9

Oto bardziej zwięzła wersja odpowiedzi Ali, podająca jeden odrębny kolor na działkę:

import matplotlib.pyplot as plt

N = len(data)
cmap = plt.cm.get_cmap("hsv", N+1)
for i in range(N):
    X,Y = data[i]
    plt.scatter(X, Y, c=cmap(i))
Champitoad
źródło
4

Na podstawie odpowiedzi Ali i Champitoad:

Jeśli chcesz wypróbować różne palety dla tego samego, możesz to zrobić w kilku wierszach:

cmap=plt.cm.get_cmap(plt.cm.viridis,143)

^ 143 to liczba kolorów, które próbujesz

Wybrałem 143, ponieważ w grę wchodzi cała gama kolorów na mapie kolorów. To, co możesz zrobić, to próbkować n-ty kolor w każdej iteracji, aby uzyskać efekt mapy kolorów.

n=20 for i,(x,y) in enumerate(points): plt.scatter(x,y,c=cmap(n*i))

ai-shwarya
źródło
1
enter code here

import numpy as np

clrs = np.linspace( 0, 1, 18 )  # It will generate 
# color only for 18 for more change the number
np.random.shuffle(clrs)
colors = []
for i in range(0, 72, 4):
    idx = np.arange( 0, 18, 1 )
    np.random.shuffle(idx)
    r = clrs[idx[0]]
    g = clrs[idx[1]]
    b = clrs[idx[2]]
    a = clrs[idx[3]]
    colors.append([r, g, b, a])
Santosh Magadum
źródło
Przypisz tę listę kolorów do koloru w czasie rysowania wykresu
Santosh Magadum,
1

Jeśli chcesz mieć pewność, że kolory są wyraźne - ale nie wiesz, ile kolorów potrzeba. Spróbuj czegoś takiego. Wybiera kolory z przeciwnych stron spektrum i systematycznie zwiększa ziarnistość.

import math

def calc(val, max = 16):
    if val < 1:
        return 0
    if val == 1:
        return max

    l = math.floor(math.log2(val-1))    #level 
    d = max/2**(l+1)                    #devision
    n = val-2**l                        #node
    return d*(2*n-1)
import matplotlib.pyplot as plt

N = 16
cmap = cmap = plt.cm.get_cmap('gist_rainbow', N)

fig, axs = plt.subplots(2)
for ax in axs:
    ax.set_xlim([ 0, N])
    ax.set_ylim([-0.5, 0.5])
    ax.set_yticks([])

for i in range(0,N+1):
    v = int(calc(i, max = N))
    rect0 = plt.Rectangle((i, -0.5), 1, 1, facecolor=cmap(i))
    rect1 = plt.Rectangle((i, -0.5), 1, 1, facecolor=cmap(v))
    axs[0].add_artist(rect0)
    axs[1].add_artist(rect1)

plt.xticks(range(0, N), [int(calc(i, N)) for i in range(0, N)])
plt.show()

wynik

Podziękowania dla @Ali za zapewnienie podstawowej implementacji.

Johan
źródło