Matplotlib में कॉलोर्म के मध्य बिंदु को परिभाषित करना


87

मैं एक कॉलॉर्मैप के मध्य बिंदु को सेट करना चाहता हूं, अर्थात मेरा डेटा -5 से 10 तक जाता है, मैं चाहता हूं कि शून्य मध्य हो। मुझे लगता है कि ऐसा करने का तरीका सामान्यीकृत करना और आदर्श का उपयोग करना है, लेकिन मुझे कोई उदाहरण नहीं मिला और यह मेरे लिए स्पष्ट नहीं है कि वास्तव में मुझे क्या लागू करना है।


इसे "डाइवरिंग" या "बाइपोलर" कॉलोर्मैप कहा जाता है, जहां मानचित्र का केंद्र बिंदु महत्वपूर्ण है और डेटा इस बिंदु से ऊपर और नीचे जाता है। sandia.gov/~kmorel/documents/ColorMaps
endolith

3
इस सूत्र में सभी उत्तर जटिल प्रतीत होते हैं। समाधान का उपयोग करना आसान है इस उत्कृष्ट उत्तर में दिखाया गया है , जो इस बीच में भी इसे मैटलपोटलिब प्रलेखन, अनुभाग कस्टम सामान्यीकरण: दो रैखिक श्रेणियों में बनाया है
इम्पोर्टेंसऑफबिंगएरेस्ट

जवाबों:


14

ध्यान दें कि matplotlib संस्करण 3.1 में DivergingNorm वर्ग जोड़ा गया था। मुझे लगता है कि यह आपके उपयोग-मामले को कवर करता है। इसका उपयोग इस तरह किया जा सकता है:

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

Matplotlib 3.2 में क्लास का नाम बदलकर TwoSlopesNorm कर दिया गया है


यह दिलचस्प लग रहा है, लेकिन ऐसा लगता है कि इसका इस्तेमाल साजिश रचने से पहले डेटा को बदलने के लिए किया जाना है। रंग पट्टी की किंवदंती रूपांतरित डेटा से संबंधित होगी, मूल नहीं।
ब्लि

3
@ ऐसी बात नहीं है। normअपनी छवि के लिए सामान्य है। normsकोलमैप्स के साथ हाथ से हाथ मिलाएं।
पॉल एच।

1
अनायास यह 3.2 के रूप में पदावनत है जिसमें कोई डॉक नहीं है कि इसे कैसे बदलना है: matplotlib.org/3.2.0/api/_as_gen/…
daknowles

1
हाँ डॉक्स अस्पष्ट हैं। मुझे लगता है कि इसका नाम बदल दिया गया है TwoSlopeNorm: matplotlib.org/3.2.0/api/_as_gen/…
macKaiver

91

मुझे पता है कि इस खेल के लिए देर हो चुकी है, लेकिन मैं बस इस प्रक्रिया से गुज़रा और एक समाधान के साथ आया जो शायद सामान्य बनाने की तुलना में कम मजबूत है, लेकिन बहुत सरल है। मैंने सोचा कि इसे यहाँ पोस्टीरिटी के लिए साझा करना अच्छा होगा।

कार्यक्रम

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

एक उदाहरण

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

उदाहरण के परिणाम:

यहाँ छवि विवरण दर्ज करें


आपके भयानक योगदान के लिए बहुत धन्यवाद! हालाँकि, कोड एक ही रंग के मानचित्र को क्रॉप और शिफ्ट करने दोनों में सक्षम नहीं था , और आपके निर्देश थोड़े अव्यवस्थित और गलत थे। मैंने अब इसे ठीक कर लिया है और आपकी पोस्ट को संपादित करने की स्वतंत्रता ले ली है। इसके अलावा, मैंने इसे अपने व्यक्तिगत पुस्तकालयों में से एक में शामिल किया है , और आपको एक लेखक के रूप में जोड़ा है। मैं आशा करता हूँ आपको कोई आपत्ति नही है।
TheChymera

@ TheChymera निचले दाएं कोने में स्थित कॉलमैप को क्रॉप और पुनरावृत्त दोनों किया गया है। जैसा कि आप फिट देखते हैं, इसका उपयोग करने के लिए स्वतंत्र महसूस करें।
पॉल एच।

