рекомендации

вторник, 14 ноября 2017 г.

Работа с временными рядами в R

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

В R есть несколько очень мощных пакетов, предназначенных для работы с временными рядами. В данной статье мы рассмотрим их возможности.

Создание dataframe

Dataframe - это очень популярный объект в R. Он очень активно используется в различных областях. Если вы загружаете внешние данные с помощью функций “read.table”, “read.csv” или “read.xlsx”, результатом будет dataframe.

В начале любого скрипта R желательно сразу указать, сколько чисел после запятой вам требуется. Я предпочитаю два, но в определенных случаях может потребоваться большая точность. Во всех случаях мы также можем использовать для этого функцию round, например round(x, 5) - пять чисел после запятой для объекта "x".

options(digits = 2)

# Создаем dataframe для временных рядов рыночной капитализации и отношения PE.
> data <- data.frame(Date = c("12-12-2012", "13-12-2012", "14-12-2012", "16-12-2012","19-12-2012"), MktCap = c(110, 120, 130, 200, 180), PE = c(18, 18, 18.5, 19, 19))
> data

       Date MktCap   PE
1 12-12-2012    110 18.0
2 13-12-2012    120 18.0
3 14-12-2012    130 18.5
4 16-12-2012    200 19.0
5 19-12-2012    180 19.0

Объект Timeseries

Несмотря на широкое применение и гибкость dataframe, многие важные операции при работе с временными рядами невозможно выполнить с использованием dataframe.

В R есть пакета, которые чаще всего используются для работы с временными рядами: ts (Timeseries) и xts(eXtensible Time Series). Во временных рядах всегда в наличии даты и значения индикатора, соответствующие датам.

Date

Функция “as.Date” позволяет напрямую конвертировать символьные данные в даты заданного формата. Приведенная ниже таблица описывает коды, используемые для различных форматов даты.

Код Значение Вывод
%d День месяца 01-31
%m Месяц в виде числа 01,02…,12
%b Месяц в виде аббревиатуры Jun Jul
%B Полное название месяца June July
%y Год без века 01, 02, …, 10
%Y Год с веком 2001,2002, …., 2010

> date <- "12-01-2014"
> date
[1] "12-01-2014"

> class(date)
[1] "character"

# по умолчанию 'as.Date' генерирует дату в формате YYYYMMDD
> date <- as.Date(date, "%d-%m-%Y")
> date
[1] "2014-01-12"

> class(date)
[1] "Date"

Используя приведенную выше таблицу, мы можем получить дату в любом формате. Однако я предпочитаю конвертировать дату в другие форматы только в том случае, если это необходимо для отчета, поскольку функция "format" меняет класс вектора на "character", лишая нас преимуществ класса "Date".

> date
[1] "12-Январь-2014"

> class(date)
[1] "character"

Значения индикатора или числа

Функция “as.numeric” конвертирует символьный вектор в числовой.

# Создаем вектор
> number <- c("20", "30", "90", "40")
> class(number)
[1] "character"

> number
[1] "20" "30" "90" "40"

# Конвертируем символьный вектор 'number' в числовой
> number <- as.numeric(number)
> class(number)
[1] "numeric"

> number
[1] 20 30 90 40

Конвертирование dataframe в объект xts

Объект xts представляет собой временной ряд. dataframe очень просто конвертируется в xts. Процесс происходит следующим образом:
конвертируем столбец данных из dataframe в класс “date” -> конвертируем значения столбца индикатора из dataframe в класс “numeric” -> используем функцию - конструктор “xts” для создания объекта временного ряда.

Теперь давайте использовать функцию 'ls.str' вместо 'class', чтобы увидеть классы различных векторов в нашем объекте. Она отображает внутреннюю структуру объекта R. В выводе str() на каждый столбец объекта приходится одна строка.

> str(data)
'data.frame': 5 obs. of  3 variables:
$ Date  : Factor w/ 5 levels "12-12-2012","13-12-2012",..: 1 2 3 4 5
$ MktCap: num  110 120 130 200 180
$ PE    : num  18 18 18.5 19 19

Столбец 'Date' является факторным. Мы преобразуем его в символьный, а затем в Date,  всего одной строкой кода:

