Подтвердить что ты не робот

R + ggplot: Временные ряды с событиями

Я новичок в R/ggplot. Я хотел бы создать график geom_line непрерывного переменного временного ряда, а затем добавить слой, состоящий из событий. Непрерывная переменная и ее временные метки сохраняются в одном кадре данных. События и их временные метки сохраняются в другом кадре данных.

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

google chart with events

Если целесообразно (?), я хотел бы использовать отдельные data.frames для каждого слоя (один для непрерывных переменных наблюдений, другой для событий).

После некоторых проб и ошибок это примерно так же близко, как я могу получить. Здесь я использую примеры данных из наборов данных, которые поставляются с ggplot. "экономика" содержит некоторые данные временного ряда, которые я хотел бы построить, а "президентский" содержит несколько событий (президентских выборов).

library(ggplot2)
data(presidential)
data(economics)

presidential <- presidential[-(1:3),]
yrng <- range(economics$unemploy)
ymin <- yrng[1]
ymax <- yrng[1] + 0.1*(yrng[2]-yrng[1])

p2 <- ggplot()
p2 <- p2 + geom_line(mapping=aes(x=date, y=unemploy), data=economics , size=3, alpha=0.5) 
p2 <- p2 + scale_x_date("time") +  scale_y_continuous(name="unemployed [1000's]")
p2 <- p2 + geom_segment(mapping=aes(x=start,y=ymin, xend=start, yend=ymax, colour=name), data=presidential, size=2, alpha=0.5)
p2 <- p2 + geom_point(mapping=aes(x=start,y=ymax, colour=name ), data=presidential, size=3) 
p2 <- p2 + geom_text(mapping=aes(x=start, y=ymax, label=name, angle=20, hjust=-0.1, vjust=0.1),size=6, data=presidential)
p2

my attempt

Вопросы:

  • Это нормально для очень редких событий, но если есть кластер из них (как это часто бывает в файле журнала), он становится беспорядочным. Есть ли какая-то техника, которую я могу использовать, чтобы аккуратно отображать кучу событий, происходящих за короткий промежуток времени? Я думал о position_jitter, но мне было очень трудно зайти так далеко. google charts складывает эти флаги событий друг над другом, если их много.

  • На самом деле мне не нравится вставлять данные событий в том же масштабе, что и дисплей непрерывного измерения. Я бы предпочел поставить его в facet_grid. Проблема в том, что все грани должны быть получены из одного и того же data.frame(не уверен, что это правда). Если это так, это также кажется не идеальным (или, может быть, я просто пытаюсь избежать изменения)?

4b9b3361

Ответ 1

Насколько мне нравится @JD Длинный ответ, я поставлю тот, который находится только в R/ggplot2.

Подходом является создание второго набора данных событий и использование этого для определения позиций. Начиная с того, что @Angelo:

library(ggplot2)
data(presidential)
data(economics)

Вытащите данные о событиях (президентских) и преобразуйте их. Вычислить baseline и offset как доли экономических данных, с которыми он будет построен. Установите нижнюю (ymin) базовую линию. Вот тут и возникает сложная часть. Мы должны иметь возможность размещать метки, если они слишком близко друг к другу. Поэтому определите расстояние между соседними метками (предполагается, что события отсортированы). Если это меньше некоторой суммы (я выбрал около 4 лет для этой шкалы данных), то обратите внимание, что эта метка должна быть выше. Но он должен быть выше, чем тот, который после него, поэтому используйте rle, чтобы получить длину TRUE (то есть, должен быть выше) и вычислить вектор смещения, используя это (каждая строка TRUE должна отсчитывать вниз от его длины до 2, FALSE находятся только на расстоянии 1). Используйте это, чтобы определить верхнюю часть баров (ymax).

events <- presidential[-(1:3),]
baseline = min(economics$unemploy)
delta = 0.05 * diff(range(economics$unemploy))
events$ymin = baseline
events$timelapse = c(diff(events$start),Inf)
events$bump = events$timelapse < 4*370 # ~4 years
offsets <- rle(events$bump)
events$offset <- unlist(mapply(function(l,v) {if(v){(l:1)+1}else{rep(1,l)}}, l=offsets$lengths, v=offsets$values, USE.NAMES=FALSE))
events$ymax <- events$ymin + events$offset * delta

