Maak een eigen kleurenkaart met matplotlib en plot kleurenschaal

Ik heb het volgende probleem, ik wil mijn eigen kleurenkaart maken (rood-mix-violet-mix-blauw) die overeenkomt met waarden tussen -2 en +2 en ik wil deze gebruiken om punten in mijn plot te kleuren.
De plot zou dan de kleurenschaal aan de rechterkant moeten hebben.

Zo heb ik de kaart tot nu toe gemaakt. Maar ik weet niet zeker of het de kleuren mengt.

cmap = matplotlib.colors.ListedColormap(["red","violet","blue"], name='from_list', N=None)
m = cm.ScalarMappable(norm=norm, cmap=cmap)

Op die manier koppel ik de kleuren aan de waarden.

colors = itertools.cycle([m.to_rgba(1.22), ..])

Dan plot ik het:

for i in range(0, len(array_dg)):
  plt.plot(array_dg[i], markers.next(),alpha=alpha[i], c=colors.next())

Mijn problemen zijn:
1. Ik kan de kleurenschaal niet plotten.
2. Ik ben er niet helemaal zeker van of mijn schaal een doorlopende (gladde) kleurenschaal creëert.


Antwoord 1, autoriteit 100%

Er is een illustratief voorbeeld van hoe u hier aangepaste kleurenkaarten maakt.
De docstring is essentieel voor het begrijpen van de betekenis van:
cdict. Als je dat eenmaal onder de knie hebt, kun je een cdictals volgt gebruiken:

cdict = {'red':   ((0.0, 1.0, 1.0), 
                   (0.1, 1.0, 1.0),  # red 
                   (0.4, 1.0, 1.0),  # violet
                   (1.0, 0.0, 0.0)), # blue
         'green': ((0.0, 0.0, 0.0),
                   (1.0, 0.0, 0.0)),
         'blue':  ((0.0, 0.0, 0.0),
                   (0.1, 0.0, 0.0),  # red
                   (0.4, 1.0, 1.0),  # violet
                   (1.0, 1.0, 0.0))  # blue
          }

Hoewel het formaat cdictje veel flexibiliteit geeft, vind ik het simpel
gradiënten het formaat is nogal onintuïtief. Hier is een hulpprogramma om te helpen:
eenvoudige LinearSegmentedColormaps genereren:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
def make_colormap(seq):
    """Return a LinearSegmentedColormap
    seq: a sequence of floats and RGB-tuples. The floats should be increasing
    and in the interval (0,1).
    """
    seq = [(None,) * 3, 0.0] + list(seq) + [1.0, (None,) * 3]
    cdict = {'red': [], 'green': [], 'blue': []}
    for i, item in enumerate(seq):
        if isinstance(item, float):
            r1, g1, b1 = seq[i - 1]
            r2, g2, b2 = seq[i + 1]
            cdict['red'].append([item, r1, r2])
            cdict['green'].append([item, g1, g2])
            cdict['blue'].append([item, b1, b2])
    return mcolors.LinearSegmentedColormap('CustomMap', cdict)
c = mcolors.ColorConverter().to_rgb
rvb = make_colormap(
    [c('red'), c('violet'), 0.33, c('violet'), c('blue'), 0.66, c('blue')])
N = 1000
array_dg = np.random.uniform(0, 10, size=(N, 2))
colors = np.random.uniform(-2, 2, size=(N,))
plt.scatter(array_dg[:, 0], array_dg[:, 1], c=colors, cmap=rvb)
plt.colorbar()
plt.show()


Trouwens, de for-loop

for i in range(0, len(array_dg)):
  plt.plot(array_dg[i], markers.next(),alpha=alpha[i], c=colors.next())

plot één punt voor elke aanroep naar plt.plot. Dit werkt voor een klein aantal punten, maar zal voor veel punten extreem traag worden. plt.plotkan maar in één kleur tekenen, maar plt.scatterkan aan elke stip een andere kleur toewijzen. Dus plt.scatteris de juiste keuze.


Antwoord 2, autoriteit 97%

Omdat de methoden die in andere antwoorden worden gebruikt nogal ingewikkeld lijken voor zo’n gemakkelijke taak, is hier een nieuw antwoord:

In plaats van een ListedColormap, die een discrete kleurenkaart produceert, kunt u een LinearSegmentedColormapgebruiken. Dit kan eenvoudig worden gemaakt vanuit een lijst met behulp van de from_listmethode.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors
x,y,c = zip(*np.random.rand(30,3)*4-2)
norm=plt.Normalize(-2,2)
cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", ["red","violet","blue"])
plt.scatter(x,y,c=c, cmap=cmap, norm=norm)
plt.colorbar()
plt.show()


Meer in het algemeen, als u een lijst met waarden heeft (bijv. [-2., -1, 2]) en bijbehorende kleuren (bijv. ["red","violet","blue"]), zodat de nde waarde moet overeenkomen met de nde kleur, kunt u de waarden normaliseren en als tupels aan de from_listmethode.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors
x,y,c = zip(*np.random.rand(30,3)*4-2)
cvals  = [-2., -1, 2]
colors = ["red","violet","blue"]
norm=plt.Normalize(min(cvals),max(cvals))
tuples = list(zip(map(norm,cvals), colors))
cmap = matplotlib.colors.LinearSegmentedColormap.from_list("", tuples)
plt.scatter(x,y,c=c, cmap=cmap, norm=norm)
plt.colorbar()
plt.show()


Antwoord 3, autoriteit 15%

Als u het maken van een aangepaste afwijkende kleurenkaart wilt automatiseren die vaak wordt gebruikt voor surface plots, deze module in combinatie met de @unutbu-methode werkte goed voor mij.

def diverge_map(high=(0.565, 0.392, 0.173), low=(0.094, 0.310, 0.635)):
    '''
    low and high are colors that will be used for the two
    ends of the spectrum. they can be either color strings
    or rgb color tuples
    '''
    c = mcolors.ColorConverter().to_rgb
    if isinstance(low, basestring): low = c(low)
    if isinstance(high, basestring): high = c(high)
    return make_colormap([low, c('white'), 0.5, c('white'), high])

De hoge en lage waarden kunnen tekenreekskleurnamen of RGB-tupels zijn. Dit is het resultaat met behulp van de surface plot-demo:


Antwoord 4, autoriteit 2%

Dit lijkt voor mij te werken.

def make_Ramp( ramp_colors ): 
    from colour import Color
    from matplotlib.colors import LinearSegmentedColormap
    color_ramp = LinearSegmentedColormap.from_list( 'my_list', [ Color( c1 ).rgb for c1 in ramp_colors ] )
    plt.figure( figsize = (15,3))
    plt.imshow( [list(np.arange(0, len( ramp_colors ) , 0.1)) ] , interpolation='nearest', origin='lower', cmap= color_ramp )
    plt.xticks([])
    plt.yticks([])
    return color_ramp
custom_ramp = make_Ramp( ['#754a28','#893584','#68ad45','#0080a5' ] ) 

Other episodes