Matplotlib'de bir renk haritasının orta noktasını tanımlama


88

Bir renk haritasının orta noktasını ayarlamak istiyorum, yani verilerim -5'ten 10'a gidiyor, sıfırın orta olmasını istiyorum. Sanırım bunu yapmanın yolu alt sınıfları normalleştirmek ve normu kullanmaktır, ancak herhangi bir örnek bulamadım ve tam olarak neyi hayata geçirmem gerektiği bana açık değil.


buna, haritanın merkez noktasının önemli olduğu ve verilerin bu noktanın üstüne ve altına gittiği "uzaklaşan" veya "iki kutuplu" renk haritası denir. sandia.gov/~kmorel/documents/ColorMaps
Endolit

3
Bu konudaki tüm cevaplar oldukça karmaşık görünüyor. Kullanımı kolay çözüm, aynı zamanda matplotlib belgelerine de dahil olan bu mükemmel yanıtta gösterilmektedir , Özel normalleştirme: İki doğrusal aralık .
ImportanceOfBeingErnest

Yanıtlar:


15

Matplotlib sürüm 3.1'de DivergingNorm sınıfının eklendiğine dikkat edin. Sanırım kullanım durumunuzu kapsıyor. Şu şekilde kullanılabilir:

from matplotlib import colors
colors.DivergingNorm(vmin=-4000., vcenter=0., vmax=10000)

Matplotlib 3.2'de sınıf TwoSlopesNorm olarak yeniden adlandırıldı


Bu ilginç görünüyor, ancak bunun, çizmeden önce verileri dönüştürmek için kullanılması gerektiği anlaşılıyor. Renk çubuğunun açıklaması, orijinal olanla değil, dönüştürülen verilerle ilgili olacaktır.
bli

3
@bli durum bu değil. normResminiz için normalleştirme yapar. normsrenk haritalarıyla el ele gidin.
Paul H

1
Can sıkıcı bir şekilde bu, nasıl değiştirileceğine dair herhangi bir belge olmadan 3.2 itibariyle kullanımdan kaldırıldı: matplotlib.org/3.2.0/api/_as_gen/…
daknowles

1
Evet, dokümanlar net değil. Sanırım şu şekilde yeniden adlandırıldı TwoSlopeNorm: matplotlib.org/3.2.0/api/_as_gen/…
macKaiver

91

Bunun oyuna geç kaldığını biliyorum, ancak bu süreçten yeni geçtim ve belki de alt sınıfları normalleştirmekten daha az sağlam, ancak çok daha basit bir çözüm buldum. Gelecek nesil için burada paylaşmanın iyi olacağını düşündüm.

İşlev

import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import AxesGrid

def shiftedColorMap(cmap, start=0, midpoint=0.5, stop=1.0, name='shiftedcmap'):
    '''
    Function to offset the "center" of a colormap. Useful for
    data with a negative min and positive max and you want the
    middle of the colormap's dynamic range to be at zero.

    Input
    -----
      cmap : The matplotlib colormap to be altered
      start : Offset from lowest point in the colormap's range.
          Defaults to 0.0 (no lower offset). Should be between
          0.0 and `midpoint`.
      midpoint : The new center of the colormap. Defaults to 
          0.5 (no shift). Should be between 0.0 and 1.0. In
          general, this should be  1 - vmax / (vmax + abs(vmin))
          For example if your data range from -15.0 to +5.0 and
          you want the center of the colormap at 0.0, `midpoint`
          should be set to  1 - 5/(5 + 15)) or 0.75
      stop : Offset from highest point in the colormap's range.
          Defaults to 1.0 (no upper offset). Should be between
          `midpoint` and 1.0.
    '''
    cdict = {
        'red': [],
        'green': [],
        'blue': [],
        'alpha': []
    }

    # regular index to compute the colors
    reg_index = np.linspace(start, stop, 257)

    # shifted index to match the data
    shift_index = np.hstack([
        np.linspace(0.0, midpoint, 128, endpoint=False), 
        np.linspace(midpoint, 1.0, 129, endpoint=True)
    ])

    for ri, si in zip(reg_index, shift_index):
        r, g, b, a = cmap(ri)

        cdict['red'].append((si, r, r))
        cdict['green'].append((si, g, g))
        cdict['blue'].append((si, b, b))
        cdict['alpha'].append((si, a, a))

    newcmap = matplotlib.colors.LinearSegmentedColormap(name, cdict)
    plt.register_cmap(cmap=newcmap)

    return newcmap

