Как установить 0 в белый цвет при неравномерной цветовой гамме? - PullRequest
1 голос
/ 08 января 2020

У меня неравномерная цветовая шкала, и я хочу, чтобы 0 был белым. Все отрицательные цвета должны быть синего цвета sh, а все положительные цвета должны быть красного цвета sh. Моя текущая попытка отображает 0 синих sh и 0,7 белых.

Есть ли способ установить 0 на белый?

import numpy as np
import matplotlib.colors as colors 
from matplotlib import pyplot as m

bounds_min = np.arange(-2, 0, 0.1)
bounds_max = np.arange(0, 4.1, 0.1)
bounds = np.concatenate((bounds_min, bounds_max), axis=None)       
norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256)      # I found this on the internet and thought this would solve my problem. But it doesn't...
m.pcolormesh(xx, yy, interpolated_grid_values, norm=norm, cmap='RdBu_r')

Ответы [ 2 ]

1 голос
/ 08 января 2020

Чтобы получить желаемую норму, установите то же количество цветов на отрицательной стороне, что и на положительной стороне.

В качестве альтернативы, вы можете использовать неизмененную норму и создать специальную цветовую карту. Такая цветовая карта будет иметь 1/3 rd сине-белых цветов и 2/3 rd бело-красных цветов. Преимущество было бы в том, что цветовая панель выглядит лучше. Такой подход работает, только если баланс между отрицательными и положительными числами не слишком велик.

Вот демонстрационный код с сгенерированными данными. zz выбирается как синус, повернутый вокруг центра, и масштабируется до go от -2 до 4, поэтому симметрично c вокруг 1. Слева изображение показано с измененной картой цветов. Справа норма изменяется, чтобы заставить белый в нуле.

Из-за красной окраски всех положительных значений красные полосы шире, чем синие. В изображении без изменения норм или цветовых карт полосы будут иметь равную ширину. Цветные полосы показывают, что ноль является белым.

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

x = np.linspace(-20, 20, 500)
y = np.linspace(-20, 20, 500)
xx, yy = np.meshgrid(x, y)
zz = np.sin(np.sqrt(xx * xx + yy * yy)) * 3 + 1

negatives = -2.0
positives = 4.0

bounds_min = np.linspace(negatives, 0, 129)
bounds_max = np.linspace(0, positives, 129)[1:]
    # the zero is only needed once
    # in total there will be 257 bounds, so 256 bins
bounds = np.concatenate((bounds_min, bounds_max), axis=None)
norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256)

num_neg_colors = int(256 / (positives - negatives) * (-negatives))
num_pos_colors = 256 - num_neg_colors
cmap_BuRd = plt.cm.RdBu_r
colors_2neg_4pos = [cmap_BuRd(0.5*c/num_neg_colors) for c in range(num_neg_colors)] +\
                   [cmap_BuRd(1-0.5*c/num_pos_colors) for c in range(num_pos_colors)][::-1]
cmap_2neg_4pos = colors.LinearSegmentedColormap.from_list('cmap_2neg_4pos', colors_2neg_4pos, N=256)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))

mesh1 = ax1.pcolormesh(xx, yy, zz, cmap=cmap_2neg_4pos)
ax1.set_aspect('equal')
ax1.set_title('using a modified cmap')
fig.colorbar(mesh1, ax=ax1)

mesh2 = ax2.pcolormesh(xx, yy, zz, norm=norm, cmap='RdBu_r')
ax2.set_aspect('equal')
ax2.set_title('using a special norm')
ticks = np.append(np.arange(-2.0, 0, 0.25), np.arange(0, 4.001, 0.5))
fig.colorbar(mesh2, ax=ax2, ticks=ticks)

plt.show()

example plot

Следующий код отображает норму, которая выглядит как пошаговая функция. Только с 257 границами эта пошаговая функция везде имеет правильную форму (увеличение до x при -2, 0 и 4).

nx = np.linspace(-3,5,10000)
plt.plot(nx, norm(nx))

PS: существует альтернативный метод для создания похожая цветовая карта. Но, пробуя это, становится ясно, что цветовая карта RdBu точно настроена и дает намного лучше выглядящие графики.

norm_2neg_4pos = mcolors.Normalize(negatives, positives)
colors_2neg_4pos = [[0, 'blue'],
                    [norm_2neg_4pos(0.0), "white"],
                    [1, 'red']]
cmap_2neg_4pos = mcolors.LinearSegmentedColormap.from_list("", colors_2neg_4pos)

Еще одно простое решение - масштабирование всего между -4 и 4. Однако это потерял бы более темный блюз. Альтернативой 'RdBu_r' является 'seismi c' с другим способом перехода с красного на белый на синий.

ax.pcolormesh(xx, yy, zz, vmin=-positives, vmax=positives, cmap='RdBu_r')
0 голосов
/ 10 января 2020

Другой ответ делает его немного сложнее, чем нужно. Чтобы средняя точка цветовой карты была равна 0, используйте DivergingNorm с vcenter=0.

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

x, y = np.meshgrid(np.linspace(0,50,51), np.linspace(0,50,51))
z = np.linspace(-2,4,50*50).reshape(50,50)

norm = DivergingNorm(vmin=z.min(), vcenter=0, vmax=z.max())
pc = plt.pcolormesh(x,y,z, norm=norm, cmap="RdBu_r")
plt.colorbar(pc)

plt.show()

enter image description here

Примечание: Начиная с версии matplotlib 3.2 DivergingNorm будет переименовано в TwoSlopeNorm

...