2011-11-29 78 views
56

我是R/ggplot新手。我想創建一個連續變量時間序列的geom_line圖,然後添加一個由事件組成的圖層。連續變量及其時間戳存儲在一個data.frame中,事件及其時間戳存儲在另一個data.frame中。R + ggplot:事件時間系列

我會真的喜歡做的事情就像在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

問題:

  • 這是非常稀疏事件OK,但如果有他們的集羣(如在日誌文件中經常發生的),它就會變得混亂。是否有一些技巧可以用來整齊地顯示一小段時間內發生的事件?我在考慮position_jitter,但對於我來說這很難實現。如果有很多這些事件,Google圖表會將這些事件「標誌」疊加在一起。

  • 我實際上不喜歡粘貼與連續測量顯示相同比例的事件數據。我寧願把它放在facet_grid中。問題是所有方面都必須來自相同的數據框架(不知道這是真的)。如果是的話,那也似乎並不理想(或者也許我只是想避免使用重塑?)

+6

有趣的情節:不要指望在共和黨總統掌權後找份工作! – James

+0

這只是最方便和可用的數據作爲例子 - 但是,它確實讓你覺得:-) – Angelo

回答

36

雖然我很喜歡@JD龍的回答,我會把一個是剛剛在R/GGPLOT2。

該方法是創建第二個事件數據集並使用它來確定位置。從@Angelo開始:

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

拉出事件(總統)數據並進行轉換。計算baselineoffset作爲將繪製的​​經濟數據的一部分。將底部(ymin)設置爲基準。這是棘手的部分來的地方。如果標籤太靠近,我們需要交錯。因此,確定相鄰標籤之間的間距(假定事件已排序)。如果它小於一定數量(我選擇了大約4年的數據),那麼請注意,該標籤需要更高。但它必須高於後面的那個,所以使用rle來獲得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\]") 

你可以小面,但它是棘手的不同尺度。另一種方法是組成兩張圖。有一些額外的操作必須完成,以確保繪圖具有相同的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)) 

+0

這是一個非常好的答案和一個很好的ggplot插圖。 –

+3

嗚呼!你和@JDLong之間,今天我學到了一些很好的功夫! – Angelo

+0

非常有用,感謝@Brian Diggs。有點不贊成。以下是代碼的更新版本:http://pastebin.com/sVAACtQe(不得不擺弄邊距,單調乏味 - 自然地隨意複製粘貼)。 – PatrickT

81

現在我喜歡ggplot儘可能未來的傢伙,但如果你想在谷歌財經類型圖表,爲什麼不直接使用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

哇!我甚至不知道R有一個googleVis包。 – Angelo

+10

你覺得幸福感增加了,不是嗎?看,圖不說謊! :) –

+0

預測:你會從該演示中獲得認真的反響。 –

1

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成plotly對象。

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