matplotlib: чи можу я створити об'єкти AxesSubplot, а потім додати їх до екземпляра Figure?


79

Переглядаючи matplotlibдокументацію, здається стандартним способом додати AxesSubplota Figureдо використання Figure.add_subplot:

from matplotlib import pyplot

fig = pyplot.figure()
ax = fig.add_subplot(1,1,1)
ax.hist( some params .... )

Я хотів би мати можливість створювати AxesSubPlotподібні об’єкти незалежно від фігури, щоб я міг використовувати їх у різних фігурах. Щось на зразок

fig = pyplot.figure()
histoA = some_axes_subplot_maker.hist( some params ..... )
histoA = some_axes_subplot_maker.hist( some other params ..... )
# make one figure with both plots
fig.add_subaxes(histo1, 211)
fig.add_subaxes(histo1, 212)
fig2 = pyplot.figure()
# make a figure with the first plot only
fig2.add_subaxes(histo1, 111)

Чи можливо це в, matplotlibі якщо так, як я можу це зробити?

Оновлення: Мені не вдалося розв’язати створення Осей та Фігур, але, наводячи приклади у відповідях нижче, можна легко повторно використати раніше створені осі в нових примірниках або olf. Це можна проілюструвати простою функцією:

def plot_axes(ax, fig=None, geometry=(1,1,1)):
    if fig is None:
        fig = plt.figure()
    if ax.get_geometry() != geometry :
        ax.change_geometry(*geometry)
    ax = fig.axes.append(ax)
    return fig

Здається, ваша функція plot_axes вже не працює.
Джим

Відповіді:


40

Зазвичай ви просто передаєте екземпляр осей функції.

Наприклад:

import matplotlib.pyplot as plt
import numpy as np

def main():
    x = np.linspace(0, 6 * np.pi, 100)

    fig1, (ax1, ax2) = plt.subplots(nrows=2)
    plot(x, np.sin(x), ax1)
    plot(x, np.random.random(100), ax2)

    fig2 = plt.figure()
    plot(x, np.cos(x))

    plt.show()

def plot(x, y, ax=None):
    if ax is None:
        ax = plt.gca()
    line, = ax.plot(x, y, 'go')
    ax.set_ylabel('Yabba dabba do!')
    return line

if __name__ == '__main__':
    main()

Щоб відповісти на ваше запитання, ви завжди можете зробити щось подібне:

def subplot(data, fig=None, index=111):
    if fig is None:
        fig = plt.figure()
    ax = fig.add_subplot(index)
    ax.plot(data)

Крім того, ви можете просто додати екземпляр осей до іншої фігури:

import matplotlib.pyplot as plt

fig1, ax = plt.subplots()
ax.plot(range(10))

fig2 = plt.figure()
fig2.axes.append(ax)

plt.show()

Зміна його розміру відповідно до інших «фігур» підрозділу, але це швидко спричинить більші труднощі, ніж це варте. На мій досвід, підхід до простого обходу екземпляра фігури чи осі (або списку екземплярів) набагато простіший для складних випадків ...


+1 Це корисно, але мені здається, що осі все ще пов'язані з малюнками та / або з деяким станом у pyplot. Я не можу по-справжньому відокремити створення осі від малювання та побудови графіків за вашим прикладом.
juanchopanza

Осі принципово пов’язані з певною фігурою в matplotlib. Це ніяк не обійти. Тим не менш, ви все ще можете повністю "відокремити створення осі від створення та складання фігури", просто передавши навколо осі та фігурні об'єкти. Я не зовсім впевнений, що дотримуюся того, що ви хочете зробити ...
Джо Кінгтон,

Ну, насправді, я думаю, вони не такі фундаментально пов’язані, як я думав. Ви можете просто додати однакові осі до іншої фігури. (Просто зробіть fig2.axes.append(ax1)) Також можливе зміна розміру відповідно до різних форм сюжету. Це, мабуть, призведе до більших проблем, ніж варто ...
Джо Кінгтон,

4
додавання екземпляра осей до іншої фігури (останній приклад) не працює для мене в Enthought 7.3-2 (matplotlib 1.1.0).
aaren

13
@aaren - Це не працює, оскільки спосіб роботи стеку осей для фігури змінено в нових версіях matplotlib. Зараз сокири навмисно не повинні ділитися між різними фігурами. Як обхідний шлях ви можете це зробити fig2._axstack.add(fig2._make_key(a), a), але це хакінг і, можливо, зміниться в майбутньому. Здається, це працює належним чином, але це може зламати деякі речі.
Джо Кінгтон,

22

Далі показано, як "переміщати" осі від однієї фігури до іншої. Це передбачувана функціональність останнього прикладу @ JoeKington , який у нових версіях matplotlib вже не працює, оскільки осі не можуть жити одночасно у кількох фігурах.

Спочатку потрібно буде зняти осі з першої фігури, а потім додати її до наступної фігури і надати їй якусь позицію для життя.

import matplotlib.pyplot as plt

