Вырезать диапазон и данные маски при определении средней точки карты цветов в matplotlib

Я хочу построить 2D-данные в небольшом скрипте и позволить пользователю выбрать vmin/vamx (я называю их порогом и порогом) значений в наборе данных для построения. В идеале я хочу, чтобы все значения ниже/выше vmin/vamx были прозрачными. Средняя точка палитры всегда должна быть на 0 и быть белой!. Я хотел бы иметь как можно больше динамического диапазона. Цвет должен варьироваться от синего для отрицательного до белого при 0 и красного для положительных значений! Пороговое значение и число_контуров устанавливаются пользователем, и масштаб должен быть адаптирован соответствующим образом.

Я нашел несколько советов по различным формам, но не смог заставить их работать на меня. Вот что я нашел до сих пор:

Если я использую решение, предоставленное в третьей ссылке пользователем Paul H, я получаю сообщение об ошибке r, g, b, a = cmap(ri) TypeError: 'str' object is not callable независимо от того, что я делать. Я попытался использовать решение midpointnorm, которое также можно найти по ссылкам выше. Вот небольшой пример:

    from numpy import*
    import matplotlib
    import numpy as np
    import matplotlib.pyplot as plt
    from matplotlib import cm
    import pylab
    from matplotlib.colors import Normalize
    # class to norm a color sacle so the midpoint is ecatcly in the middle of the colormap . This is usefull if a colormap goes form color1 to white to color2
    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 * (val - 0.5)
                if val < 0: 
                    return  val*abs(vmin-midpoint) + midpoint
                else:
                    return  val*abs(vmax-midpoint) + midpoint
    # Make some illustrative fake data:

    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) * 10
    # Make the color i want to use


    cdict3 = {'red':  ((0.0, 0.0, 0.0),
                       (0.25, 0.0, 0.0),
                       (0.5, 0.8, 1.0),
                       (0.75, 1.0, 1.0),
                       (1.0, 0.4, 1.0)),

             'green': ((0.0, 0.0, 0.0),
                       (0.25, 0.0, 0.0),
                       (0.5, 0.9, 0.9),
                       (0.75, 0.0, 0.0),
                       (1.0, 0.0, 0.0)),

             'blue':  ((0.0, 0.0, 0.4),
                       (0.25, 1.0, 1.0),
                       (0.5, 1.0, 0.8),
                       (0.75, 0.0, 0.0),
                       (1.0, 0.0, 0.0))
            }

    # Make a modified version of cdict3 with some transparency
    # in the middle of the range.
    cdict4 = cdict3.copy()
    cdict4['alpha'] = ((0.0, 1.0, 1.0),
                    #   (0.25,1.0, 1.0),
                       (0.5, 0.3, 0.3),
                    #   (0.75,1.0, 1.0),
                       (1.0, 1.0, 1.0))


    #########################################MY questions start here###########################################                
                       
    threshold=10        # i only want to plot data in the range [-threshold,threshold]     
    number_of_contours=100  

    ##Instead of just giving a number of contours i thought about giving a predefined list wich contains that speciefies the point of the contours.
    ##The poinst denisty should be heigher at the Ends of the intervall as the data at theses ends is of higher interest for me. The total number of points should be variable.
    #levels = [-threshold, -0.005, -0.001, 0, 0.001,0.005, threshold]                  



    norm = MidPointNorm(midpoint=0,vmin=-threshold, vmax=threshold) # norm the colormap                
                   
                       
                       
    plt.register_cmap(name='BlueRedAlpha', data=cdict4)


    im4 = pylab.contourf(x,y,Z,number_of_contours, cmap='BlueRedAlpha',interpolation='nearest',norm=norm)
    #im4 = pylab.contourf(x,y,Z,number_of_contours, cmap='BlueRedAlpha',interpolation='nearest',vmin=-threshold,vmax=threshold,norm=norm)# is it enough to give the norm parameter after the normalisation or do i have to give vmin/vmas to be sure?
    sm = plt.cm.ScalarMappable(cmap='BlueRedAlpha', norm=plt.Normalize(vmin=-threshold, vmax=threshold)) 
    sm._A = []

    plt.colorbar(sm,extend="both")





    plt.show()
                

Это как-то работает сейчас. Я не понимаю код в классе midpointnorm. Достаточно ли передать параметр norm в containerf после нормализации или мне нужно указать значения vmin/vmax, чтобы быть уверенным, что отображаются только значения между vmin и vmax, и чтобы быть уверенным? Игнорируются ли vmin и vmax, если я указываю параметр нормы?

Но я хотел бы изменить динамический диапазон таким образом, чтобы цветовая шкала не была насыщенной для значений vmin и vmax.

Что еще более важно, на данный момент я отображаю в коде значения от -10 до 10. В моих данных я отображаю значения от -0,03 до 0,03, и все, что близко к этим значениям, уже насыщено, поскольку оно находится в верхней части динамического диапазона. Но этот регион представляет для меня большой интерес. Мне нужна цветовая шкала, которая идет от синего (отрицательного) до красного (положительного). Я хотел бы иметь некоторый контроль над поведением контурных линий.

Я знаю, что вы можете дать список контурных линий для контура. Есть ли у кого-нибудь идея, как заполнить список длиной (number_of_contours+1) в диапазоне от значений от -threshold до +threshold, где расстояние между элементами ближе к положительному и отрицательному концу диапазона? У вас есть другая идея?


person NorrinRadd    schedule 10.05.2017    source источник
comment
Можете ли вы рассказать нам, почему Solution1 не помог? У меня возникло бы желание использовать именно такое решение.   -  person ImportanceOfBeingErnest    schedule 10.05.2017
comment
Можете ли вы добавить комментарии в вопрос и удалить их потом? У меня проблемы с пониманием проблемы динамического диапазона. Не могли бы вы показать минимально воспроизводимый пример с использованием решения MidpointNormalize (просто придумайте некоторые данные), из которого вы можете объясните что не так.   -  person ImportanceOfBeingErnest    schedule 10.05.2017
comment
Привет, я попытался отредактировать свой пост соответствующим образом. В этом случае сложно сделать минимальный рабочий пример. Я скопировал класс MidPointNorm и отметил, где начинается мой код. Я попытался добавить комментарии, чтобы выделить то, что я хочу сделать.   -  person NorrinRadd    schedule 10.05.2017
comment
Извините, без работающего кода я не вижу проблемы и поэтому не могу вам помочь.   -  person ImportanceOfBeingErnest    schedule 11.05.2017
comment
Уважаемый пользователь :ImportanceOfBeingErnest, вы были абсолютно правы, мой вопрос был задан неправильно. Я попытался решить несколько проблем самостоятельно, изменил вопрос и включил MWE, даже если искусственные данные не являются хорошим представлением того, что я хочу построить.   -  person NorrinRadd    schedule 17.05.2017