Matplotlib discrete kleurenbalk

Ik probeer een discrete kleurenbalk te maken voor een scatterplot in matplotlib

Ik heb mijn x, y-gegevens en voor elk punt een integer-tagwaarde die ik wil weergeven met een unieke kleur, bijvoorbeeld

plt.scatter(x, y, c=tag)

Tag is meestal een geheel getal van 0-20, maar het exacte bereik kan veranderen

tot nu toe heb ik alleen de standaardinstellingen gebruikt, bijvoorbeeld

plt.colorbar()

die een continu kleurenpalet geeft. Idealiter zou ik een set van n discrete kleuren willen (n=20 in dit voorbeeld). Nog beter zou zijn om een ​​tagwaarde van 0 te krijgen om een ​​grijze kleur te produceren en 1-20 om kleurrijk te zijn.

Ik heb enkele ‘kookboek’-scripts gevonden, maar ze zijn erg ingewikkeld en ik denk niet dat ze de juiste manier zijn om een ​​schijnbaar eenvoudig probleem op te lossen


Antwoord 1, autoriteit 100%

Je kunt vrij eenvoudig een aangepaste discrete kleurenbalk maken door een BoundaryNorm te gebruiken als normalizer voor je scatter. Het eigenaardige beetje (in mijn methode) is dat 0 wordt weergegeven als grijs.

Voor afbeeldingen gebruik ik vaak de cmap.set_bad() en converteer ik mijn gegevens naar een numpy masked array. Dat zou veel gemakkelijker zijn om 0 grijs te maken, maar ik kon dit niet laten werken met de scatter of de aangepaste cmap.

Als alternatief kunt u uw eigen cmmap helemaal opnieuw maken, of een bestaande uitlezen en slechts enkele specifieke vermeldingen overschrijven.

import numpy as np
import matplotlib as mpl
import matplotlib.pylab as plt
fig, ax = plt.subplots(1, 1, figsize=(6, 6))  # setup the plot
x = np.random.rand(20)  # define the data
y = np.random.rand(20)  # define the data
tag = np.random.randint(0, 20, 20)
tag[10:12] = 0  # make sure there are some 0 values to show up as grey
cmap = plt.cm.jet  # define the colormap
# extract all colors from the .jet map
cmaplist = [cmap(i) for i in range(cmap.N)]
# force the first color entry to be grey
cmaplist[0] = (.5, .5, .5, 1.0)
# create the new map
cmap = mpl.colors.LinearSegmentedColormap.from_list(
    'Custom cmap', cmaplist, cmap.N)
# define the bins and normalize
bounds = np.linspace(0, 20, 21)
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
# make the scatter
scat = ax.scatter(x, y, c=tag, s=np.random.randint(100, 500, 20),
                  cmap=cmap, norm=norm)
# create a second axes for the colorbar
ax2 = fig.add_axes([0.95, 0.1, 0.03, 0.8])
cb = plt.colorbar.ColorbarBase(ax2, cmap=cmap, norm=norm,
    spacing='proportional', ticks=bounds, boundaries=bounds, format='%1i')
ax.set_title('Well defined discrete colors')
ax2.set_ylabel('Very custom cbar [-]', size=12)

Persoonlijk denk ik dat het met 20 verschillende kleuren een beetje moeilijk is om de specifieke waarde te lezen, maar dat is natuurlijk aan jou.


Antwoord 2, autoriteit 66%

Je zou dit voorbeeldkunnen volgen:

#!/usr/bin/env python
"""
Use a pcolor or imshow with a custom colormap to make a contour plot.
Since this example was initially written, a proper contour routine was
added to matplotlib - see contour_demo.py and
http://matplotlib.sf.net/matplotlib.pylab.html#-contour.
"""
from pylab import *
delta = 0.01
x = arange(-3.0, 3.0, delta)
y = arange(-3.0, 3.0, delta)
X,Y = meshgrid(x, y)
Z1 = bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = Z2 - Z1 # difference of Gaussians
cmap = cm.get_cmap('PiYG', 11)    # 11 discrete colors
im = imshow(Z, cmap=cmap, interpolation='bilinear',
            vmax=abs(Z).max(), vmin=-abs(Z).max())
axis('off')
colorbar()
show()

wat de volgende afbeelding oplevert:


Antwoord 3, autoriteit 50%

De bovenstaande antwoorden zijn goed, behalve dat ze niet de juiste plaatsing van de vinkjes op de kleurenbalk hebben. Ik vind het prettig om de vinkjes in het midden van de kleur te hebben, zodat het cijfer -> kleurtoewijzing is duidelijker. U kunt dit probleem oplossen door de limieten van de matshow-oproep te wijzigen:

import matplotlib.pyplot as plt
import numpy as np
def discrete_matshow(data):
    #get discrete colormap
    cmap = plt.get_cmap('RdBu', np.max(data)-np.min(data)+1)
    # set limits .5 outside true range
    mat = plt.matshow(data,cmap=cmap,vmin = np.min(data)-.5, vmax = np.max(data)+.5)
    #tell the colorbar to tick at integers
    cax = plt.colorbar(mat, ticks=np.arange(np.min(data),np.max(data)+1))
#generate data
a=np.random.randint(1, 9, size=(10, 10))
discrete_matshow(a)


Antwoord 4, autoriteit 36%

