Создать подзаговоры matplotlib без ручного подсчета количества подзаговоров? - PullRequest
1 голос
/ 06 февраля 2020

Когда я выполняю анализ ad-ho c в Jupyter Notebook, я часто хочу просматривать последовательности преобразований для некоторых Pandas DataFrame в виде вертикально расположенных участков. Мой обычный быстрый и грязный метод состоит в том, чтобы вообще не использовать подзаговоры, а создать новую фигуру для каждого графика:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

df = pd.DataFrame({"a": range(100)})  # Some arbitrary DataFrame
df.plot(title="0 to 100")
plt.show()

df = df * -1  # Some transformation
df.plot(title="0 to -100")
plt.show()

df = df * 2  # Some other transformation
df.plot(title="0 to -200")
plt.show()

Этот метод имеет ограничения. Тики оси X не выровнены даже при одинаковом индексировании (поскольку ширина оси x зависит от меток оси Y), а выходные данные ячейки Jupyter содержат несколько отдельных встроенных изображений, а не одно, которое я могу сохранить или скопировать и вставить .

Насколько я знаю, правильное решение состоит в том, чтобы использовать plt.subplots():

fig, axes = plt.subplots(3, figsize=(20, 9))

df = pd.DataFrame({"a": range(100)}) # Arbitrary DataFrame
df.plot(ax=axes[0], title="0 to 100")

df = df * -1 # Some transformation
df.plot(ax=axes[1], title="0 to -100")

df = df * 2 # Some other transformation
df.plot(ax=axes[2], title="0 to -200")

plt.tight_layout()
plt.show()

Это дает именно тот результат, который я хотел бы получить. Однако он также вызывает раздражение, которое заставляет меня использовать первый метод по умолчанию: мне нужно подсчитать вручную количество созданных мной вспомогательных участков и обновить этот счетчик в нескольких разных местах по мере изменения кода.

В случае нескольких фигур добавить четвертый график так же просто, как вызвать df.plot() и plt.show() в четвертый раз. При использовании вспомогательных графиков эквивалентное изменение требует обновления счетчика вспомогательных участков, плюс арифметическое значение c, чтобы изменить размер выходного значения, заменив plt.subplots(3, figsize=(20, 9)) на plt.subplots(4, figsize=(20, 12)). Каждому вновь добавленному субплоту необходимо знать, сколько других субплощадок уже существует (ax=axes[0], ax=axes[1], ax=axes[2] и др. c.), Поэтому любые добавления или удаления требуют каскадных изменений для графиков ниже.

Это кажется , как будто это должно быть тривиально для автоматизации - это просто подсчет и умножение - но я нахожу невозможным реализовать с помощью API matplotlib / pyplot. Самое близкое, что я могу получить, - это следующее частичное решение, которое достаточно лаконично, но все еще требует явного подсчета:

n_subplots = 3  # Must still be updated manually as code changes

fig, axes = plt.subplots(n_subplots, figsize=(20, 3 * n_subplots))
i = 0  # Counts how many subplots have been added so far 

df = pd.DataFrame({"a": range(100)}) # Arbitrary DataFrame
df.plot(ax=axes[i], title="0 to 100")
i += 1

df = df * -1 # Arbitrary transformation
df.plot(ax=axes[i], title="0 to -100")
i += 1

df = df * 2 # Arbitrary transformation
df.plot(ax=axes[i], title="0 to -200")
i += 1

plt.tight_layout()
plt.show()

Проблема root в том, что в любой момент вызова df.plot() должен существовать axes список известных размеров. Я подумывал о том, чтобы как-то отложить выполнение df.plot(), например, добавив к списку лямбда-функций, которые можно посчитать до их последовательного вызова, но это кажется чрезмерной церемонией, чтобы избежать обновления целого числа вручную.

Есть ли более удобный способ сделать это? В частности, есть ли способ создать фигуру с «расширяемым» количеством вспомогательных участков, подходящую для специальных / интерактивных контекстов, где количество заранее неизвестно?

( Примечание: Этот вопрос может выглядеть как дубликат этого вопроса или этого , но принятые ответы на оба вопроса содержат именно ту проблему, которую я пытаюсь решить решить - что параметр nrows= plt.subplots() должен быть объявлен перед добавлением подзаговоров.)

Ответы [ 2 ]

1 голос
/ 07 февраля 2020

Вы можете создать объект, который хранит данные и создает фигуру только после того, как вы скажете это сделать.

import pandas as pd
import matplotlib.pyplot as plt

class AxesStacker():
    def __init__(self):
        self.data = []
        self.titles = []

    def append(self, data, title=""):
        self.data.append(data)
        self.titles.append(title)

    def create(self):
        nrows = len(self.data)
        self.fig, self.axs = plt.subplots(nrows=nrows)
        for d, t, ax in zip(self.data, self.titles, self.axs.flat):
            d.plot(ax=ax, title=t)



stacker = AxesStacker()

df = pd.DataFrame({"a": range(100)})  # Some arbitrary DataFrame
stacker.append(df, title="0 to 100")

df = df * -1  # Some transformation
stacker.append(df, title="0 to -100")

df = df * 2  # Some other transformation
stacker.append(df, title="0 to -200")

stacker.create()
plt.show()
1 голос
/ 07 февраля 2020

IIU C вам нужен какой-то контейнер для ваших преобразований - например, list. Что-то вроде:

arbitrary_trx = [
    lambda x: x,         # No transformation
    lambda x: x * -1,    # Arbitrary transformation
    lambda x: x * 2]     # Arbitrary transformation

fig, axes = plt.subplots(nrows=len(arbitrary_trx))

for ax, f in zip(axes, arbitrary_trx):
    df = df.apply(f)
    df.plot(ax=ax)

enter image description here

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