हाँ, यह दुख की बात है कि यह केवल एक संयोग के रूप में लगभग सही लगता है। तो startऔर stop0 और 1 क्रमशः, आप कर के बाद नहीं कर रहे हैं reg_index = np.linspace(start, stop, 257), आप अब मान सकते हैं कि मूल्य 129, मूल CMAP के मध्य है इसलिए पूरे rescaling कोई मतलब नहीं है जब भी आप फसल में आता है। इसके अलावा, start0 से 0.5 तक और stop0.5 से 1 तक होना चाहिए , जैसा कि आप निर्देश देते हैं, दोनों 0 से 1 तक नहीं।
TheChymera

@ TheCmermera मैंने आपके संस्करण की कोशिश की और इसके बारे में दो विचार थे। 1) यह मुझे लगता है कि आपके द्वारा बनाए गए सूचकांकों की लंबाई 257 है, और matplotlib में यह 256% मान लिया गया है? 2) मान लीजिए कि मेरी डेटा सीमा -1 से 1000 तक है, यह सकारात्मकता पर हावी है और इसलिए अधिक स्तर / परतों को सकारात्मक शाखा में जाना चाहिए। लेकिन आपका कार्य नकारात्मक और सकारात्मक दोनों को 128 स्तर देता है, इसलिए मुझे लगता है कि असमान स्तर को विभाजित करने के लिए यह अधिक "उचित" होगा।
जेसन

यह एक उत्कृष्ट समाधान है, लेकिन यह विफल रहता है यदि midpointडेटा 0 या 1 के बराबर है। उस समस्या के लिए एक सरल समाधान के लिए नीचे मेरा उत्तर देखें।
डेविसिस्टिस्ट

22

यहाँ एक समाधान उपवर्ग सामान्यीकरण है। इसके प्रयेाग के लिए

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

यहाँ कक्षा है:

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

क्या अधिक उप-वर्ग बनाने के बिना लॉग या सिम-लॉग स्केलिंग के अलावा इस वर्ग का उपयोग करना संभव है? मेरा वर्तमान उपयोग मामला पहले से ही "आदर्श = SymLogNorm (linthresh = 1)" का उपयोग करता है
AnnanFay

बिल्कुल सही, यह वही है जो मैं देख रहा था। शायद आपको अंतर प्रदर्शित करने के लिए एक चित्र जोड़ना चाहिए? यहां मिडपॉइंट को बार में केंद्रित किया गया है, अन्य मिडपॉइंट नॉर्मलाइजर्स के विपरीत जहां मिडपॉइंट को चरम सीमाओं की ओर खींचा जा सकता है।
20

18

उपवर्ग के बजाय केवल ( vminऔर आप छवि डेटा के साथ काम कर रहे हैं) यह vmaxतर्क का उपयोग करना आसान है ।imshowmatplotlib.colors.Normalize

उदाहरण के लिए

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

यहाँ छवि विवरण दर्ज करें


1
क्या यह संभव है कि उदाहरण को एक गाऊसी वक्र से अपडेट किया जाए ताकि हम रंग के उन्नयन को बेहतर ढंग से देख सकें?
Dat चू

3
मुझे यह समाधान पसंद नहीं है, क्योंकि यह उपलब्ध रंगों की पूर्ण गतिशील सीमा का उपयोग नहीं करता है। इसके अलावा, मैं सामान्यीकरण का एक उदाहरण देना चाहूंगा, जो कि एक समलॉग-प्रकार के सामान्यीकरण का निर्माण कर सके।
tillsten

2
@tillsten - मैं उलझन में हूँ, तो ... आप colorbar की पूर्ण गतिशील रेंज का उपयोग नहीं कर सकते हैं यदि आप बीच में 0 चाहते हैं, है ना? तुम तो एक गैर रेखीय पैमाने चाहते हैं? नीचे दिए गए मानों के लिए एक पैमाना, नीचे दिए गए मानों के लिए एक पैमाना? उस मामले में, हाँ, आपको उपवर्ग की आवश्यकता होगी Normalize। मैं सिर्फ एक उदाहरण में जोड़ दूंगा (किसी और को यह मुझे नहीं हराता ...)।
जो किंगटन

@ जो: आप सही हैं, यह रैखिक (अधिक सटीक, दो रैखिक भागों) नहीं है। Vmin / vmax का उपयोग करते हुए, -5 से छोटे मान के लिए कलंगे का उपयोग नहीं किया जाता है (जो कुछ अनुप्रयोगों में समझ में आता है, लेकिन मेरा नहीं।)।
16

2
जेड में जेनेरिक डेटा के लिए:vmax=abs(Z).max(), vmin=-abs(Z).max()
एंडोलिथ

12

यहाँ मैं Normalizeएक न्यूनतम उदाहरण के बाद का उपवर्ग बनाता हूँ ।

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