Bir örnek

biased_data = np.random.random_integers(low=-15, high=5, size=(37,37))

orig_cmap = matplotlib.cm.coolwarm
shifted_cmap = shiftedColorMap(orig_cmap, midpoint=0.75, name='shifted')
shrunk_cmap = shiftedColorMap(orig_cmap, start=0.15, midpoint=0.75, stop=0.85, name='shrunk')

fig = plt.figure(figsize=(6,6))
grid = AxesGrid(fig, 111, nrows_ncols=(2, 2), axes_pad=0.5,
                label_mode="1", share_all=True,
                cbar_location="right", cbar_mode="each",
                cbar_size="7%", cbar_pad="2%")

# normal cmap
im0 = grid[0].imshow(biased_data, interpolation="none", cmap=orig_cmap)
grid.cbar_axes[0].colorbar(im0)
grid[0].set_title('Default behavior (hard to see bias)', fontsize=8)

im1 = grid[1].imshow(biased_data, interpolation="none", cmap=orig_cmap, vmax=15, vmin=-15)
grid.cbar_axes[1].colorbar(im1)
grid[1].set_title('Centered zero manually,\nbut lost upper end of dynamic range', fontsize=8)

im2 = grid[2].imshow(biased_data, interpolation="none", cmap=shifted_cmap)
grid.cbar_axes[2].colorbar(im2)
grid[2].set_title('Recentered cmap with function', fontsize=8)

im3 = grid[3].imshow(biased_data, interpolation="none", cmap=shrunk_cmap)
grid.cbar_axes[3].colorbar(im3)
grid[3].set_title('Recentered cmap with function\nand shrunk range', fontsize=8)

for ax in grid:
    ax.set_yticks([])
    ax.set_xticks([])

Örneğin sonuçları:

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


Müthiş katkınız için çok teşekkürler! Bununla birlikte, kod aynı renk haritasını hem kırpma hem de değiştirme yeteneğine sahip değildi ve talimatlarınız biraz belirsiz ve yanıltıcıydı. Şimdi bunu düzelttim ve gönderinizi düzenleme özgürlüğünü aldım. Ayrıca, onu kişisel kütüphanelerimden birine ekledim ve sizi yazar olarak ekledim. Umarım aldırış etmezsin.
TheChymera

@TheChymera sağ alt köşedeki renk haritası hem kırpılmış hem de yeniden ortalanmış. Bunu uygun gördüğünüz gibi kullanmaktan çekinmeyin.
Paul H

Evet, var, ne yazık ki sadece bir tesadüf olarak doğru görünüyor. Eğer startve stop0 ve 1 sırasıyla yapmanız sonra değil reg_index = np.linspace(start, stop, 257), artık bu değer 129 nedenle tüm yeniden ölçeklendirme kırpmak zaman hiçbir anlamı yapar, orijinal CMAP orta noktası olduğunu varsayabiliriz. Ayrıca, talimat verdiğiniz gibi 0'dan 1'e değil, 0'dan start0.5'e ve stop0.5'den 1'e olmalıdır.
TheChymera

@TheChymera Senin versiyonunu denedim ve bu konuda iki düşüncem vardı. 1) bana öyle görünüyor ki oluşturduğunuz indekslerin tamamı 257 uzunluğunda ve matplotlib'de varsayılan olarak 256 olarak ayarlandı. 2) verilerimin -1 ile 1000 arasında değiştiğini, pozitiflerin hakim olduğunu ve bu nedenle daha fazla seviyenin / katmanların pozitif dala gitmesi gerektiğini varsayalım. Fakat fonksiyonunuz hem negatiflere hem de pozitiflere 128 seviye veriyor, bu yüzden seviyeleri eşit olmayan bir şekilde bölmek daha "adil" olur diye düşünüyorum.
Jason

