Создание цветовой полосы сдвигает два ранее выровненных объекта оси относительно друг друга - Matplotlib

В моей проблеме «реального мира» я хочу пересчитать значения x y, записанные в галочке моей фигуры после того, как я увеличил ее таким образом, чтобы начало координат всегда находилось в (0,0) и, очевидно, относительные расстояния значения по осям x и y остаются прежними. Моя проблема была решена в этой теме: Исходное решение

Решение включает в себя создание одной невидимой оси, которая содержит график, и одной видимой оси, которая получает разные метки деления после масштабирования.

В моем случае я хочу наложить несколько графиков countor и coutourf. Только для одного из этих графиков я хочу добавить цветную полосу к рисунку! Но когда я создаю цветовую полосу в своем скрипте, два объекта осей, которые я создал, смещаются друг относительно друга. Без цветной полосы они идеально выровнены!

Вот MWE, который примерно воссоздает поведение:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib import mlab, cm

# Default delta is large because that makes it fast, and it illustrates
# the correct registration between image and contours.
delta = 0.5

extent = (-3, 4, -4, 3)

x = np.arange(-3.0, 4.001, delta)
y = np.arange(-4.0, 3.001, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = (Z1 - Z2) * 10

levels = np.arange(-2.0, 1.601, 0.4)  # Boost the upper limit to avoid truncation errors.

norm = cm.colors.Normalize(vmax=abs(Z).max(), vmin=-abs(Z).max())
cmap = cm.PRGn




# ax is empty
fig, ax = plt.subplots()
ax.set_navigate(False)
# ax2 will hold the plot, but has invisible labels
ax2 = fig.add_subplot(111,zorder=2)

ax2.contourf(X, Y, Z, levels,
                 cmap=cm.get_cmap(cmap, len(levels) - 1),
                 norm=norm,
                 )
ax2.axis("off")

ax.set_xlim(ax2.get_xlim())
ax.set_ylim(ax2.get_ylim())

#
# Declare and register callbacks
def on_lims_change(axes):
    # change limits of ax, when ax2 limits are changed.
    a=ax2.get_xlim()
    ax.set_xlim(0, a[1]-a[0])
    a=ax2.get_ylim()
    ax.set_ylim(0, a[1]-a[0])



sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm ) #Do not show unnecessary parts of the colormap
sm._A = []
cb = plt.colorbar(sm,extend="both", label="units")
cb.ax.tick_params(labelsize=10)


ax2.callbacks.connect('xlim_changed', on_lims_change)
ax2.callbacks.connect('ylim_changed', on_lims_change)
ax.axis('scaled')
plt.axis('scaled')
# Show
plt.show()

Теперь контурная диаграмма кажется сдвинутой относительно видимой оси. Я нашел несколько подсказок в Интернете, которые предполагают, что «поле с цветной полосой автоматически съедает пространство от осей, к которым оно прикреплено» вторая ось x">Ссылка1 Ссылка2

Но я действительно не знаю, что мне нужно сделать, чтобы изменить это поведение, и я не понимаю, связана ли моя проблема.

Обратите внимание, что часть:

ax.axis('scaled')
plt.axis('scaled')

необходимо, так как мне нужно сохранить соотношение сторон точно так же, как в наборе данных!

Заранее спасибо!


person NorrinRadd    schedule 21.10.2017    source источник


Ответы (1)


Вы можете изменить положение ax (пустые оси с метками) на положение ax2 (оси, отображающие данные) после добавления цветовой полосы через

ax.set_position(ax2.get_position())

В качестве альтернативы создайте цветовую полосу, «улучшив» пространство по обеим осям,

cb = fig.colorbar(sm,ax=[ax,ax2], extend="both", label="units")

Оба решения можно найти в ответах на этот связанный вопрос.


Ниже приведены некоторые дополнительные улучшения, выходящие за рамки вопроса:

ax.axis('scaled')
ax2.axis('scaled') 

Кроме того, поместите ax сверху, если ax2, чтобы график контура не перекрывал шипы осей.

# put `ax` on top, to let the contours not overlap the shown axes
ax.set_zorder(2)     
ax.patch.set_visible(False)
# ax2 will hold the plot, but has invisible labels
ax2 = fig.add_subplot(111,zorder=1)

Полный код:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib import mlab, cm

delta = 0.5
extent = (-3, 4, -4, 3)
x = np.arange(-3.0, 4.001, delta)
y = np.arange(-4.0, 3.001, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = (Z1 - Z2) * 10

levels = np.arange(-2.0, 1.601, 0.4) 

norm = cm.colors.Normalize(vmax=abs(Z).max(), vmin=-abs(Z).max())
cmap = cm.PRGn

# ax is empty
fig, ax = plt.subplots()
ax.set_navigate(False)
 # put `ax` on top, to let the contours not overlap the shown axes
ax.set_zorder(2)     
ax.patch.set_visible(False)
# ax2 will hold the plot, but has invisible labels
ax2 = fig.add_subplot(111,zorder=1)

ax2.contourf(X, Y, Z, levels,
                 cmap=cm.get_cmap(cmap, len(levels) - 1),
                 norm=norm,
                 )
ax2.axis("off")

ax.set_xlim(ax2.get_xlim())
ax.set_ylim(ax2.get_ylim())

#
# Declare and register callbacks
def on_lims_change(axes):
    # change limits of ax, when ax2 limits are changed.
    a=ax2.get_xlim()
    ax.set_xlim(0, a[1]-a[0])
    a=ax2.get_ylim()
    ax.set_ylim(0, a[1]-a[0])


sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm )
sm._A = []
cb = fig.colorbar(sm,ax=[ax,ax2], extend="both", label="units")
cb.ax.tick_params(labelsize=10)

ax2.callbacks.connect('xlim_changed', on_lims_change)
ax2.callbacks.connect('ylim_changed', on_lims_change)
ax.axis('scaled')
ax2.axis('scaled')
#ax.set_position(ax2.get_position())
# Show
plt.show()

введите здесь описание изображения

person ImportanceOfBeingErnest    schedule 21.10.2017
comment
Я безуспешно пытался реализовать решение в ветке, что подчеркивает мое непонимание. Проблема, с которой я сталкиваюсь сейчас, заключается в том, что если я хочу добавить matplotlib.patches вот так circle = Circle((-3, 2), 0.1, color='r') ax2.add_patch(circle) . Когда патч добавляется к краю графика, отображаемый диапазон X-Y изменяется, чтобы показать полный круг и сдвиг координат. Это связано с вызовом ax.axis('scaled') ax2.axis('scaled').... Я был бы признателен за подсказку, чтобы посмотреть, так как я ничего не могу найти, когда ищу его. Спасибо за ваше решение! Это действительно ценится! - person NorrinRadd; 23.10.2017
comment
Вам нужно добавить кружок (или что угодно) перед вызовом ax.set_xlim(ax2.get_xlim()). - person ImportanceOfBeingErnest; 23.10.2017
comment
Я знаю, что это сообщество немного ограничено в преувеличенных выражениях благодарности ... но я, честно говоря, хотел бы купить вам пива или что-то в этом роде .... Это решило очень насущную проблему для меня! Благодарю вас! - person NorrinRadd; 23.10.2017