matplotlib: могу ли я создать объекты AxesSubplot, а затем добавить их в экземпляр Figure?
Глядя на matplotlib
документация, кажется, стандартный способ добавить AxesSubplot
к 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
и если да, то как я могу это сделать?
Обновление: мне не удалось отделить создание осей и фигур, но, следуя примерам в ответах ниже, можно легко повторно использовать ранее созданные оси в новых или старых экземплярах фигур. Это можно проиллюстрировать с помощью простой функции:
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
5 ответов
Как правило, вы просто передаете экземпляр осей функции.
Например:
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()
Изменение размера в соответствии с другими "формами" подзаговора также возможно, но это быстро вызовет больше хлопот, чем стоит. По моему опыту, подход, заключающийся в просто передаче экземпляра фигуры или оси (или списка экземпляров), намного проще для сложных случаев...
Ниже показано, как "перемещать" оси от одной фигуры к другой. Это предназначенная функциональность последнего примера @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()
Для линейных участков вы можете иметь дело с 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])
TL;DR частично основан на хорошем ответе user325565
Вариант 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 в книге Van der Plas .
Чтобы углубиться в кроличью нору. Расширяя мой предыдущий ответ, можно было бы вернуть целое
ax
, и не
ax.plot()
Только. Например
Если в кадре данных было 100 тестов 20 типов (здесь id):
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)