Документация Engee

Работа с WorkspaceArray в Engee

Страница в процессе разработки.

WorkspaceArray — это специальный тип данных в среде моделирования Engee, предназначенный для хранения и обработки временных рядов. Он основан на интерфейсе AbstractArray языка Julia, но в отличие от стандартных массивов, реализует ленивую загрузку (Lazy Loading) данных, что делает его ленивым массивом.

Ленивый массив (или лениво загружаемый массив) — это структура, которая не хранит данные целиком в оперативной памяти, а подгружает их по мере необходимости: например, при итерировании, обращении по индексу или преобразовании в таблицу. Это позволяет эффективно работать с очень большими наборами данных, которые не умещаются в память или поступают из внешних источников (например, из базы данных или файла).

WorkspaceArray в Engee используется в следующих сценариях:

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

Создание WorkspaceArray

Обычно WorkspaceArray формируется автоматически в процессе симуляции — например, при записи сигналов через блок В рабочую область или при сохранении результатов в переменную simout.

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

  • Из CSV-файла. Чтобы загрузить данные из CSV-файла, достаточно указать путь к файлу:

    wa = WorkspaceArray("my_wa", "/user/path_to_csv.csv")

    "my_wa" — это имя, под которым массив будет сохранен в Окно переменных variables article 2 1 Engee. Оно используется для последующей ссылки на массив в моделях, скриптах и других приложениях.

    CSV-файл должен содержать два столбца: один обязательно с именем time, второй — со значениями сигнала. Пример структуры CSV-файла:

    time    value
    0.1     1
    0.2     2
    0.3     3
  • Из таблицы DataFrame. Если вы уже работаете с табличными данными в Julia, можно создать WorkspaceArray напрямую из объекта DataFrame. Это особенно удобно при генерации входных сигналов программно, во время анализа или трансформации данных.

    DataFrame — это табличная структура из пакета DataFrames.jl, аналогичная таблицам в Excel или DataFrame в Python (pandas). Каждая колонка имеет имя и тип, а данные хранятся по строкам. Пример создания:

    using DataFrames
    
    df = DataFrame(time = [0.1, 0.2, 0.3], value = [1, 2, 3])
    wa = WorkspaceArray("my_wa", df)

    Таблица должна содержать два столбца: time (тип Float64) и value (может быть скаляром, вектором или массивом чисел).

Работа с WorkspaceArray

WorkspaceArray реализует интерфейс AbstractArray, что позволяет обращаться к нему как к обычному массиву в Julia — индексировать, итерировать, выполнять срезы и т.д. Однако помимо базового поведения массивов, WorkspaceArray содержит внутреннюю структуру и дополнительные поля, которые обеспечивают его связь с источниками данных, поддерживают ленивую загрузку и позволяют удобно работать с временными рядами.

Каждый WorkspaceArray — это не просто набор чисел, а объект с «метаданными»: он знает, где лежат его данные, как они связаны со временем, откуда загружаются, и как были получены. Эти дополнительные свойства можно запросить напрямую, просто обращаясь к полям объекта через точку:

wa = WorkspaceArray("my_wa")
wa.time    # доступ к временны́м меткам
wa.value   # доступ к значениям
wa.type    # узнаём, какой тип у массива — :pair, :time или :value

Доступны следующие поля:

  • time — дочерний WorkspaceArray, содержащий только временные метки. Имеет тип :time;

  • value — дочерний WorkspaceArray, содержащий значения сигнала. Имеет тип :value;

  • type — тип текущего массива: :time, :value или :pair (временные метки + значения);

  • parent — ссылка на родительский WorkspaceArray, если текущий является результатом среза;

  • range — диапазон индексов, на основе которого был создан срез. Используется при ленивом срезе;

  • dimension — размерность данных в поле value (например, скаляр, вектор, матрица);

  • signal_id — уникальный идентификатор источника данных в Engee.

Чаще всего при работе с WorkspaceArray напрямую используются поля time и value, особенно при необходимости получить данные по отдельности или преобразовать их в таблицу.

Повторное использование

После создания переменной, содержащей WorkspaceArray (например с именем wa), автоматически связывается с signal_id (уникальным идентификатором) в Engee. Это позволяет ссылаться на нее по имени и повторно использовать в других частях проекта — например, в веб-приложениях на базе фреймворка Genie (подробнее см. Работа с Genie в Engee), в других моделях или в блоках с вложенностью, например Подсистема:

wa = WorkspaceArray("my_wa")

Такая запись создает новый WorkspaceArray, ссылающийся на уже зарегистрированные данные под именем "my_wa".

Использование с моделями

Блоки В рабочую область и Из рабочей области предназначены для взаимодействия с WorkspaceArray в модели:

Блок В рабочую область записывает данные из модели в переменную типа WorkspaceArray в рабочей области Engee. Имя переменной задается параметром Variable name. Поддерживаются только числовые данные (скалярные и массивы).

Блок Из рабочей области считывает данные из рабочей области и подает их на вход модели. Используются только переменные типа WorkspaceArray.

Блоки To Workspace и From Workspace работают только с типом WorkspaceArray.

Пример использования: From/To Workspace демо

Работа с CSV

Экспорт WorkspaceArray в CSV

Для экспорта WorkspaceArray в CSV используйте CSV.write с обязательным параметром delim="\t":

using CSV
CSV.write("/user/workspacearray_csv.csv", delim="\t", data_frame)
Отсутствие параметра delim="\t" вызовет ошибку BoundsError при импорте и создаст пустой файл.

Импорт WorkspaceArray из CSV

wa = WorkspaceArray("my_wa", "/user/my_data.csv")

Файл должен содержать два столбца:

  • time — обязательное имя столбца;

  • value — значение.