fig1, ax = plt.subplots()
ax.plot(range(10))
ax.remove()

fig2 = plt.figure()
ax.figure=fig2
fig2.axes.append(ax)
fig2.add_axes(ax)

dummy = fig2.add_subplot(111)
ax.set_position(dummy.get_position())
dummy.remove()
plt.close(fig1)

plt.show()

1
невелике доповнення: при plt.show()заміні fig2.savefig('out.png', dpi=300)на позицію псується через dpiключове слово. Цього можна уникнути, встановивши фінал, dpiколи axініціалізується:fig1, ax = plt.subplots(dpi=300)
Matthias123

У моїй оболонці Python, схоже, цей рядок нічого не робить: fig2.axes.append (ax)
Спірко

1
@ImportanceOfBeingErnest Так; Я отримав свою фігуру у pickle. Перепрошую, що пропустив цю важливу деталь. У підсумку я встановив 9 AxesSubplot set_visible(False)і змінив положення тієї, яку хотів показати.
gerrit

1
@gerrit Може вам потрібна ця відповідь ?
ImportanceOfBeingErnest

1
@irene Зверніть увагу, що це рішення лише переміщує осі до нової фігури, воно не встановлює жодного з перетворень. Тож такі питання очікуються. Оскільки не рекомендується переміщати художників між фігурами, краще не використовуйте це, якщо вам потрібні надійні результати.
ImportanceOfBeingErnest

4

Для лінійних графіків можна мати справу з Line2Dсамими об’єктами:

fig1 = pylab.figure()
ax1 = fig1.add_subplot(111)
lines = ax1.plot(scipy.randn(10))

fig2 = pylab.figure()
ax2 = fig2.add_subplot(111)
ax2.add_line(lines[0])

+1 Хороший приклад. Здається, я не можу відокремити створення осей від створення фігури, але я можу захопити екземпляр осей і передати його новим фігурам.
juanchopanza

1
Зверніть увагу, що такий підхід вже не працює. Див. Коментар Джо Кінгтона від 7 грудня 2012 р. Щодо його відповіді вище.
joelostblom

2
ax2.add_line(lines[0])результати в RuntimeError: Can not put single artist in more than one figure(Python 3.7.0, matplotlib 2.2.2).
Герріт

0

TL; DR, частково заснований на Джо, приємна відповідь.

Опт.1: fig.add_subplot()

def fcn_return_plot():
    return plt.plot(np.random.random((10,)))
n = 4
fig = plt.figure(figsize=(n*3,2))
#fig, ax = plt.subplots(1, n,  sharey=True, figsize=(n*3,2)) # also works
for index in list(range(n)):
    fig.add_subplot(1, n, index + 1)
    fcn_return_plot()
    plt.title(f"plot: {index}", fontsize=20) 

Опт.2: перехід ax[index]до функції, яка повертаєтьсяax[index].plot()

def fcn_return_plot_input_ax(ax=None):
    if ax is None:
        ax = plt.gca()
    return ax.plot(np.random.random((10,)))
n = 4
fig, ax = plt.subplots(1, n,  sharey=True, figsize=(n*3,2))
for index in list(range(n)):
    fcn_return_plot_input_ax(ax[index])
    ax[index].set_title(f"plot: {index}", fontsize=20)

Виходи поважають. введіть тут опис зображення введіть тут опис зображення

Примітка: Опт.1 plt.title()змінено в опції 2 на ax[index].set_title(). Знайдіть більше Matplotlib Gotchas у книзі Ван дер Пласа .


0

Заглибитися в кролячу нору. Розширюючи свою попередню відповідь, можна повернути ціле ax, і не ax.plot()тільки. Напр

Якщо фрейм даних мав 100 тестів 20 типів (тут ідентифікатор):

dfA = pd.DataFrame(np.random.random((100,3)), columns = ['y1', 'y2', 'y3'])
dfB = pd.DataFrame(np.repeat(list(range(20)),5), columns = ['id'])
dfC = dfA.join(dfB)

І функція сюжету (це ключ до всієї цієї відповіді):

def plot_feature_each_id(df, feature, id_range=[], ax=None, legend_bool=False):
    feature = df[feature]
    if not len(id_range): id_range=set(df['id'])
    legend_arr = []
    for k in id_range:
        pass
        mask = (df['id'] == k)
        ax.plot(feature[mask])
        legend_arr.append(f"id: {k}")
    if legend_bool: ax.legend(legend_arr)
    return ax

Ми можемо досягти:

feature_arr = dfC.drop('id',1).columns
id_range= np.random.randint(len(set(dfC.id)), size=(10,))
n = len(feature_arr)
fig, ax = plt.subplots(1, n,  figsize=(n*6,4));
for i,k in enumerate(feature_arr):
    plot_feature_each_id(dfC, k, np.sort(id_range), ax[i], legend_bool=(i+1==n))
    ax[i].set_title(k, fontsize=20)
    ax[i].set_xlabel("test nr. (id)", fontsize=20)

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.