2025, Nov 16 06:02

Динамическое построение графиков по датам в Matplotlib из нескольких источников

Как в Matplotlib и pandas строить по одной фигуре на дату из нескольких источников: группировать DataFrame по дате, задавать строковый id в plt.figure

Когда вы сравниваете синхронизированные по времени метрики из нескольких источников, строить графики становится неудобно, если набор дат меняется, а вы пытаетесь заранее вручную создать фигуры. Так часто бывает с данными, прочитанными из CSV‑файлов и сохранёнными во вложенных словарях, где каждый сайт даёт по одному DataFrame на день. Цель проста: для каждой даты отрисовать единственную фигуру, в которой представлены линии со всех сайтов за эту дату, без жёсткого прописывания количества дат.

Постановка задачи

Данные организованы как вложенное отображение сайтов в ежедневные DataFrame, и прямолинейная попытка построения приводит к предварительному выделению фигур по индексам. Такой подход работает лишь если вы уже знаете, сколько дат будет; это хрупко и требует ручных правок.

import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
meteo_store = {'Station_A':
               {'Mean': {'01-01': pd.DataFrame(np.random.rand(5,2)), '01-02': pd.DataFrame(np.random.rand(5,2)),
                         '01-03': pd.DataFrame(np.random.rand(5,2))},
                'Misc': {'01-01': 'dummy_data', '01-02': 'dummy_data'}},
               'Station_B':
               {'Mean': {'01-01': pd.DataFrame(np.random.rand(5,2)), '01-02': pd.DataFrame(np.random.rand(5,2)),
                         '01-03': pd.DataFrame(np.random.rand(5,2))},
                'Misc': {'01-01': 'dummy_data', '01-02': 'dummy_data'}}}
plt.figure(1)
plt.figure(2)
plt.figure(3)
for station, payload in meteo_store.items():
    fig_idx = 1
    mean_block = payload['Mean']
    for date_key, frame in mean_block.items():
        plt.figure(fig_idx)
        plt.plot(frame[0], frame[1], label=station)
        plt.legend()
        fig_idx += 1

Почему это хрупко

Число дат заранее неизвестно, поэтому предварительное создание фигур по индексам оборачивается постоянным обслуживанием. Даже если ограничиться небольшим диапазоном дней, это по‑прежнему чревато ошибками и засоряет код. Есть и важная мысль: вам не нужно создавать фигуры заранее. Matplotlib создаст фигуру по требованию, когда вы вызываете конструктор figure с идентификатором.

Не нужно заранее создавать фигуры. Вызов plt.figure(i) создаст фигуру, если её ещё нет.

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

Решение: сначала сгруппировать по дате, затем строить

Идея в том, чтобы построить отображение от даты ко всем DataFrame сайтов за эту дату. После этого проходите по отображению и создавайте по одной фигуре на каждую дату. Это устраняет ручной учёт фигур и масштабируется на любое число сайтов и дат.

import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
from collections import defaultdict
meteo_store = {'Station_A': 
               {'Mean': {'01-01': pd.DataFrame(np.random.rand(5,2)), '01-02': pd.DataFrame(np.random.rand(5,2)),
                         '01-03': pd.DataFrame(np.random.rand(5,2))},
                'Misc': {'01-01': 'dummy_data', '01-02': 'dummy_data'}},
               'Station_B':
               {'Mean': {'01-01': pd.DataFrame(np.random.rand(5,2)), '01-02': pd.DataFrame(np.random.rand(5,2)),
                         '01-03': pd.DataFrame(np.random.rand(5,2))},
                'Misc': {'01-01': 'dummy_data', '01-02': 'dummy_data'}}}
by_day = defaultdict(list)
for station_name, content in meteo_store.items():
    for date_key, df in content['Mean'].items():
        by_day[date_key].append((station_name, df))
for day_key, series in by_day.items():
    plt.figure(day_key)
    for st, dframe in series:
        plt.plot(dframe[0], dframe[1], label=st)
    plt.legend()
plt.show()

В результате получается одна фигура на дату, и в каждой — по одной линии на сайт. Даты определяются динамически, а передача строковой даты в plt.figure использует её как идентификатор, так что вам никогда не придётся считать или заранее объявлять индексы.

Почему этот подход важен

Динамическая группировка по дате делает код устойчивым к меняющемуся входу. Вы избегаете предположений о числе дней и получаете сопоставимые графики по сайтам для каждой даты. Ещё один практический плюс: агрегирование по датам можно формировать прямо при чтении CSV‑файлов, при желании обходя промежуточную вложенную структуру.

Выводы

При построении данных из нескольких источников, синхронизированных по времени, не создавайте фигуры заранее. Сначала агрегируйте записи по ключу построения (дате), затем позвольте matplotlib создавать фигуры по требованию. Простое отображение «дата → серии» делает цикл построения компактным, подходит для любого числа сайтов, и избавляет от ручного управления фигурами.