Выгрузка данных

Полная выгрузка в DataFrame

Для полной выгрузки данных из WorkspaceArray в DataFrame используйте collect:

df = collect(wa) # получаем DataFrame с колонками time и value

Вы также можете получить отдельные колонки из WorkspaceArray:

collect(wa.time)   # только время
collect(wa.value)  # только значения
Не рекомендуется использовать collect для очень больших данных — возможна перегрузка памяти.

Ленивые срезы

Одно из ключевых преимуществ WorkspaceArray — поддержка ленивых срезов (Lazy Slices).

В отличие от обычных массивов Julia, где срез (wa[1:10]) создает новый массив с копией данных, WorkspaceArray работает иначе. При срезе создается представление (view), которое хранит только диапазон индексов (range) и ссылку на исходный объект (parent) — без фактической загрузки значений в память. Данные подгружаются из хранилища только при явном запросе, например, через collect, при итерации или доступе по индексу.

Это особенно полезно для:

  • Экономии памяти — срезы не копируют данные, а лишь указывают на нужные элементы;

  • Быстродействия — можно работать только с нужными фрагментами данных;

  • Гибкости — ленивые срезы можно использовать в качестве входных данных моделей, в визуализациях или при экспорте.

Пример:

wa_slice = wa[1:2:end] # ленивое представление: каждый второй элемент
collect(wa_slice)      # загружаем данные в память (в виде DataFrame)

Результат:

Row │ time     value
    │ Float64  Int64
────┼───────────────
  1 │ 0.1      1
  2 │ 0.3      3
  3 │ 0.5      5

Кроме срезов, можно получить доступ к отдельному элементу по индексу:

wa[1] # доступ к первой записи, будет получено (0.1, 1)

Срезы по полям time и value

В WorkspaceArray доступны отдельные поля .time и .value, к которым также можно применять ленивые срезы:

wa_values = wa.value[2:end] # срез только по значениям
wa_times  = wa.time[2:end]  # срез только по временной шкале

Срез wa[1:2:end] возвращает объект типа WorkspaceArray с тем же signal_id, но с новым полем range, указывающим на срез. Это позволяет узнать, что перед вами ленивое представление, а не копия данных.

При необходимости данные можно загрузить в память:

wa = WorkspaceArray("my_wa")

wa_slice = wa[1:2:end]     # создаем ленивый срез
df = collect(wa_slice)     # загрузка данных
println(df)                # получаем таблицу со срезанными значениями

Методы и интерфейс WorkspaceArray

WorkspaceArray реализует стандартные интерфейсы Julia и дополняет их собственными методами. Ниже — краткий обзор наиболее полезных функций, которые можно использовать при работе с временными рядами:

  • wa[i] — доступ к элементу по индексу, возвращает кортеж (time, value):

    wa[1] # будет получено (0.1, 42.0)
  • wa[start:stop] — срез по диапазону, возвращает ленивое представление. Данные не копируются, а подгружаются по мере необходимости:

    wa_slice = wa[1:10]
    collect(wa_slice) # загружает только выбранный диапазон
  • wa[1:2:end] — ленивый срез с шагом.

  • wa.time, wa.value — доступ к дочерним WorkspaceArray, содержащим отдельно время и значения. К ним тоже можно применять срезы:

    wa_times = wa.time[5:end]
    wa_values = wa.value[5:end]
  • collect(wa) — преобразует WorkspaceArray в DataFrame. Это основной способ получить данные в явном виде:

    df = collect(wa)
  • copy(wa) / similar(wa) — создают поверхностную копию объекта. В текущей реализации работают одинаково.

  • wa1 == wa2 — сравнение двух WorkspaceArray по signal_id и диапазону (range).

Если вы работаете с временными рядами в Engee, то, скорее всего, вам хватит collect, срезов (wa[1:10]) и iterrows для повседневных задач. Остальные методы пригодятся при оптимизации и тонкой настройке поведения.

Пример использования

Рассмотрим базовый пример работы с WorkspaceArray — от создания до получения среза и выгрузки данных. Этот сценарий охватывает самые типовые сценарии использования WorkspaceArray.

  1. Для начала создайте таблицу (DataFrame), содержащую два столбца: time и value. Это обязательный формат для импорта данных в WorkspaceArray:

    using DataFrames
    
    df = DataFrame(time = [0.1, 0.2, 0.3, 0.4, 0.5],
                   value = [1, 2, 3, 4, 5])
    • time — момент времени (в секундах);

    • value — значение сигнала в этот момент.

  2. Преобразуйте таблицу в WorkspaceArray, указав имя, под которым массив будет доступен в Engee:

    wa = WorkspaceArray("myarr", df)

    Здесь "myarr" — имя переменной, с которой можно будет работать в моделях и скриптах.

  3. WorkspaceArray поддерживает доступ по индексу. Например, получите первую запись:

    println(wa[1])

    Результат:

    (0.1, 1)

    Это кортеж: первый элемент — время, второй — значение сигнала.

    Данные подгружаются только при обращении к ним. То есть даже при wa[1] загружается только один элемент.
  4. Сделайте срез массива, получите каждый второй элемент (1, 3, 5). Это создаст ленивое представление, а не копию:

    wa_slice = wa[1:2:end]

    Чтобы получить данные в виде таблицы, используйте collect:

    println(collect(wa_slice))

    Результат:

    3×2 DataFrame
     Row │ time     value
         │ Float64  Int64
    ─────┼────────────────
       1 │     0.1      1
       2 │     0.3      3
       3 │     0.5      5

    Срез wa[1:2:end] не загружает данные сам по себе. Только при collect, итерации или обращении по индексу данные реально подгружаются из хранилища.