Als u een waarde boven of onder het bereik van de kleurenkaart wilt instellen, moet u de methoden set_overen set_undervan de kleurenkaart gebruiken. Als u een bepaalde waarde wilt markeren, maskeer deze dan (d.w.z. maak een gemaskeerde array) en gebruik de set_badmethode. (Bekijk de documentatie voor de basisklasse colormap: http://matplotlib. org/api/colors_api.html#matplotlib.colors.Colormap)

Het klinkt alsof je zoiets als dit wilt:

import matplotlib.pyplot as plt
import numpy as np
# Generate some data
x, y, z = np.random.random((3, 30))
z = z * 20 + 0.1
# Set some values in z to 0...
z[:5] = 0
cmap = plt.get_cmap('jet', 20)
cmap.set_under('gray')
fig, ax = plt.subplots()
cax = ax.scatter(x, y, c=z, s=100, cmap=cmap, vmin=0.1, vmax=z.max())
fig.colorbar(cax, extend='min')
plt.show()


Antwoord 5, autoriteit 19%

Dit onderwerp is al goed behandeld, maar ik wilde iets specifiekers toevoegen: ik wilde er zeker van zijn dat een bepaalde waarde zou worden toegewezen aan die kleur (niet aan een kleur).

Het is niet ingewikkeld, maar omdat het me wat tijd kostte, zou het anderen kunnen helpen om niet zoveel tijd te verliezen als ik 🙂

import matplotlib
from matplotlib.colors import ListedColormap
# Let's design a dummy land use field
A = np.reshape([7,2,13,7,2,2], (2,3))
vals = np.unique(A)
# Let's also design our color mapping: 1s should be plotted in blue, 2s in red, etc...
col_dict={1:"blue",
          2:"red",
          13:"orange",
          7:"green"}
# We create a colormar from our list of colors
cm = ListedColormap([col_dict[x] for x in col_dict.keys()])
# Let's also define the description of each category : 1 (blue) is Sea; 2 (red) is burnt, etc... Order should be respected here ! Or using another dict maybe could help.
labels = np.array(["Sea","City","Sand","Forest"])
len_lab = len(labels)
# prepare normalizer
## Prepare bins for the normalizer
norm_bins = np.sort([*col_dict.keys()]) + 0.5
norm_bins = np.insert(norm_bins, 0, np.min(norm_bins) - 1.0)
print(norm_bins)
## Make normalizer and formatter
norm = matplotlib.colors.BoundaryNorm(norm_bins, len_lab, clip=True)
fmt = matplotlib.ticker.FuncFormatter(lambda x, pos: labels[norm(x)])
# Plot our figure
fig,ax = plt.subplots()
im = ax.imshow(A, cmap=cm, norm=norm)
diff = norm_bins[1:] - norm_bins[:-1]
tickz = norm_bins[:-1] + diff / 2
cb = fig.colorbar(im, format=fmt, ticks=tickz)
fig.savefig("example_landuse.png")
plt.show()


Antwoord 6, autoriteit 7%

Ik heb deze ideeën onderzocht en hier is mijn vijf cent waard. Het vermijdt het aanroepen van BoundaryNormen het specificeren van normals argument voor scatteren colorbar. Ik heb echter geen manier gevonden om de nogal langdradige oproep naar matplotlib.colors.LinearSegmentedColormap.from_listte elimineren.

Enige achtergrond is dat matplotlib zogenaamde kwalitatieve kleurenkaarten biedt, bedoeld om te gebruiken met discrete gegevens. Set1heeft bijvoorbeeld 9 gemakkelijk te onderscheiden kleuren en tab20kan worden gebruikt voor 20 kleuren. Met deze kaarten kan het natuurlijk zijn om hun eerste n kleuren te gebruiken om scatterplots met n categorieën te kleuren, zoals in het volgende voorbeeld. Het voorbeeld levert ook een kleurenbalk op met n discrete kleuren die op de juiste manier zijn gelabeld.

import matplotlib, numpy as np, matplotlib.pyplot as plt
n = 5
from_list = matplotlib.colors.LinearSegmentedColormap.from_list
cm = from_list(None, plt.cm.Set1(range(0,n)), n)
x = np.arange(99)
y = x % 11
z = x % n
plt.scatter(x, y, c=z, cmap=cm)
plt.clim(-0.5, n-0.5)
cb = plt.colorbar(ticks=range(0,n), label='Group')
cb.ax.tick_params(length=0)

wat de onderstaande afbeelding oplevert. De nin de aanroep van Set1specificeert
de eerste nkleuren van die kleurenkaart, en de laatste nin de aanroep naar from_list
specificeert om een ​​kaart te construeren met nkleuren (de standaard is 256). Om cmin te stellen als de standaard kleurenkaart met plt.set_cmap, vond ik het nodig om het een naam te geven en het te registreren, namelijk:

cm = from_list('Set15', plt.cm.Set1(range(0,n)), n)
plt.cm.register_cmap(None, cm)
plt.set_cmap(cm)
...
plt.scatter(x, y, c=z)


Antwoord 7

Ik denk dat je zou willen kijken naar colors.ListedColormapom uw kleurenkaart te genereren, of als u alleen een statische kleurenkaart nodig heeft, heb ik gewerkt aan een appdie hulp.

Other episodes