Можно ли размещать метки и метки за пределами осей? - PullRequest
0 голосов
/ 25 мая 2020

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

Я хотел бы предоставить шкалу в виде позвоночника оси для первого профиля (см. Прилагаемый код и рисунок).

Есть ли способ разместить отметки и метки за пределами оси?

fig, ax = plt.subplots()
y = np.linspace(0, 10, 100)
x = 10 * np.sin(y)
x_offsets = np.linspace(0, 100, 20)
for offset in x_offsets: 
    if offset == 0:
        color = 'tab:blue'
        ax.axvline(0, color=color, ls='dotted', lw=0.5)
    else:
        color = 'k'

    ax.plot(x + offset, y, color, clip_on=False)

ax.spines['left'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)

major_ticks = np.linspace(x.min(), x.max(), 5)
minor_ticks = np.linspace(x.min(), x.max(), 9)
ax.set_xticks(major_ticks)
ax.set_xticks(minor_ticks, True)
ax.spines['top'].set_bounds(major_ticks[0], major_ticks[-1])
ax.spines['top'].set_color('tab:blue')
ax.xaxis.tick_top()
ax.tick_params('x', which='both', color='tab:blue', labelcolor='tab:blue')
ax.set_xlabel('x label', position=(0, -0.1), color='tab:blue')
ax.xaxis.set_label_position('top')

# ax.tick_params('x', which='both', bottom=False, top=False, labelbottom=False)
ax.tick_params('y', which='both', left=False, right=False, labelleft=False)

ax.axis((0, 100, 0, 11))

enter image description here

Ответы [ 3 ]

0 голосов
/ 26 мая 2020

Вот еще один ответ, который, я надеюсь, удовлетворит ваше требование. Соберите все соответствующие отметки и метки и добавьте их к осям (опять же?):

import matplotlib.pyplot as plt
import numpy as np


fig, ax = plt.subplots()
y = np.linspace(0, 10, 100)
x = 10 * np.sin(y)
x_offsets = np.linspace(0, 100, 20)
for offset in x_offsets: 
    if offset == 0:
        color = 'tab:blue'
        ax.axvline(0, color=color, ls='dotted', lw=0.5)
    else:
        color = 'k'

    ax.plot(x + offset, y, color, clip_on=False)

