23 дек. 2012 г.

Построение графика с двумя осями Y в статистическом пакете R


При построении графиков временных рядов иногда бывает нужно объединить два ряда в один график. Чаще всего это бывает необходимо в двух случаях:
  • каждый из рядов имеет разную размерность
  • размерность одна, но ряды отличаются на порядок или несколько порядков по величине.
В Excel такое построение решается очень просто: “Параметры ряда” - “По вспомогательной оси”. Как же сделать подобное в R? Проблема заключается в том, что стандартные средства построение графиков в R - базовая графика, либо известный пакет ggplot2 не имеют встроенных средств для построения таких графиков. Причем по принципиальным соображениям. К примеру, создатель пакета ggplot2 Hadley Wickham написал еще в 2008 году по этому поводу следующее: “It's possible to do this with base graphics, but impossible to do this with ggplot2. I'm against this technique because I believe it leads to serious visual distortions and meaningless graphics.” Специалисты по визуализации предлагают вместо того, чтобы “смешивать” два графика в один, использовать две панели на одном графике.
К примеру, мы хотим построить график индекса S&P 500 и цены нефти марки Brent для того, чтобы посмотреть, есть ли визуальная зависимость между двумя рядами. Для начала получим исходные данные из базы данных Федерального резервного банка Сент-Луиса - FRED c помощью отличной библиотеки для получения бесплатных финансово-экономических данных quantmod.
require(quantmod)
Sys.setenv(TZ = "GMT")  # необходимо установить часовой пояс на GMT, чтобы без скачивание из FRED работало без ошибок
spx <- getSymbols("SP500", src = "FRED", auto.assign = FALSE)  #S&P 500 Stock Price Index (SP500)
brent <- getSymbols("DCOILBRENTEU", src = "FRED", auto.assign = FALSE)  #Crude Oil Prices: Brent - Europe
API базы данных FRED работает таким образом, что он возвращает все историю в каждом вызове. Задать временной интервал  за который необходимы данные, в действующей реализации невозможно. Скорее всего, они добавят это в будущем, чтобы снизить нагрузки на свои сервера. С нашей точки зрения, это приводит к тому, что данные по индексу S&P 500 доступны с 1957 года.
plot(spx)
plot of chunk unnamed-chunk-2
plot(brent)
plot of chunk unnamed-chunk-2
А данные по ценам на нефть - с 1987 года. Мы же хотим, к примеру, только цифры за последние 5 лет.
brent <- brent["2007::"]  # возьмем данные по нефти только с 2007 года
data <- merge(spx, brent, join = "right")  #объединим индикаторы в один объект xts
names(data) <- c("spx", "brent")  # присвоим нашим имена
Параметр join в функции merge.xts определяет каким образом, осуществляется “слияние” рядов. В нашем случае мы определили слияние по правой переменной (right). Это означает, что в новый объект должны войти все показатели правой переменной и только те значения левой переменной, которые совпадают с правой. В R очень легко комбинировать несколько графиков в один визуальный элемент. Параметр mfrow позволяет задавать матрицу из n-строк и m-столбцов, который заполняются построчно.
par(mfrow = c(2, 1))  # строим графики в 2 строках и 1 столбце
plot(data$spx, main = "S&P 500")
plot(data$brent, main = "Brent")
plot of chunk unnamed-chunk-4
Графики совмещены точно друг над другом, что позволяет соотносить их между собой. Это стандартная рекомендация от специалистов по R, когда необходимо строить графики временных рядов для сравнения друг с другом. Но что делать, если все таки необходимо объединить два ряда в один график и нарисовать разные левые и правые оси для оси Y?
Как обычно в R можно один и тот же результат получить с помощью разных инструментов. Наиболее простой вариант следующий. С помощью того же параметра new можно “заставить” R нарисовать на месте уже нарисованного. Так как оба наших ряда полностью совпадают по оси Х (времени), то все будет выглядеть абсолютно нормально. Создадим специальную функцию для этого, чтобы можно было ее использовать в будущем для разных рядов:
Plot2Axis <- function(y1, y2, name1, name2) {
    y <- merge(y1, y2)
    plot.zoo(na.omit(y[, 1]), type = "l", col = "#3266cc", lwd = 2, main = "", 
        xlab = "Дата", ylab = "points", oma = c(4, 4, 2, 0.5))
    grid()  #нарисовать сеточку
    par(new = TRUE)  # параметр, который позволяет рисовать новый график на текущем графике
    plot.zoo(na.omit(y[, 2]), type = "l", col = "#db3a10", lwd = 2, xaxt = "n", 
        yaxt = "n", xlab = "", ylab = "", oma = c(4, 4, 2, 0.5))
    axis(4)  #построим ось второго графика слева
    legend("topright", col = c("#3266cc", "#db3a10"), lty = 1, lwd = 3, bty = "n", 
        legend = c(name1, name2), text.col = c("#3266cc", "#db3a10"))
}
# теперь можно использовать функцию,
# задав предварительно несколько
# глобальных параметров, чтобы график
# выглядел более привлекательно

par(bty = "l")  #  глобальный параметр - граница вокруг графика слева и снизу
par(las = 1)  #  глобальный параметр - текст пишется параллельно оси X
par(mar = c(2, 4, 1, 3))  #  глобальный параметр - границы между область графика и остальной частью

Plot2Axis(data$spx, data$brent, "S&P 500", "Brent")
plot of chunk unnamed-chunk-5
Примечание: данный пост создавался с помощью пакета knitr, который не всегда дружит с кириллицей в текущей реализации. Поэтому я не стал использовать надписи на кириллице в графиках. В самом R все отображается правильно.
-->