Bu mükemmel bir çözümdür, ancak midpointverilerin 0 veya 1'e eşit olması durumunda başarısız olur . Bu soruna basit bir çözüm için aşağıdaki cevabıma bakın.
DaveTheScientist

22

İşte Normalize alt sınıflandırma çözümü. Kullanmak için

norm = MidPointNorm(midpoint=3)
imshow(X, norm=norm)

İşte Sınıf:

import numpy as np
from numpy import ma
from matplotlib import cbook
from matplotlib.colors import Normalize

class MidPointNorm(Normalize):    
    def __init__(self, midpoint=0, vmin=None, vmax=None, clip=False):
        Normalize.__init__(self,vmin, vmax, clip)
        self.midpoint = midpoint

    def __call__(self, value, clip=None):
        if clip is None:
            clip = self.clip

        result, is_scalar = self.process_value(value)

        self.autoscale_None(result)
        vmin, vmax, midpoint = self.vmin, self.vmax, self.midpoint

        if not (vmin < midpoint < vmax):
            raise ValueError("midpoint must be between maxvalue and minvalue.")       
        elif vmin == vmax:
            result.fill(0) # Or should it be all masked? Or 0.5?
        elif vmin > vmax:
            raise ValueError("maxvalue must be bigger than minvalue")
        else:
            vmin = float(vmin)
            vmax = float(vmax)
            if clip:
                mask = ma.getmask(result)
                result = ma.array(np.clip(result.filled(vmax), vmin, vmax),
                                  mask=mask)

            # ma division is very slow; we can take a shortcut
            resdat = result.data

            #First scale to -1 to 1 range, than to from 0 to 1.
            resdat -= midpoint            
            resdat[resdat>0] /= abs(vmax - midpoint)            
            resdat[resdat<0] /= abs(vmin - midpoint)

            resdat /= 2.
            resdat += 0.5
            result = ma.array(resdat, mask=result.mask, copy=False)                

        if is_scalar:
            result = result[0]            
        return result

    def inverse(self, value):
        if not self.scaled():
            raise ValueError("Not invertible until scaled")
        vmin, vmax, midpoint = self.vmin, self.vmax, self.midpoint

        if cbook.iterable(value):
            val = ma.asarray(value)
            val = 2 * (val-0.5)  
            val[val>0]  *= abs(vmax - midpoint)
            val[val<0] *= abs(vmin - midpoint)
            val += midpoint
            return val
        else:
            val = 2 * (value - 0.5)
            if val < 0: 
                return  val*abs(vmin-midpoint) + midpoint
            else:
                return  val*abs(vmax-midpoint) + midpoint

Bu sınıfı, daha fazla alt sınıf oluşturmak zorunda kalmadan log veya sym-log ölçeklendirmeye ek olarak kullanmak mümkün müdür? Şu anki kullanım durumum zaten "norm = SymLogNorm (linthresh = 1)" kullanıyor
AnnanFay

Mükemmel, tam da aradığım buydu. Belki farkı göstermek için bir resim eklemelisiniz? Orta nokta, orta noktanın ekstremitelere doğru sürüklenebildiği diğer orta nokta normalleştiricilerinin aksine, burada orta nokta çubuğun merkezindedir.
titiz

18

Alt sınıflandırma yerine (görüntü verileriyle çalıştığınızı varsayarak) vminve vmaxargümanlarını kullanmak en kolayıdır .imshowmatplotlib.colors.Normalize

Örneğin

import numpy as np
import matplotlib.pyplot as plt

data = np.random.random((10,10))
# Make the data range from about -5 to 10
data = 10 / 0.75 * (data - 0.25)

plt.imshow(data, vmin=-10, vmax=10)
plt.colorbar()

plt.show()

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


1
Rengin tonlamasını daha iyi görebilmemiz için örneğin bir gauss eğrisine güncellenmesi mümkün mü?
Dat Chu

