Matplotlib ayrık renk çubuğu


100

Matplotlib'de bir dağılım grafiği için ayrı bir renk çubuğu yapmaya çalışıyorum

X, y verilerim var ve her nokta için benzersiz bir renkle temsil edilmesini istediğim bir tamsayı etiket değeri, örn.

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

tipik olarak etiket, 0-20 arasında değişen bir tam sayı olacaktır, ancak tam aralık değişebilir

şimdiye kadar sadece varsayılan ayarları kullandım, örneğin

plt.colorbar()

sürekli bir renk aralığı verir. İdeal olarak n farklı renkten oluşan bir set istiyorum (bu örnekte n = 20). Daha da iyisi, gri bir renk oluşturmak için 0 ve renkli olmak için 1-20'lik bir etiket değeri almaktır.

Bazı 'yemek kitabı' senaryoları buldum ama çok karmaşıklar ve görünüşte basit bir sorunu çözmenin doğru yolu olduklarını düşünemiyorum


1
yapar bu ya bu yardım?
Francesco Montesano

bağlantılar için teşekkürler, ancak 2. örnek, (görünüşte) önemsiz bir görevi gerçekleştirmek için aşırı derecede karmaşık araçlarla ilgili kastettiğim şeydir - 1. bağlantı yararlıdır
bph

2
Bu bağlantıyı, mevcut bir renk haritasını ayırmada
BallpointBen

Yanıtlar:


99

Dağılımınız için normalleştirici olarak bir BoundaryNorm kullanarak oldukça kolay bir şekilde özel bir ayrı renk çubuğu oluşturabilirsiniz. Tuhaf bit (benim yöntemimde) 0'ın gri olarak görünmesini sağlıyor.

Görüntüler için genellikle cmap.set_bad () kullanıyorum ve verilerimi uyuşmuş bir maskelenmiş diziye dönüştürüyorum. Bunu 0 gri yapmak çok daha kolay olurdu, ancak bunu scatter veya özel cmap ile çalıştırmayı başaramadım.

Alternatif olarak sıfırdan kendi cmap'inizi oluşturabilir veya mevcut olanı okuyabilir ve sadece bazı belirli girdileri geçersiz kılabilirsiniz.

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)

görüntü açıklamasını buraya girin

Şahsen, 20 farklı renkle belirli bir değeri okumanın biraz zor olduğunu düşünüyorum, ama bu elbette size kalmış.


Buna izin verilip verilmediğinden emin değilim, ama soruma buradan bakabilir misin ?
vwos

7
plt.colorbar.ColorbarBaseHata atar. Kullanımmpl.colorbar.ColorbarBase
zeeshan khan

Bu cevap için teşekkür ederim, gerçekten doktordan özledim. Yüzdelik dilimlerin ön yüzleri için onu değiştirmeye çalıştım ve renk eşlemeyle ilgili bir hatam vardı. Bu farklı bir kullanım durumu, ama buna olduğunu önerebilir N-1içinde cmap = mpl.colors.LinearSegmentedColormap.from_list('Custom cmap', cmaplist, cmap.N-1). Değilse renkler kutular içinde eşit olarak dağılmıyorsa ve bir çit bariyeri sorununuz var.
jlandercy

1
Eşit olarak dağıtılmış bir eşlemeyi yeniden oluşturmak için kod:q=np.arange(0.0, 1.01, 0.1) cmap = mpl.cm.get_cmap('jet') cmaplist = [cmap(x) for x in q] cmap = mpl.colors.LinearSegmentedColormap.from_list('Custom cmap', cmaplist, len(q)-1) norm = mpl.colors.BoundaryNorm(q, cmap.N)
jlandercy

Emin değilim N-1, belki haklısın ama bunu örneğimle çoğaltamam. A kullanarak LinearSegmentedColormap(ve Nargümanından) kaçınabilirsiniz ListedColormap. Dokümanlar '13'ten beri çok gelişti, örneğin bakınız: matplotlib.org/3.1.1/tutorials/colors/…
Rutger Kassies

66

Bu örneği takip edebilirsiniz :

#!/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()

aşağıdaki görüntüyü oluşturur:

poormans_contour


14
cmap = cm.get_cmap ('jet', 20) sonra dağılma (x, y, c = etiketler, cmap = cmap) beni oraya götürüyor - matplotlib için yararlı belgeler bulmak çok zor
bph

Bilginize, bağlantı bozuk görünüyor.
Quinn Culver

47

Yukarıdaki cevaplar, renk çubuğunda uygun kene yerleşimi olmaması dışında iyidir. Rengin ortasında işaretler olmasını seviyorum, böylece sayı -> renk eşlemesi daha net. Bu sorunu matshow çağrısının sınırlarını değiştirerek çözebilirsiniz:

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)

ayrı renk çubuğu örneği


