R + ggplot:包含事件的时间序列

时间:2011-11-29 20:53:31

标签: r ggplot2 visualization graphing

我是R / ggplot新手。我想创建一个连续变量时间序列的geom_line图,然后添加一个由事件组成的图层。连续变量及其时间戳存储在一个data.frame中,事件及其时间戳存储在另一个data.frame中。

真正喜欢做的事情就像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,但这对我来说真的很难。谷歌图表将这些事件“标志”叠加在一起,如果它们有很多的话。

  • 我实际上不喜欢以与连续测量显示相同的比例粘贴事件数据。我宁愿把它放在facet_grid中。问题是所有facet都必须来自相同的data.frame(不确定是否为真)。如果是这样,那似乎也不理想(或者我只是想避免使用重塑?)

4 个答案:

答案 0 :(得分:83)

现在我和下一个人一样喜欢ggplot,但是如果你想制作Google财经类型的图表,为什么不用Google图形API呢?!?你会喜欢这个:

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

答案 1 :(得分:36)

尽管我喜欢@JD Long的答案,但我会把它放在R / ggplot2中。

方法是创建第二个事件数据集并使用它来确定位置。从@Angelo开始:

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

拉出事件(总统)数据并进行转换。将baselineoffset计算为将绘制的​​经济数据的一部分。将底部(ymin)设置为基线。这是棘手的部分。如果标签太靠近,我们需要能够错开标签。因此,确定相邻标签之间的间距(假设事件已排序)。如果它小于某个数量(我为这个数据量选择了大约4年),那么请注意该标签需要更高。但它必须高于它之后的那个,所以使用rle来获取TRUE的长度(也就是说,必须更高)并使用它来计算偏移向量(每个字符串TRUE必须从其长度倒数到2,FALSE s只是偏移量为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\]")

你可能会受到影响,但不同规模下它很棘手。另一种方法是组成两个图。为了确保图表具有相同的x范围,使标签全部适合下图,并消除上图中的x轴,必须进行一些额外的小动作。

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 :(得分:3)

Plotly是让ggplots互动的简单方法。要显示事件,请将它们强制转换为可以显示为美学的因素,如颜色。

最终结果是可以将光标拖过的绘图。这些图显示了感兴趣的数据:

enter image description here

以下是制作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变成一个绘图对象。

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

答案 3 :(得分:2)

考虑到您正在绘制时间序列和定性信息,大多数经济学书籍都使用绘图区域来表示数据的结构变化或事件,因此我建议使用以下内容:

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

ggplot() +
  geom_rect(aes(xmin = start,
                xmax = end,
                ymin = 0, ymax = Inf,
                fill = name),
            data = presidential,
            show.legend = F) +
  geom_text(aes(x = start+500,
                y = 2000,
                label = name,
                angle = 90),
            data = presidential) +
  geom_line(aes(x = date, y = unemploy),
            data= economics) +
  scale_fill_brewer(palette = "Blues") +
  labs(x = "time", y = "unemploy")

enter image description here