3
Bu çözümü sevmiyorum çünkü mevcut renklerin tam dinamik aralığını kullanmıyor. Ayrıca sembolik bir normalleştirme türü oluşturmak için bir normalize etme örneği istiyorum.
tillsten

2
@tillsten - Kafam karıştı, öyleyse ... Ortada 0 istiyorsanız, colorbar'ın tam dinamik aralığını kullanamazsınız, değil mi? Doğrusal olmayan bir ölçek mi istiyorsunuz? 0'ın üzerindeki değerler için bir ölçek, aşağıdaki değerler için bir ölçek? Bu durumda, evet, alt sınıflara girmeniz gerekecek Normalize. Birazdan bir örnek ekleyeceğim (başka birinin beni yenmediğini varsayarak ...).
Joe Kington

@Joe: Haklısın, bu doğrusal değil (daha doğrusu iki doğrusal parça). Vmin / vmax kullanıldığında, -5'ten küçük değerler için renk aralığı kullanılmaz (bu, bazı uygulamalarda anlamlıdır, ancak benimki değildir.).
tillsten

2
Z'deki jenerik veriler için:vmax=abs(Z).max(), vmin=-abs(Z).max()
endolith

13

Burada bir alt sınıf ve Normalizeardından minimal bir örnek oluşturuyorum.

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt


class MidpointNormalize(mpl.colors.Normalize):
    def __init__(self, vmin, vmax, midpoint=0, clip=False):
        self.midpoint = midpoint
        mpl.colors.Normalize.__init__(self, vmin, vmax, clip)

    def __call__(self, value, clip=None):
        normalized_min = max(0, 1 / 2 * (1 - abs((self.midpoint - self.vmin) / (self.midpoint - self.vmax))))
        normalized_max = min(1, 1 / 2 * (1 + abs((self.vmax - self.midpoint) / (self.midpoint - self.vmin))))
        normalized_mid = 0.5
        x, y = [self.vmin, self.midpoint, self.vmax], [normalized_min, normalized_mid, normalized_max]
        return np.ma.masked_array(np.interp(value, x, y))


vals = np.array([[-5., 0], [5, 10]]) 
vmin = vals.min()
vmax = vals.max()

norm = MidpointNormalize(vmin=vmin, vmax=vmax, midpoint=0)
cmap = 'RdBu_r' 

plt.imshow(vals, cmap=cmap, norm=norm)
plt.colorbar()
plt.show()

Sonuç: pic-1

Yalnızca pozitif verilerle aynı örnek vals = np.array([[1., 3], [6, 10]])

pic-2

Özellikleri:

  • Orta nokta orta rengi alır.
  • Üst ve alt aralıklar aynı doğrusal dönüşüm ile yeniden ölçeklendirilir.
  • Renk çubuğunda yalnızca resimde görünen renk gösterilir.
  • Şundan vmindaha büyük olsa bile iyi çalışıyor gibi görünüyor midpoint(ancak tüm uç durumları test etmedi).

Bu çözüm, bu sayfadaki aynı adı taşıyan bir sınıftan esinlenmiştir.


3
Basitliği nedeniyle en iyi cevap. Diğer cevaplar, ancak zaten bir süper uzman olmaya çalışan bir Matplotlib uzmanı iseniz en iyisidir. Çoğu matplotlib cevabı arayan, eve köpeklerine ve / veya ailelerine gitmek için bir şeyler yaptırmaya çalışıyor ve onlar için bu cevap en iyisidir.
sapo_cosmico

Bu çözüm gerçekten de en iyisi gibi görünüyor ama işe yaramıyor! Test komut dosyasını yeni çalıştırdım ve sonuç tamamen farklı (sadece mavi kareler dahil ve kırmızı yok). @icemtel, kontrol edebilir misin lütfen? (girintili problemin yanında def __call__)
Filipe

Tamam, problem (ler) i buldum: hesaplamadaki sayılar normalized_minve normalized_maxtamsayılar olarak alınır. Onları 0.0 olarak koyun. Ayrıca figürünüzün doğru çıktısını almak için kullanmak zorundaydım vals = sp.array([[-5.0, 0.0], [5.0, 10.0]]) . Cevabınız için yine de teşekkürler!
Filipe