2
Ayrık verilere bakarken onay işaretini karşılık gelen rengin ortasına yerleştirmenin çok yararlı olduğunu kabul ediyorum. İkinci yönteminiz doğrudur. Ancak, ilk yönteminiz genel olarak yanlıştır : keneleri, renk çubuğundaki yerleşimleriyle tutarsız olan değerlerle etiketliyorsunuz. set_ticklabels(...)yalnızca etiket biçimlendirmesini kontrol etmek için kullanılmalıdır (örn. ondalık sayı, vb.). Veriler gerçekten ayrıksa, herhangi bir sorun fark etmeyebilirsiniz. Sistemde parazit varsa (örn. 2 -> 1.9), bu tutarsız etiketleme yanıltıcı ve yanlış bir renk çubuğuna neden olur.
E. Davis

E., bence sınırları değiştirmenin daha üstün bir çözüm olduğu konusunda haklısın, bu yüzden diğerini kaldırdım - her ikisi de "gürültü" ile iyi başa çıkamazdı. Sürekli verilerin işlenmesi için bazı ayarlamalar gerekli olacaktır.
ben.dichter

39

Renk haritası aralığının üstünde veya altında bir değer ayarlamak için, set_overveset_under yöntemlerini . Belirli bir değeri işaretlemek istiyorsanız, onu maskeleyin (yani, maskelenmiş bir dizi oluşturun) veset_bad yöntemi . (Temel renk haritası sınıfının belgelerine bir göz atın: http://matplotlib.org/api/colors_api.html#matplotlib.colors.Colormap )

Görünüşe göre böyle bir şey istiyorsun:

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

görüntü açıklamasını buraya girin


bu gerçekten iyi - set_under kullanmayı denedim ama vmin'i dahil etmedim, bu yüzden hiçbir şey yaptığını düşünmüyorum
bph

10

Bu konu zaten iyi bir şekilde ele alınmıştır, ancak daha spesifik bir şey eklemek istedim: Belirli bir değerin o renge (herhangi bir renge değil) eşleneceğinden emin olmak istedim.

Karmaşık değil ama biraz zaman aldığından başkalarının benim kadar zaman kaybetmemesine yardımcı olabilir :)

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

görüntü açıklamasını buraya girin


Bunu kopyalamaya çalışıyordu, ancak kod çalışmıyor çünkü 'tmp' tanımsız. Ayrıca lambda işlevinde 'pos'un ne olduğu belirsizdir. Teşekkürler!
George Liu

@GeorgeLiu Gerçekten yazdın! Kopyala / yapıştır hatası yaptım ve şimdi düzeltildi! Kod pasajı şimdi çalışıyor! İlgili posBuranın neden tamamen emin değilim ama FuncFormatter () tarafından istenen ... Belki başkası bizi bu konuda aydınlatmak yapabilir!
Enzoupi

7

Bu fikirleri araştırıyorum ve işte beş sent değerim. Bu söylemekten çekiniyor BoundaryNormyanı sıra belirterek normbir argüman olarak scatterve colorbar. Ancak, oldukça uzun soluklu çağrıyı ortadan kaldırmanın bir yolunu bulamadım matplotlib.colors.LinearSegmentedColormap.from_list.

Bazı arka plan, matplotlib'in ayrık verilerle kullanılması amaçlanan sözde niteliksel renk haritaları sağlamasıdır. Set1örneğin 9 kolayca ayırt edilebilen renge sahiptir ve tab2020 renk için kullanılabilir. Bu haritalarla, aşağıdaki örnekte olduğu gibi, ilk n rengini, n kategorili dağılım grafiklerini renklendirmek için kullanmak doğal olabilir. Örnek ayrıca, uygun şekilde etiketlenmiş n ayrı rengin olduğu bir renk çubuğu üretir.

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)

aşağıdaki görüntüyü oluşturur. nÇağrısında Set1belirtiyorsa ilk no renk haritası renkleri ve son nçağrısında from_list belirtiyorsa ile bir harita oluşturmak için nrenkler (varsayılan 256 olmak üzere). cmVarsayılan renk haritası olarak ayarlamak plt.set_cmapiçin, ona bir isim vermenin ve kaydettirmenin gerekli olduğunu gördüm, yani:

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)

düzensiz renklerle dağılım grafiği


0

Renk haritanızı oluşturmak için renklere bakmak isteyeceğinizi düşünüyorum.ListedColormap veya yalnızca statik bir renk haritasına ihtiyacınız varsa yardımcı olabilecek bir uygulama üzerinde çalışıyorum .


Bu havalı görünüyor, muhtemelen ihtiyaçlarımı fazlasıyla karşılayacak - mevcut bir renk haritasına gri bir değeri etiketlemenin bir yolunu önerebilir misiniz? böylece 0 değerleri gri çıkarken diğerleri renk olarak çıksın?
bph

@Hiett, y değerlerinize göre bir RGB dizisi color_list oluşturup bunu ListedColormap'e aktarmaya ne dersiniz? Color_list [y == value_to_tag] = gray_color ile bir değeri etiketleyebilirsiniz.
ChrisC
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.