> date <- as.Date(as.character(data$Date), "%d-%m-%Y")
> class(date)
[1] "Date"

> date
[1] "2012-12-12" "2012-12-13" "2012-12-14" "2012-12-16" "2012-12-19"

> mktCap <- as.numeric(data$MktCap)
> class(mktCap)
[1] "numeric"

> mktCap
[1] 110 120 130 200 180

Создадим объект временного ряда xts. Обратите внимание на порядок расположения данных и дат при сборке объекта: xts(data,date).

> library(xts)
> mcData <- xts(mktCap, date)
> mcData
          [,1]
2012-12-12  110
2012-12-13  120
2012-12-14  130
2012-12-16  200
2012-12-19  180

Базовое форматирование объектов временных рядов

Изменение имени индикатора (столбца) объекта

# Читаем данные котировок google, используя пакет quantmod
> library(quantmod)
> getSymbols("GOOG",src="google", from="2010-01-01", to="2015-01-01")
 [1] "GOOG"

> head(GOOG, 5)
          GOOG.Open GOOG.High GOOG.Low GOOG.Close GOOG.Volume
2010-01-04    313.16    314.44   311.81     313.06          NA
2010-01-05    313.28    313.61   310.46     311.68          NA
2010-01-06    312.62    312.62   302.88     303.83          NA
2010-01-07    304.40    304.70   296.03     296.75          NA
2010-01-08    295.70    301.32   294.26     300.71          NA

# Выводим названия столбцов объекта GOOG
> colnames(GOOG)
[1] "GOOG.Open"   "GOOG.High"   "GOOG.Low"    "GOOG.Close"  "GOOG.Volume"

# Изменяем названия столбцов
> colnames(GOOG) <- c("Open", "High", "Low", "Close", "Volume")

# Изменяем название только одного столбца.
# Например, изменим “Volume” на “Vol”.
> colnames(GOOG)[5] <- "Vol"
> colnames(GOOG)
[1] "Open"  "High"  "Low"   "Close" "Vol"

Удаление столбцов

Перед тем, как мы двинемся дальше, важно знать, как извлечь из объекта один столбец. В нашем объекте “GOOG” пять столбцов: "Open",  "High",  "Low",   "Close" и "Vol". Извлечь из  него один столбец можно несколькими способами.

# Способ 1
> high <- GOOG[, 2]
> head(high, 5)
            High
2010-01-04 314.44
2010-01-05 313.61
2010-01-06 312.62
2010-01-07 304.70
2010-01-08 301.32

# Способ 2
> high <- GOOG$High
> head(high, 5)
            High
2010-01-04 314.44
2010-01-05 313.61
2010-01-06 312.62
2010-01-07 304.70
2010-01-08 301.32

Давайте теперь удалим “High” из объекта “GOOG”.

> GOOG.ex.High <- GOOG[, -2]
> head(GOOG.ex.High, 5)

            Open    Low  Close Vol
2010-01-04 313.16 311.81 313.06  NA
2010-01-05 313.28 310.46 311.68  NA
2010-01-06 312.62 302.88 303.83  NA
2010-01-07 304.40 296.03 296.75  NA
2010-01-08 295.70 294.26 300.71  NA

Также просто можно удалить несколько столбцов из объекта с большим количеством данных. Мы будем использовать набор временных рядов, показывающих ВВП на душу населения в 165 странах с 1970 по 2010 год. Файл можно скачать здесь.

# Загружаем данные в R
> library(xlsx)
> rawdata <- read.xlsx("gdp.xlsx", sheetIndex = 1)
> str(rawdata)

# Конвертируем дату в объекте  'rawdata'
> date <- as.Date(as.character(rawdata[, 1]), "%Y-%m-%d")
> data <- xts(rawdata[, -1], date)

# Удалим следующий набор из объекта 'data'
> set <- c("Brazil.", "Russia", "India.", "China.", "South.Africa.")

#Удаляем столбцы этих стран из 'data' и сохраняем  data в 'dataExBRICS'
> dataExBRICS <- data[, -(which(colnames(data) %in% set))]

# Количество столбцов в объектах 'data' и 'dataExBRICS'
> ncol(data)
[1] 255