परिणाम: पिक-1

केवल सकारात्मक डेटा के साथ एक ही उदाहरण vals = np.array([[1., 3], [6, 10]])

पिक-2

गुण:

  • मध्यबिंदु को मध्य रंग मिलता है।
  • ऊपरी और निचले पर्वतमाला एक ही रैखिक परिवर्तन द्वारा rescaled हैं।
  • केवल रंग जो चित्र पर दिखाई देता है, उसे रंग पट्टी में दिखाया गया है।
  • की vminतुलना में बड़ा होने पर भी ठीक काम करने लगता है midpoint(हालांकि सभी किनारे के मामलों का परीक्षण नहीं किया है)।

यह समाधान इस पृष्ठ से समान नाम वाले वर्ग से प्रेरित है


3
इसकी सादगी के कारण सर्वश्रेष्ठ उत्तर। यदि आप पहले से ही सुपर-विशेषज्ञ बनने की कोशिश कर रहे हैं, तो अन्य उत्तर सबसे अच्छे हैं। अधिकांश matplotlib उत्तर चाहने वाले केवल अपने कुत्ते और / या परिवार के घर जाने के लिए कुछ करने की कोशिश कर रहे हैं, और उनके लिए यह उत्तर सबसे अच्छा है।
sapo_cosmico

यह समाधान वास्तव में सबसे अच्छा लगता है, लेकिन काम नहीं करता है! मैंने केवल परीक्षण स्क्रिप्ट चलाई और परिणाम पूरी तरह से अलग है (केवल नीले वर्ग और कोई लाल सहित)। @icemtel, क्या आप कृपया जाँच कर सकते हैं? (पर खरोज के साथ समस्या के बगल में def __call__)
फिलिपे

ठीक है, मुझे समस्या मिली है: गणना में संख्याएँ normalized_minऔर normalized_maxपूर्णांक के रूप में ली गई हैं। बस उन्हें 0.0 के रूप में डाल दिया। इसके अलावा, आपके आंकड़े का सही आउटपुट पाने के लिए, मुझे इसका उपयोग करना था vals = sp.array([[-5.0, 0.0], [5.0, 10.0]]) । वैसे भी, उत्तर के लिए धन्यवाद!
फ़िलिप करें

हाय @ फीलिप मैं अपनी मशीन (पायथन 3.7, matplotlib 2.2.3, और मुझे लगता है कि नए संस्करणों पर भी ऐसा ही होना चाहिए) पर आपकी समस्या को फिर से प्रस्तुत नहीं कर सकता। आपके पास क्या संस्करण है? वैसे भी, मैंने फ्लोट प्रकार की सरणी बनाते हुए एक छोटा संपादन किया, और इंडेंटेशन समस्या को ठीक किया। इसे इंगित करने के लिए धन्यवाद
icemtel

हम्म .. मैं सिर्फ अजगर 3 के साथ कोशिश की और यह भी काम करता है। लेकिन मैं python2.7 का उपयोग कर रहा हूं। फिक्सिंग के लिए और जवाब देने के लिए धन्यवाद। यह उपयोग करने के लिए बहुत सरल है! :)
ipe

5

यकीन नहीं होता अगर आप अभी भी उत्तर की तलाश में हैं। मेरे लिए, उपवर्ग की कोशिश Normalizeअसफल रही। इसलिए मैंने मैन्युअल रूप से एक नया डेटा सेट, टिक और टिक-लेबल बनाने पर ध्यान केंद्रित किया, जिससे मुझे लगता है कि आप जिस उद्देश्य के लिए लक्ष्य बना रहे हैं, उसे प्राप्त करने के लिए।

मैंने ढूंढा scale माटप्लोटलिब में मॉड्यूल जिसमें क्लास प्लॉट को 'सिसलॉग' नियमों द्वारा लाइन प्लॉट बदलने के लिए इस्तेमाल किया जाता है, इसलिए मैं डेटा को बदलने के लिए उपयोग करता हूं। फिर मैं डेटा को स्केल करता हूं ताकि यह 0 से 1 हो जाए ( Normalizeआमतौर पर क्या करता है), लेकिन मैं सकारात्मक संख्याओं को नकारात्मक संख्याओं से अलग तरीके से मापता हूं। ऐसा इसलिए है क्योंकि आपका vmax और vmin समान नहीं हो सकता है, इसलिए .5 -> 1, नकारात्मक रेंज को .5 -> 0 की तुलना में एक बड़ी सकारात्मक सीमा को कवर कर सकता है। टिक और लेबल मूल्यों की गणना करने के लिए दिनचर्या बनाना मेरे लिए आसान था।

