Shifted colorbar matplotlib

Я пытаюсь сделать заполненный контур для набора данных. Это должно быть довольно просто:

plt.contourf(x, y, z, label = 'blah', cm = matplotlib.cm.RdBu)

Однако, что мне делать, если мой набор данных не симметричен относительно 0? Скажем, я хочу перейти от синего (отрицательные значения) к 0 (белый), к красному (положительные значения). Если мой набор данных идет от -8 до 3, то белая часть цветовой полосы, которая должна быть равна 0, на самом деле слегка отрицательна. Есть ли способ сдвинуть цветную панель?

Ответ 1

Во-первых, существует более одного способа сделать это.

  • Используйте colors kwarg для contourf и вручную укажите цвета
  • Используйте собственный Normalize класс и передайте экземпляр в качестве norm kwarg.
  • Используйте дискретную цветовую карту, созданную с помощью matplotlib.colors.from_levels_and_colors.

Самый простой способ - передать определенные цвета с помощью colors=sequence_of_colors. Однако, если вы не вручную устанавливаете количество контуров, это может быть неудобно.

Наиболее гибким способом является второй вариант: используйте norm kwarg, чтобы указать обычную нормализацию. Для чего вы хотите, вам нужно подклассом Normalize, но это не так сложно сделать. Это единственный вариант, который позволяет использовать непрерывную цветокоррекцию.

Причиной использования второго или третьего варианта является то, что они будут работать для любого типа графика matplotlib, который использует цветовой код (например, imshow, scatter и т.д.).

Третий вариант создает дискретный цветовой код и объект нормализации из определенных цветов. Он в основном идентичен первому варианту, но он будет: а) работать с другими типами графиков, чем контурные графики, и б) избегать необходимости вручную указывать количество контуров.

В качестве примера второго варианта (я буду использовать imshow здесь, потому что он имеет больше смысла, чем contourf для случайных данных, но contourf будет иметь идентичное использование, отличное от опции interpolation.):

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize

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

    def __call__(self, value, clip=None):
        # I'm ignoring masked values and all kinds of edge cases to make a
        # simple example...
        x, y = [self.vmin, self.midpoint, self.vmax], [0, 0.5, 1]
        return np.ma.masked_array(np.interp(value, x, y))

data = np.random.random((10,10))
data = 10 * (data - 0.8)

fig, ax = plt.subplots()
norm = MidpointNormalize(midpoint=0)
im = ax.imshow(data, norm=norm, cmap=plt.cm.seismic, interpolation='none')
fig.colorbar(im)
plt.show()

enter image description here

В качестве примера третьего варианта (обратите внимание, что это дает дискретную цветовую палитру вместо непрерывной цветовой карты):

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import from_levels_and_colors

data = np.random.random((10,10))
data = 10 * (data - 0.8)

num_levels = 20
vmin, vmax = data.min(), data.max()
midpoint = 0
levels = np.linspace(vmin, vmax, num_levels)
midp = np.mean(np.c_[levels[:-1], levels[1:]], axis=1)
vals = np.interp(midp, [vmin, midpoint, vmax], [0, 0.5, 1])
colors = plt.cm.seismic(vals)
cmap, norm = from_levels_and_colors(levels, colors)

fig, ax = plt.subplots()
im = ax.imshow(data, cmap=cmap, norm=norm, interpolation='none')
fig.colorbar(im)
plt.show()

enter image description here