> ncol(dataExBRICS)
[1] 250

Добавление столбцов (объединение двух объектов данных)

Объединим два объекта с одним и тем же временным индексом

> library(quantmod)
# Объект 1
> getSymbols("GOOG",src="google", from="2017-08-01", to="2017-08-30")
[1] "GOOG"

> head(GOOG, 5)
          GOOG.Open GOOG.High GOOG.Low GOOG.Close GOOG.Volume
2017-08-01    932.38    937.45   929.26     930.83     1277734
2017-08-02    928.61    932.60   916.68     930.39     1824448
2017-08-03    930.34    932.24   922.24     923.65     1202512
2017-08-04    926.75    930.31   923.03     927.96     1082267
2017-08-07    929.06    931.70   926.50     929.36     1032239

# Объект 2
> getSymbols("MSFT",src="google", from="2017-08-01", to="2017-08-30")
[1] "MSFT"

> head(MSFT, 5)
          MSFT.Open MSFT.High MSFT.Low MSFT.Close MSFT.Volume
2017-08-01     73.10     73.42    72.49      72.58    20823890
2017-08-02     72.55     72.56    71.44      72.26    26499158
2017-08-03     72.19     72.44    71.84      72.15    18214424
2017-08-04     72.40     73.04    72.24      72.68    22578952
2017-08-07     72.80     72.90    72.26      72.40    18705681

# Объединяем объект 1 и объект 2 в объект mergedData
> mergedData <- merge(GOOG, MSFT)
> head(mergedData)
          GOOG.Open GOOG.High GOOG.Low GOOG.Close GOOG.Volume MSFT.Open MSFT.High MSFT.Low MSFT.Close MSFT.Volume
2017-08-01    932.38    937.45   929.26     930.83     1277734     73.10     73.42    72.49      72.58    20823890
2017-08-02    928.61    932.60   916.68     930.39     1824448     72.55     72.56    71.44      72.26    26499158
2017-08-03    930.34    932.24   922.24     923.65     1202512     72.19     72.44    71.84      72.15    18214424
2017-08-04    926.75    930.31   923.03     927.96     1082267     72.40     73.04    72.24      72.68    22578952
2017-08-07    929.06    931.70   926.50     929.36     1032239     72.80     72.90    72.26      72.40    18705681
2017-08-08    927.09    935.81   925.61     926.79     1061579     72.09     73.13    71.75      72.79    22044587

Добавление строк

# Объект 1
> AAPL1=getSymbols("AAPL",src="google", from="2017-08-01", to="2017-08-10", auto.assign=FALSE)

#Смотрим последние 5 значений
> tail(AAPL1, 5)
          AAPL.Open AAPL.High AAPL.Low AAPL.Close AAPL.Volume
2017-08-04    156.07    157.40   155.69     156.39    20559852
2017-08-07    157.06    158.92   156.67     158.81    21870321
2017-08-08    158.60    161.83   158.27     160.08    36205896
2017-08-09    159.26    161.27   159.11     161.06    26131530
2017-08-10    159.90    160.00   154.63     155.32    40804273

# Объект 2
> AAPL2=getSymbols("AAPL",src="google", from="2017-08-11", to="2017-08-20", auto.assign=FALSE)

> tail(AAPL2, 5)
          AAPL.Open AAPL.High AAPL.Low AAPL.Close AAPL.Volume
2017-08-14    159.32    160.21   158.75     159.85    22122734
2017-08-15    160.66    162.20   160.14     161.60    29465487
2017-08-16    161.94    162.51   160.15     160.95    27671612
2017-08-17    160.52    160.71   157.84     157.86    27940565
2017-08-18    157.86    159.50   156.72     157.50    27428069

# Добавляем к объекту 1 строки из объекта 2 с помощью функции 'append'
# Имена столбцов должны быть одинаковые
> AAPL <- append(AAPL1, AAPL2)
> tail(AAPL, 5)
          AAPL.Open AAPL.High AAPL.Low AAPL.Close AAPL.Volume