नीचे कोड और एक उदाहरण आंकड़ा है।

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 और लाइनथ्रेश = 1e-4

VMAXस्क्रिप्ट के शीर्ष पर "स्थिरांक" (जैसे ) को समायोजित करने के लिए स्वतंत्र महसूस करें ताकि यह पुष्टि हो सके कि यह अच्छी तरह से व्यवहार करता है।


आपके सुझाव के लिए धन्यवाद, जैसा कि नीचे देखा गया है, मुझे उपवर्ग में सफलता मिली। लेकिन टिकलैबल्स को सही बनाने के लिए आपका कोड अभी भी बहुत उपयोगी है।
tillsten

4

मैं पॉल एच से उत्कृष्ट उत्तर का उपयोग कर रहा था, लेकिन एक मुद्दे में भाग गया क्योंकि मेरे कुछ डेटा नकारात्मक से सकारात्मक तक थे, जबकि अन्य सेट 0 से सकारात्मक या नकारात्मक से 0 तक थे; या तो मामले में मैं 0 को सफेद रंग में रंगना चाहता था (कॉलमैप का मध्य बिंदु जो मैं उपयोग कर रहा हूं)। मौजूदा कार्यान्वयन के साथ, यदि आपका midpointमान 1 या 0 के बराबर है, तो मूल मैपिंग को ओवरराइट नहीं किया जा रहा है। आप निम्न चित्र में देख सकते हैं: संपादन से पहले रेखांकन तीसरा स्तंभ सही दिखता है, लेकिन 2 कॉलम में गहरा नीला क्षेत्र और शेष स्तंभों में गहरा लाल क्षेत्र सभी को सफेद होना चाहिए (उनके डेटा मान वास्तव में 0 हैं)। मेरे फिक्स का उपयोग करने से मुझे मिलता है: संपादन के बाद रेखांकन मेरा कार्य अनिवार्य रूप से पॉल एच से वैसा ही है, जैसे forलूप की शुरुआत में मेरे संपादन के साथ :

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

संपादित करें: मैं फिर से इसी तरह के मुद्दे पर भाग गया जब मेरा कुछ डेटा एक छोटे सकारात्मक मूल्य से बड़े सकारात्मक मूल्य तक पहुंच गया, जहां बहुत कम मान सफेद के बजाय लाल रंग के हो रहे थे। मैंने Edit #2ऊपर कोड में लाइन जोड़कर इसे ठीक किया ।


यह अच्छा लग रहा है, लेकिन ऐसा लगता है कि पॉल एच (और टिप्पणियों) के जवाब से तर्क बदल गए ... क्या आप अपने जवाब में एक उदाहरण कॉल जोड़ सकते हैं?
फिलीप

1

अगर आपको vmin, vmax, और शून्य के बीच के अनुपात में काम करने में कोई आपत्ति नहीं है, तो यह नीले रंग से सफेद से लाल तक का एक बहुत ही मूल रेखीय मानचित्र है, जो अनुपात के अनुसार सफेद सेट करता है 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 प्रारूप काफी सरल है: पंक्तियाँ उस ग्रेडिएंट में मौजूद बिंदु हैं जो निर्मित होती है: पहली प्रविष्टि x- मान है (0 से 1 के ग्रेडिएंट के साथ अनुपात), दूसरा पिछले खंड के लिए अंतिम मान है, और तीसरा अगले सेगमेंट के लिए स्टार्ट वैल्यू है - अगर आप स्मूथ ग्रेडिएंट चाहते हैं, तो बाद वाले दो हमेशा समान होते हैं। अधिक विवरण के लिए डॉक्स देखें


1
LinearSegmentedColormap.from_list()ट्यूपल्स के भीतर निर्दिष्ट करने (val,color)और उन्हें colorइस पद्धति के तर्क के लिए सूची के रूप में पास करने का विकल्प भी है val0=0<val1<...<valN==1
मोरिज़ियो

0

मुझे एक समान समस्या थी, लेकिन मैं चाहता था कि उच्चतम मूल्य पूर्ण लाल हो और नीले रंग के निचले मूल्यों को काट दिया जाए, जिससे यह अनिवार्य रूप से दिखता है जैसे कि कलरबार के नीचे का हिस्सा कटा हुआ था। इसने मेरे लिए काम किया (वैकल्पिक पारदर्शिता भी शामिल है):

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()
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.