Merhaba @Filipe Sorununuzu makinemde yeniden oluşturamıyorum (Python 3.7, matplotlib 2.2.3 ve daha yeni sürümlerde de aynı olması gerektiğini düşünüyorum). Hangi versiyona sahipsin? Her neyse, float türü dizisi oluşturarak küçük bir düzenleme yaptım ve girinti sorununu çözdüm. Gösterdiğiniz için teşekkürler
icemtel

Hmm .. python3 ile denedim ve o da çalışıyor. Ama python2.7 kullanıyorum. Düzelttiğiniz ve cevabınız için teşekkür ederim. Kullanması çok basit! :)
Filipe

5

Hala bir cevap arıyor musunuz emin değilim. Benim için alt sınıfa çalışmak Normalizebaşarısız oldu. Bu yüzden amaçladığınız efekti elde etmek için yeni bir veri seti, işaretler ve işaret etiketleri manuel olarak oluşturmaya odaklandım.

scaleMatplotlib'de çizgi grafiklerini 'syslog' kurallarına göre dönüştürmek için kullanılan bir sınıfa sahip modülü buldum , bu yüzden veriyi dönüştürmek için bunu kullanıyorum. Sonra verileri 0'dan 1'e çıkacak şekilde ölçeklendiriyorum (neNormalize genellikle olduğu gibi), ancak pozitif sayıları negatif sayılardan farklı şekilde ölçeklendiriyorum. Bunun nedeni, vmax ve vmin'iniz aynı olmayabilir, bu yüzden .5 -> 1, .5 -> 0'dan daha büyük bir pozitif aralığı kapsayabilir, negatif aralık kapar. Tik ve etiket değerlerini hesaplamak için bir rutin oluşturmak benim için daha kolaydı.

Aşağıda kod ve örnek bir şekil bulunmaktadır.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.mpl as mpl
import matplotlib.scale as scale

NDATA = 50
VMAX=10
VMIN=-5
LINTHRESH=1e-4

def makeTickLables(vmin,vmax,linthresh):
    """
    make two lists, one for the tick positions, and one for the labels
    at those positions. The number and placement of positive labels is 
    different from the negative labels.
    """
    nvpos = int(np.log10(vmax))-int(np.log10(linthresh))
    nvneg = int(np.log10(np.abs(vmin)))-int(np.log10(linthresh))+1
    ticks = []
    labels = []
    lavmin = (np.log10(np.abs(vmin)))
    lvmax = (np.log10(np.abs(vmax)))
    llinthres = int(np.log10(linthresh))
    # f(x) = mx+b
    # f(llinthres) = .5
    # f(lavmin) = 0
    m = .5/float(llinthres-lavmin)
    b = (.5-llinthres*m-lavmin*m)/2
    for itick in range(nvneg):
        labels.append(-1*float(pow(10,itick+llinthres)))
        ticks.append((b+(itick+llinthres)*m))
    # add vmin tick
    labels.append(vmin)
    ticks.append(b+(lavmin)*m)

    # f(x) = mx+b
    # f(llinthres) = .5
    # f(lvmax) = 1
    m = .5/float(lvmax-llinthres)
    b = m*(lvmax-2*llinthres) 
    for itick in range(1,nvpos):
        labels.append(float(pow(10,itick+llinthres)))
        ticks.append((b+(itick+llinthres)*m))
    # add vmax tick
    labels.append(vmax)
    ticks.append(b+(lvmax)*m)

    return ticks,labels


data = (VMAX-VMIN)*np.random.random((NDATA,NDATA))+VMIN

# define a scaler object that can transform to 'symlog'
scaler = scale.SymmetricalLogScale.SymmetricalLogTransform(10,LINTHRESH)
datas = scaler.transform(data)

# scale datas so that 0 is at .5
# so two seperate scales, one for positive and one for negative
data2 = np.where(np.greater(data,0),
                 .75+.25*datas/np.log10(VMAX),
                 .25+.25*(datas)/np.log10(np.abs(VMIN))
                 )