Вводя это вместе в сюжет:

ggplot() +
    geom_line(mapping=aes(x=date, y=unemploy), data=economics , size=3, alpha=0.5) +
    geom_segment(data = events, mapping=aes(x=start, y=ymin, xend=start, yend=ymax)) +
    geom_point(data = events, mapping=aes(x=start,y=ymax), size=3) +
    geom_text(data = events, mapping=aes(x=start, y=ymax, label=name), hjust=-0.1, vjust=0.1, size=6) +
    scale_x_date("time") +  
    scale_y_continuous(name="unemployed \[1000's\]")

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

xrange = range(c(economics$date, events$start))

p1 <- ggplot(data=economics, mapping=aes(x=date, y=unemploy)) +
    geom_line(size=3, alpha=0.5) +
    scale_x_date("", limits=xrange) +  
    scale_y_continuous(name="unemployed [1000's]") +
    opts(axis.text.x = theme_blank(), axis.title.x = theme_blank())

ylims <- c(0, (max(events$offset)+1)*delta) + baseline
p2 <- ggplot(data = events, mapping=aes(x=start)) +
    geom_segment(mapping=aes(y=ymin, xend=start, yend=ymax)) +
    geom_point(mapping=aes(y=ymax), size=3) +
    geom_text(mapping=aes(y=ymax, label=name), hjust=-0.1, vjust=0.1, size=6) +
    scale_x_date("time", limits=xrange) +
    scale_y_continuous("", breaks=NA, limits=ylims)

#install.packages("ggExtra", repos="http://R-Forge.R-project.org")
library(ggExtra)

align.plots(p1, p2, heights=c(3,1))

Ответ 2

Теперь мне нравится ggplot так же, как и следующий парень, но если вы хотите составить диаграммы типа Google Finance, почему бы просто не сделать это с помощью API-интерфейсов Google?!? Вам это понравится:

install.packages("googleVis")
library(googleVis)

dates <- seq(as.Date("2011/1/1"), as.Date("2011/12/31"), "days")
happiness <- rnorm(365)^ 2
happiness[333:365] <- happiness[333:365]  * 3 + 20
Title <- NA
Annotation <- NA
df <- data.frame(dates, happiness, Title, Annotation)
df$Title[333] <- "Discovers Google Viz"
df$Annotation[333] <- "Google Viz API interface by Markus Gesmann causes acute increases in happiness."

### Everything above here is just for making up data ### 
## from here down is the actual graphics bits        ###
AnnoTimeLine  <- gvisAnnotatedTimeLine(df, datevar="dates",
                                       numvar="happiness", 
                                       titlevar="Title", annotationvar="Annotation",
                                       options=list(displayAnnotations=TRUE,
                                                    legendPosition='newRow',
                                                    width=600, height=300)
                                       )
# Display chart
plot(AnnoTimeLine) 
# Create Google Gadget
cat(createGoogleGadget(AnnoTimeLine), file="annotimeline.xml")

и он производит эту фантастическую диаграмму:

enter image description here

Ответ 3

Plotly - это простой способ сделать ggplots интерактивным. Чтобы отображать события, принуждайте их к факторам, которые могут отображаться как эстетические, как цвет.

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

введите описание изображения здесь

Вот код для создания ggplot:

# load data    
data(presidential)
data(economics)

# events of interest
events <- presidential[-(1:3),]

# strip year from economics and events data frames
economics$year = as.numeric(format(economics$date, format = "%Y")) 

# use dplyr to summarise data by year
#install.packages("dplyr")
library(dplyr)
econonomics_mean <- economics %>% 
  group_by(year) %>% 
  summarise(mean_unemployment = mean(unemploy))

# add president terms to summarized data frame as a factor
president <- c(rep(NA,14), rep("Reagan", 8), rep("Bush", 4), rep("Clinton", 8), rep("Bush", 8), rep("Obama", 7))
econonomics_mean$president <- president

# create ggplot
p <- ggplot(data = econonomics_mean, aes(x = year, y = mean_unemployment)) +
  geom_point(aes(color = president)) +
  geom_line(alpha = 1/3)

Для преобразования ggplot в объект plotly требуется только одна строка кода.

# make it interactive!
#install.packages("plotly")
library(plotly)
ggplotly(p)