ax.spines['left'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
major_ticks = np.linspace(x.min(), x.max(), 5)
minor_ticks = np.linspace(x.min(), x.max(), 9)
ax.set_xticks(major_ticks)
ax.set_xticks(minor_ticks, True)
ax.spines['top'].set_bounds(major_ticks[0], major_ticks[-1])
ax.spines['top'].set_color('tab:blue')
ax.xaxis.tick_top()
ax.tick_params('x', which='both', color='tab:blue', labelcolor='tab:blue')
ax.set_xlabel('x label', position=(0, -0.1), color='tab:blue')
ax.xaxis.set_label_position('top')
ax.tick_params('y', which='both', left=False, right=False, labelleft=False)

ax.axis((0, 100, 0, 11))
ticks = ax.get_xticklines()
mticks = ax.get_xaxis().get_minor_ticks()
labels = ax.get_xticklabels()
for artist in [*ticks, *mticks, *labels]:
    if artist.get_visible():
        print(artist.axes)
        ax.add_artist(artist)
        artist.set_clip_on(False)

fig.show()

enter image description here

Мне очень любопытно, что:

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

Таким образом, я не могу придумать способ, как добавить только художников, которые на самом деле не видны, но должны быть на самом деле, кроме как смотреть на их положение по оси X.

0 голосов
/ 27 мая 2020

Решением было использовать смешанное преобразование для добавления отдельных осей для самого левого (или любого) профиля:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.transforms import blended_transform_factory

# make some sample data
dx = dy = 1
y = np.arange(80, 0 - dy, -dy)
x = np.arange(0, 100 + dx, dx)
x_offsets = np.linspace(0, 100, 11)

xx, yy = np.meshgrid(0.05 * (x + 10), 0.1 * (y - 40))
data1 = np.exp(-xx**2 - yy**2) - np.exp(-(xx - 1)**2 - (yy - 1)**2)

xx, yy = np.meshgrid(0.05 * (x - 90), 0.1 * (y - 40))
data2 = np.exp(-xx**2 - yy**2) - np.exp(-(xx - 1)**2 - (yy - 1)**2)

data = data1 + data2
data += np.random.rand(data.shape[0], data.shape[1]) * 0.5 * data

extent = (x[0] - 0.5 * dx, x[-1] + 0.5 * dx, y[-1] - 0.5 * dy, y[0] + 0.5 * dy)

# set up the plot
fig, ax = plt.subplots(
    2, 2, sharey=True, figsize=(8, 4),
    gridspec_kw=dict(width_ratios=(0.2, 1), wspace=0.1)
)
axTL = ax[0, 0]
axTR = ax[0, 1]
axBL = ax[1, 0]
axBR = ax[1, 1]

trans = blended_transform_factory(axTR.transData, axTR.transAxes)

data_abs_max = np.abs(data).max()
im = axBR.imshow(data, 'RdBu_r', vmin=-data_abs_max, vmax=data_abs_max,
                 extent=extent, aspect='auto', interpolation='bilinear')
axBR.axis(extent)

axBL.plot(data.sum(axis=1), y, 'k')

scale = 8
for offset in x_offsets:
    profile = data[:, int(offset / dx)]
    profile = scale * profile
    xmin, xmax = profile.min(), profile.max()

    if offset == 0:
        bounds = (offset + xmin, 0, xmax - xmin, 1)
        inset_ax = axTR.inset_axes(bounds, transform=trans)
        inset_ax.set_ylim(axTR.get_ylim())
        inset_ax.set_xlim(xmin, xmax)

        color = 'tab:blue'
        inset_ax.axvline(0, color=color, ls='dotted', lw=0.5)
        inset_ax.plot(profile, y, color, clip_on=False, zorder=1)
        inset_ax.set_facecolor('none')

        inset_ax.spines['left'].set_visible(False)
        inset_ax.spines['bottom'].set_visible(False)
        inset_ax.spines['right'].set_visible(False)

        inset_ax.spines['top'].set_color('tab:blue')
        inset_ax.tick_params(
            'both', which='both',
            top=True, left=False, right=False, bottom=False,
            labeltop=True, labelleft=False,
            color='tab:blue', labelcolor='tab:blue'
        )
        inset_ax.set_xlabel('x label', color='tab:blue')
        inset_ax.xaxis.set_label_position('top')
        inset_ax.xaxis.tick_top() 
    else:
        color = 'k'

        axTR.plot(profile + offset, y, color, clip_on=False, zorder=0)

# remove unwanted spines and ticks
axTR.axis('off')

axTL.spines['top'].set_visible(False)
axTL.spines['right'].set_visible(False)
axTL.spines['bottom'].set_visible(False)
axTL.tick_params('both', which='both', top=False, right=False, bottom=False,
                 labelbottom=False)

axBR.tick_params('both', which='both', labelleft=False)

axTR.axis(extent)

enter image description here

0 голосов
/ 25 мая 2020

Хорошо, есть очень простое решение этой проблемы, однако, к сожалению, я не могу объяснить, почему это работает. Все, что вам нужно сделать, это поместить перемещение осей в начало, а не в конец:

import matplotlib.pyplot as plt
import numpy as np


fig, ax = plt.subplots()
ax.axis((0, 100, 0, 11))  # just move this line here
y = np.linspace(0, 10, 100)
x = 10 * np.sin(y)
x_offsets = np.linspace(0, 100, 20)
for offset in x_offsets: 
    if offset == 0:
        color = 'tab:blue'
        ax.axvline(0, color=color, ls='dotted', lw=0.5)
    else:
        color = 'k'

    ax.plot(x + offset, y, color, clip_on=False)

ax.spines['left'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_visible(False)
xticks = ax.get_xticklines()
for tick in xticks:
    tick.set_clip_on(False)

major_ticks = np.linspace(x.min(), x.max(), 5)
minor_ticks = np.linspace(x.min(), x.max(), 9)
ax.set_xticks(major_ticks)
ax.set_xticks(minor_ticks, True)
ax.spines['top'].set_bounds(major_ticks[0], major_ticks[-1])
ax.spines['top'].set_color('tab:blue')
ax.xaxis.tick_top()
ax.tick_params('x', which='both', color='tab:blue', labelcolor='tab:blue')
ax.set_xlabel('x label', position=(0.12, 0), color='tab:blue')
ax.xaxis.set_label_position('top')

# ax.tick_params('x', which='both', bottom=False, top=False, labelbottom=False)
ax.tick_params('y', which='both', left=False, right=False, labelleft=False)

xticks = ax.get_xticks()
axtrans = (ax.transData + ax.transAxes.inverted()).transform
figtrans = (ax.transData + fig.transFigure.inverted()).transform
for xtick in xticks:
    print(axtrans((0, xtick)), figtrans((0, xtick)))

fig.show()

enter image description here Что любопытно, если мы верим данные преобразования печатаются в конце, некоторые метки (-labels) не только расположены за пределами оси, но даже за пределами фигуры, хотя мы можем ясно видеть, что они все еще находятся внутри фигуры. Я не уверен, что с этим делать, тем более, что те же метки (-labels) также находятся снаружи (хотя и с разными значениями), когда перемещение осей выполняется в конце. Было бы интересно, если бы кто-нибудь более знающий объяснил, что происходит.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...