Временные ряды с Julia с библиотекой TimeSeries.jl
Julia позволяет достигать скорости, сравнимой с C, что делает её хорошим выбором для временных рядов.
Пакет TimeSeries
предоставляет удобные методы для работы с данными временных рядов в Julia.
Установка
Активируем менеджер пакетов, нажав клавишу ]
. Приглашение командной строки изменится на (v1.x) pkg>
, где x
текущая версия Julia:
В режиме менеджера пакетов юзаем команду:
add TimeSeries
Команда автоматически найдет, загрузит и установит пакет TimeSeries.jl
, а также все его зависимости.
После установки можно чекнуть, установлен ли пакет с помощью:
using TimeSeries
Тип TimeArray
TimeArray
— это структура данных, разработанная для хранения и обработки временных рядов. Она содержит следующее:
Timestamps: метки времени, указывающие на конкретные моменты времени для каждого наблюдения.
Values: значения данных, соответствующие каждой метке времени.
Columns: имена столбцов, представляющие различные переменные или измерения временного ряда.
Metadata: доп. данные, которые могут быть ассоциированы с временным рядом.
Для создания объекта TimeArray
необходимо предоставить данные о времени, значения и, опционально, имена столбцов и метаданные:
using TimeSeries
using Dates
dates = Date(2023, 1, 1):Day(1):Date(2024, 1, 5)
values = [101, 102, 103, 104, 105]
ta = TimeArray(dates, values, ["Price"])
Здесь создали временной ряд с ежедневными данными о цене за первые пять дней января 2023 года.
Доступ к данным в TimeArray
можно получить разными способами. Например, для получения значений:
ta.values
Для доступа к меткам времени:
ta.timestamp
И для получения имен столбцов:
ta.colnames
Индексация
В TimeSeries.jl индексация TimeArray
осуществляется не только по числовым индексам, но и по временным меткам.
Допустим, есть временной ряд, представленный объектом TimeArray
. Можно обращаться к его элементам, используя метки времени:
using TimeSeries, Dates
# пример временного ряда
dates = Date(2024, 1, 1):Day(1):Date(2024, 1, 5)
data = rand(5, 2) # случайные данные для примера
ta = TimeArray(dates, data, ["Column1", "Column2"])
# индексация по одной дате
ta[Date(2024, 1, 3)]
Код вернет значения TimeArray
за 3 января 2024 года.
Можно выбрать данные за определенный период времени, используя диапазон дат:
ta[Date(2024, 1, 2):Date(2024, 1, 4)]
Запрос вернет данные за период с 2 по 4 января 2024 года.
Индексация по условию позволяет выбирать данные на основе логического условия:
ta[ta.timestamp .> Date(2024, 1, 2)]
Этот пример вернет все данные, начиная с 3 января 2024 года.
В дополнение к временной индексации, TimeArray
поддерживает индексацию по столбцам:
# индексация по имени столбца
ta["Column1"]
# индексация по нескольким столбцам
ta[[:Column1, :Column2]]
Запросы вернут данные только для указанных столбцов.
TimeSeries.jl также поддерживает комбинированную индексацию:
# комбинированная индексация по дате и столбцу
ta[Date(2024, 1, 3), "Column1"]
Запрос вернет значение из столбца Column1 за 3 января 2024 года.
Для более сложных случаев индексации можно использовать функции, которые применяются к каждой метке времени или значению в TimeArray
:
ta[findall(x -> month(x) == 1, ta.timestamp)]
Вернутся все данные за январь 2024 года.
Разделение временных рядов по условиям
Разделение временных рядов по условиям позволяет разделить временной ряд на несколько частей на основе определенных критериев.
Функция split
в TimeSeries.jl позволяет разделить TimeArray
на несколько частей по заданному условию. Условие может быть основано на значениях данных, метках времени или метаданных. Результаом работы split
является массив TimeArray
объектов, каждый из которых соответствует сегменту данных, удовлетворяющему определенному условию.
Предположим, есть временной ряд, который хочется разделить на две части: одна содержит значения выше определенного порога, а другая — ниже:
using TimeSeries, Dates
# пример временного ряда
dates = Date(2024, 1, 1):Day(1):Date(2024, 1, 10)
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
ta = TimeArray(dates, data, ["Value"])
# Разделение по условию значения > 5
split_arrays = split(ta, ta[:Value] .> 5)
split
вернет массив из двух TimeArray
объектов: первый для значений Value > 5
и второй для остальных.
Можно также разделить временной ряд на основе даты:
split_date = Date(2024, 1, 5)
split_arrays = split(ta, ta.timestamp .<= split_date)
Здесь временной ряд разделяется на две части: до и после 5 января 2024 года.
Разделение временного ряда по дням недели:
# разделение по дню недели (например, выходные и будние)
is_weekend = x -> dayofweek(x) in (6, 7)
split_arrays = split(ta, is_weekend.(ta.timestamp))
split
создает два TimeArray
объекта: один для выходных дней и другой для будних.
Изменение существующих TimeArray
Для изменения значений в TimeArray
, можно использовать индексацию для доступа к конкретным элементам и присвоить им новые значения:
using TimeSeries, Dates
# пример временного ряда
dates = Date(2024, 1, 1):Day(1):Date(2024, 1, 5)
data = [1, 2, 3, 4, 5]
ta = TimeArray(dates, data, ["Value"])
# изменяем значение на второй дате
ta[2] = 10
Код изменит значение во второй строке TimeArray
на 10.
Можно добавить новые столбцы к существующему TimeArray
с помощью функции merge
, которая объединяет два TimeArray
объекта. Для удаления столбцов есть функция delete
:
# новый столбец данных
new_data = [6, 7, 8, 9, 10]
new_column = TimeArray(dates, new_data, ["NewValue"])
# новый столбец к существующему TimeArray
merged_ta = merge(ta, new_column)
# удаляем столбец "NewValue"
reduced_ta = delete(merged_ta, :NewValue)
Можно функции к значениям в TimeArray
для трансформации данных, с помощью функции map
или путем прямого применения функций к TimeArray
:
# функция удвоения к каждому значению
doubled_ta = map(x -> x * 2, ta)
Если хочется применить функцию к каждому столбцу TimeArray
и получить новый TimeArray
в результате, можно сделать это напрямую:
# увеличиваем каждое значение на 1
incremented_ta = ta .+ 1
Больше про временные ряды, анализ данных и практические инструменты, вы можете узнать в рамках онлайн-курсов от практикующих экспертов. В каталоге курсов OTUS все заинтересованные смогут найти подходящее направление.