Совместное масштабирование осей вспомогательных участков различного размера (без разделения осей) - PullRequest
0 голосов
/ 04 января 2019

С помощью matplotlib я хочу построить два графика с одинаковым масштабом оси X, но я хочу показать сечения разных размеров. Как мне это сделать?

До сих пор я мог строить субплоты разного размера с помощью GridSpec или тех же размеров, которые имеют общую ось X. Когда я пробую оба сразу, меньший подплот имеет ту же ось, но меньшую масштабированную, в то время как я хочу одинаковое масштабирование и другую ось, поэтому совместное использование оси может быть неправильной идеей.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

x=np.linspace(0,10,100)
y=np.sin(x)

x2=np.linspace(0,5,60)
y2=np.cos(x2)

fig=plt.figure()

gs=GridSpec(2,3)

ax1 = fig.add_subplot(gs[0, :])
ax1.plot(x,y)

ax2 = fig.add_subplot(gs[1,:-1])
    #using sharex=ax1 here decreases the scaling of ax2 too much
ax2.plot(x2,y2)

plt.show()    

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

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

Ответы [ 2 ]

0 голосов
/ 04 января 2019

Я предлагаю установить пределы второй оси на основе пределов ax1.

Попробуйте это!

ax2 = fig.add_subplot(gs[1,:-1])
ax2.plot(x2,y2)
lb, ub = ax1.get_xlim()
# Default margin is 0.05, which would be used for auto-scaling, hence reduce that here
# Set lower bound and upper bound based on the grid size, which you choose for second plot
ax2.set_xlim(lb, ub *(2/3) -0.5)

plt.show()   

enter image description here

0 голосов
/ 04 января 2019

Это все еще немного грубо. Я уверен, что есть немного более элегантный способ сделать это, но вы можете создать пользовательский transformation (см. Руководство по трансформациям ) между координатами осей ax2 и координатами данных ax1 , Другими словами, вы вычисляете, каково значение данных (согласно ax1) в позиции, соответствующей левому и правому краям ax2, и затем корректируйте xlim из ax2 соответственно.

Вот демонстрация, показывающая, что он работает, даже если второй подзаговор никак не выровнен с первым.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

x=np.linspace(0,25,100)
y=np.sin(x)

x2=np.linspace(10,30,60)
y2=np.cos(x2)

fig=plt.figure()

gs=GridSpec(2,6)

ax1 = fig.add_subplot(gs[0, :])
ax1.plot(x,y)

ax2 = fig.add_subplot(gs[1,3:-1])
ax2.plot(x2,y2)

# here is where the magic happens
trans = ax2.transAxes + ax1.transData.inverted()
((xmin,_),(xmax,_)) = trans.transform([[0,1],[1,1]])
ax2.set_xlim(xmin,xmax)

# for demonstration, show that the vertical lines end up aligned
for ax in [ax1,ax2]:
    for pos in [15,20]:
        ax.axvline(pos)

plt.show()

enter image description here

РЕДАКТИРОВАТЬ : Одним из возможных уточнений было бы преобразование в обратном вызове xlim_changed . Таким образом, оси остаются синхронизированными даже при масштабировании / панорамировании по первым осям.

Существует также небольшая проблема с tight_layout(), как вы заметили, но это легко исправить, вызвав функцию обратного вызова напрямую.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec


def on_xlim_changed(event):
    # here is where the magic happens
    trans = ax2.transAxes + ax1.transData.inverted()
    ((xmin, _), (xmax, _)) = trans.transform([[0, 1], [1, 1]])
    ax2.set_xlim(xmin, xmax)


x = np.linspace(0, 25, 100)
y = np.sin(x)

x2 = np.linspace(10, 30, 60)
y2 = np.cos(x2)

fig = plt.figure()


gs = GridSpec(2, 6)

ax1 = fig.add_subplot(gs[0, :])
ax1.plot(x, y)

ax2 = fig.add_subplot(gs[1, 3:-1])
ax2.plot(x2, y2)

# for demonstration, show that the vertical lines end up aligned
for ax in [ax1, ax2]:
    for pos in [15, 20]:
        ax.axvline(pos)

# tight_layout() messes up the axes xlim
# but can be fixed by calling on_xlim_changed()
fig.tight_layout()
on_xlim_changed(None)

ax1.callbacks.connect('xlim_changed', on_xlim_changed)


plt.show()
...