2017-08-14    159.32    160.21   158.75     159.85    22122734
2017-08-15    160.66    162.20   160.14     161.60    29465487
2017-08-16    161.94    162.51   160.15     160.95    27671612
2017-08-17    160.52    160.71   157.84     157.86    27940565
2017-08-18    157.86    159.50   156.72     157.50    27428069

Выборка интервалов

> library(xlsx)
> library(xts)

# Загружаем данные по ценам на нефть с 2015 по 2017 год.
> monOilRawData <- read.xlsx("Oil.xlsx", sheetIndex = 1, startRow = 1)
> monOilData <- xts(monOilRawData[, -1], monOilRawData[, 1])

Файл с ценами на нефть можно скачать здесь.

# Выводим временной индекс
> index(monOilData)
[1] "2015-01-15" "2015-02-15" "2015-03-15" "2015-04-15" "2015-05-15" "2015-06-15" "2015-07-15" "2015-08-15"
[9] "2015-09-15" "2015-10-15" "2015-11-15" "2015-12-15" "2016-01-15" "2016-02-15" "2016-03-15" "2016-04-15"
[17] "2016-05-15" "2016-06-15" "2016-07-15" "2016-08-15" "2016-09-15" "2016-10-15" "2016-11-15" "2016-12-15"
[25] "2017-01-15" "2017-02-15" "2017-03-15" "2017-04-15" "2017-05-15" "2017-06-15" "2017-07-15" "2017-08-15"
[33] "2017-09-15"

# Используем функцию 'window' для выборки нужного нам интервала из объекта 'monOilData'

> subMonOilData <- window(monOilData, start = "2016-12-01", end = "2017-08-01")
> index(subMonOilData)
[1] "2016-12-15" "2017-01-15" "2017-02-15" "2017-03-15" "2017-04-15" "2017-05-15" "2017-06-15" "2017-07-15"

Выборка столбца

> library(xlsx)
> library(xts)

> rawdata <- read.xlsx2("gdp.xlsx", sheetIndex = 1)
> date <- as.Date(as.character(rawdata[, 1]), "%Y-%m-%d")
> data <- xts(rawdata[, -1], date)

# Выбираем данные по нужным нам странам из объекта 'data'
> set <- c("Brazil.", "Russia", "India.", "China.", "South.Africa.")
> dataExBRICS <- data[, (which(colnames(data) %in% set))]
> colnames(dataExBRICS)

[1] "Russia"        "Brazil."       "China."        "India."        "South.Africa."

Изменение временного индекса

Дневной в недельный

# Загружаем дневные данные по курсу доллара за 2017 год
> usdrub <- read.csv(file = "usdrub.csv", header = TRUE)
> date <- as.Date(as.character(usdrub[, 1]), "%Y-%m-%d")
> UsdtoRub <- xts(usdrub[, -1], date)

Файл с данными здесь.

> week.end <- endpoints(UsdtoRub, on = "weeks")
#Дневной в недельный - последние значения
> weekly <- period.apply(UsdtoRub, INDEX = week.end, FUN = last)
> head(weekly, 5)
             [,1]
2017-01-14 59.3700
2017-01-21 59.6697
2017-01-28 60.3196
2017-02-04 59.3137
2017-02-11 58.8457

# дневной в недельный - средние значения
> weekly <- period.apply(UsdtoRub, INDEX = week.end, FUN = mean)
> head(weekly, 5)
              [,1]
2017-01-14 59.77572
2017-01-21 59.44260
2017-01-28 59.56486
2017-02-04 59.97126
2017-02-11 59.05750

Дневной в месячный

# дневной в месячный - последние значения
> month.end <- endpoints(UsdtoRub, on = "months")
> monthly <- period.apply(UsdtoRub, INDEX = month.end, FUN = last)
> head(monthly, 5)
             [,1]
2017-01-31 60.1618
2017-02-28 57.9371
2017-03-31 56.3779
2017-04-29 56.9838
2017-05-31 56.5168

# дневной в месячный - средние значения
> monthly <- period.apply(UsdtoRub, INDEX = month.end, FUN = mean)
> head(monthly, 5)
              [,1]
2017-01-31 59.62986
2017-02-28 58.53936
2017-03-31 58.00663
2017-04-29 56.43561
2017-05-31 56.95007

Комментариев нет:

Отправить комментарий