ticks,labels=makeTickLables(VMIN,VMAX,LINTHRESH)

cmap = mpl.cm.jet
fig = plt.figure()
ax = fig.add_subplot(111)
im = ax.imshow(data2,cmap=cmap,vmin=0,vmax=1)
cbar = plt.colorbar(im,ticks=ticks)
cbar.ax.set_yticklabels(labels)

fig.savefig('twoscales.png')

vmax = 10, vmin = -5 ve linthresh = 1e-4

VMAXİyi davrandığını onaylamak için betiğin üst kısmındaki "sabitleri" (örn. ) Ayarlayabilirsiniz.


Öneriniz için teşekkürler, aşağıda görüldüğü gibi, alt sınıflandırma konusunda başarılı oldum. Ancak kodunuz, onay etiketlerini doğru yapmak için hala çok kullanışlıdır.
tillsten

4

Paul H'nin mükemmel cevabını kullanıyordum, ancak bir sorunla karşılaştım çünkü verilerimin bir kısmı negatiften pozitife, diğer setler 0'dan pozitife veya negatiften 0'a değişiyordu; her iki durumda da 0'ın beyaz olarak renklendirilmesini istedim (kullandığım renk haritasının orta noktası). Mevcut uygulamayla, midpointdeğeriniz 1 veya 0'a eşitse, orijinal eşlemelerin üzerine yazılmıyordu. Bunu aşağıdaki resimde görebilirsiniz: düzenlemeden önceki grafikler 3. sütun doğru görünüyor, ancak 2. sütundaki koyu mavi alan ve kalan sütunlardaki koyu kırmızı alanın tümü beyaz olmalı (veri değerleri aslında 0'dır). Düzeltmemi kullanmak bana şunu veriyor: düzenleme sonrası grafikler İşlevim, fordöngünün başlangıcındaki düzenlemelerimle, Paul H'nin işleviyle aynı :

def shiftedColorMap(cmap, min_val, max_val, name):
    '''Function to offset the "center" of a colormap. Useful for data with a negative min and positive max and you want the middle of the colormap's dynamic range to be at zero. Adapted from /programming/7404116/defining-the-midpoint-of-a-colormap-in-matplotlib

    Input
    -----
      cmap : The matplotlib colormap to be altered.
      start : Offset from lowest point in the colormap's range.
          Defaults to 0.0 (no lower ofset). Should be between
          0.0 and `midpoint`.
      midpoint : The new center of the colormap. Defaults to
          0.5 (no shift). Should be between 0.0 and 1.0. In
          general, this should be  1 - vmax/(vmax + abs(vmin))
          For example if your data range from -15.0 to +5.0 and
          you want the center of the colormap at 0.0, `midpoint`
          should be set to  1 - 5/(5 + 15)) or 0.75
      stop : Offset from highets point in the colormap's range.
          Defaults to 1.0 (no upper ofset). Should be between
          `midpoint` and 1.0.'''
    epsilon = 0.001
    start, stop = 0.0, 1.0
    min_val, max_val = min(0.0, min_val), max(0.0, max_val) # Edit #2
    midpoint = 1.0 - max_val/(max_val + abs(min_val))
    cdict = {'red': [], 'green': [], 'blue': [], 'alpha': []}
    # regular index to compute the colors
    reg_index = np.linspace(start, stop, 257)
    # shifted index to match the data
    shift_index = np.hstack([np.linspace(0.0, midpoint, 128, endpoint=False), np.linspace(midpoint, 1.0, 129, endpoint=True)])
    for ri, si in zip(reg_index, shift_index):
        if abs(si - midpoint) < epsilon:
            r, g, b, a = cmap(0.5) # 0.5 = original midpoint.
        else:
            r, g, b, a = cmap(ri)
        cdict['red'].append((si, r, r))
        cdict['green'].append((si, g, g))
        cdict['blue'].append((si, b, b))
        cdict['alpha'].append((si, a, a))
    newcmap = matplotlib.colors.LinearSegmentedColormap(name, cdict)
    plt.register_cmap(cmap=newcmap)
    return newcmap

DÜZENLEME: Verilerimden bazıları küçük bir pozitif değerden daha büyük bir pozitif değere değiştiğinde, çok düşük değerlerin beyaz yerine kırmızı renkte olduğu benzer bir sorunla tekrar karşılaştım. Edit #2Yukarıdaki koda satır ekleyerek düzelttim .


Bu güzel görünüyor, ancak Paul H'nin (ve yorumlarının) argümanları değişmiş görünüyor ... Cevabınıza örnek bir çağrı ekleyebilir misiniz?
Filipe

1

Vmin, vmax ve sıfır arasındaki oranı hesaplamaktan çekinmezseniz, bu maviden beyaza kırmızıya oldukça basit bir doğrusal haritadır ve orana göre beyazı ayarlar z:

def colormap(z):
    """custom colourmap for map plots"""

    cdict1 = {'red': ((0.0, 0.0, 0.0),
                      (z,   1.0, 1.0),
                      (1.0, 1.0, 1.0)),
              'green': ((0.0, 0.0, 0.0),
                        (z,   1.0, 1.0),
                        (1.0, 0.0, 0.0)),
              'blue': ((0.0, 1.0, 1.0),
                       (z,   1.0, 1.0),
                       (1.0, 0.0, 0.0))
              }

    return LinearSegmentedColormap('BlueRed1', cdict1)

Cdict formatı oldukça basittir: satırlar degradede oluşturulan noktalardır: ilk giriş x-değeridir (0'dan 1'e gradyan boyunca oran), ikincisi önceki segmentin bitiş değeridir ve üçüncüsü, sonraki segment için başlangıç ​​değeridir - yumuşak degradeler istiyorsanız, son ikisi her zaman aynıdır. Daha fazla ayrıntı için belgelere bakın .


1
Ayrıca LinearSegmentedColormap.from_list()tuples içinde belirtme (val,color)ve bunları colorbu yöntemin argümanına liste olarak iletme seçeneği de vardır val0=0<val1<...<valN==1.
maurizio

0

Benzer bir sorun yaşadım, ancak en yüksek değerin tam kırmızı olmasını ve düşük mavi değerlerini kesmesini istedim, bu da temelde renk çubuğunun alt kısmı kesilmiş gibi görünmesini sağladı. Bu benim için çalıştı (isteğe bağlı şeffaflık içerir):

def shift_zero_bwr_colormap(z: float, transparent: bool = True):
    """shifted bwr colormap"""
    if (z < 0) or (z > 1):
        raise ValueError('z must be between 0 and 1')

    cdict1 = {'red': ((0.0, max(-2*z+1, 0), max(-2*z+1, 0)),
                      (z,   1.0, 1.0),
                      (1.0, 1.0, 1.0)),

              'green': ((0.0, max(-2*z+1, 0), max(-2*z+1, 0)),
                        (z,   1.0, 1.0),
                        (1.0, max(2*z-1,0),  max(2*z-1,0))),

              'blue': ((0.0, 1.0, 1.0),
                       (z,   1.0, 1.0),
                       (1.0, max(2*z-1,0), max(2*z-1,0))),
              }
    if transparent:
        cdict1['alpha'] = ((0.0, 1-max(-2*z+1, 0), 1-max(-2*z+1, 0)),
                           (z,   0.0, 0.0),
                           (1.0, 1-max(2*z-1,0),  1-max(2*z-1,0)))

    return LinearSegmentedColormap('shifted_rwb', cdict1)

cmap =  shift_zero_bwr_colormap(.3)

x = np.arange(0, np.pi, 0.1)
y = np.arange(0, 2*np.pi, 0.1)
X, Y = np.meshgrid(x, y)
Z = np.cos(X) * np.sin(Y) * 5 + 5
plt.plot([0, 10*np.pi], [0, 20*np.pi], color='c', lw=20, zorder=-3)
plt.imshow(Z, interpolation='nearest', origin='lower', cmap=cmap)
plt.colorbar()
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.