将时间序列数据从宽格式转换为长格式(用于绘图)

23

我有一个数据帧,其中包含多个时间序列的收益率,存储在各自的列中。

第一列包含日期,随后的列是独立的时间序列,每个都有一个名称。列标题是变量名。

## I have a data frame like this
t <- seq(as.Date('2009-01-01'),by='days',length=10)
X <- rnorm(10,0,1)
Y <- rnorm(10,0,2)
Z <- rnorm(10,0,4)

dat <- data.frame(t,X,Y,Z)

## which appears as
           t          X          Y         Z
1 2009-01-01 -1.8763317 -0.1885183 -6.655663
2 2009-01-02 -1.3566227 -2.1851226 -3.863576
3 2009-01-03 -1.3447188  2.4180249 -1.543931

我希望在网格中将每个时间序列作为单独的线条绘制到不同的图表中,并使用变量名标记每个图表。

要使用lattice绘制此图,数据必须以长格式呈现,如下所示:

           t symbol       price
1 2009-01-01      X -1.8763317
2 2009-01-02      Y -0.1885183
2 2009-01-02      Z -6.655663

什么样的函数调用能够实现这个功能?

1
以防有人想在下面找到未提及的答案,这是使用基本R中的reshape函数的解决方案: reshape(dat, direction = "long", varying = 2:4, v.names = "price", idvar = "t", timevar = "symbol", times = names(dat)[2:4], new.row.names = 1:30) - josep maria porrà
5个回答

18
你还可以使用'reshape'库中的melt()函数(我认为比reshape()本身更容易使用) - 这样就可以省去添加时间列的额外步骤...
> library(reshape)
> m <- melt(dat,id="t",variable_name="symbol")
> names(m) <- sub("value","price",names(m))
> head(m)
           t symbol       price
1 2009-01-01      X -1.14945096
2 2009-01-02      X -0.07619870
3 2009-01-03      X  0.01547395
4 2009-01-04      X -0.31493143
5 2009-01-05      X  1.26985167
6 2009-01-06      X  1.31492397
> class(m$t)
[1] "Date"
> library(lattice)                                                              
> xyplot( price ~ t | symbol, data=m ,type ="l", layout = c(1,3) )

对于这个特定的任务,我建议使用“zoo”库,这不需要你重新整理数据框:

> library(zoo)                                                                  
> zobj <- zoo(dat[,-1],dat[,1])                                                 
> plot(zobj,col=rainbow(ncol(zobj))) 

R开发者/贡献者(在这种情况下是Gabor和Hadley)赐予我们许多伟大的选择。(当然不能忘记lattice包的Deepayan)


Melt正是我正在寻找的。Stephen,您能否编辑您的答案以包括最后一个绘图步骤? - medriscoll
1
在浪费了一个小时的“stats::reshape()”函数之后,我不得不想知道一些编写R函数的人在服用什么药物。melt()很棒。(再次感谢Hadley) - forkandwait

18

来自tidyr gather帮助页面:

示例

library(tidyr)
library(dplyr)
# From https://dev59.com/1HM_5IYBdhLWcg3w4njb
stocks <- data.frame(
  time = as.Date('2009-01-01') + 0:9,
  X = rnorm(10, 0, 1),
  Y = rnorm(10, 0, 2),
  Z = rnorm(10, 0, 4)
)

gather(stocks, stock, price, -time)
stocks %>% gather(stock, price, -time)

14
有趣。看起来tidyr gather帮助页面在递归地链接到这个问题和答案。 - running.t
在这个 gather() 表达式中,-time 是什么意思? - T X
@TX 意味着排除时间列。 - zx8754

11
如果是多变量时间序列,请考虑使用名为zoo的软件包将其存储为zoo对象。这样可以使索引、合并和子集操作更加容易,详见zoo vigettes。
但是,由于你问到了lattice图形,这也是可行的。在此示例中,我们构建了一个简单的“长格式”数据框,其中包括日期列、值列'val'和变量id列'var'。
> set.seed(42)
> D <- data.frame(date=rep(seq(as.Date("2009-01-01"),Sys.Date(),by="week"),2),\
                  val=c(cumsum(rnorm(30)), cumsum(rnorm(30))), \
                  var=c(rep("x1",30), rep("x2",30)))

基于该数据集,按照您的描述进行绘图可以通过使用lattice包中的xyplot函数完成,请求绘制“变量分组后给定数据的值”的图表,在每个面板中打开线条:

> library(lattice)
> xyplot(val ~ date | var, data=D, panel=panel.lines)

5

对于一个数据框 'temp',其中第一列是日期,其他每一列都有值:

> par(mfrow=c(3,4)) # 3x4 grid of plots
> mapply(plot,temp[,-1],main=names(temp)[-1],MoreArgs=list(x=temp[,1],xlab="Date",type="l",ylab="Value") )

3

感谢大家的回答,Dirk的回答很准确。

缺少的一步是使用“stack()”函数将数据框从宽格式转换为长格式。我知道可能有更简单的方法可以使用reshape()函数完成,如果有人想发布示例,我会很高兴看到。

所以这就是我最终采取的做法,使用问题中提到的“dat”数据框:

## use stack() to reshape the data frame to a long format
## <time> <stock> <price>
stackdat <- stack(dat,select=-t) 
names(stackdat) <- c('price','symbol')

## create a column of date & bind to the new data frame
nsymbol <- length(levels(stackdat$symbol))  
date <- rep(dat$t, nsymbol)  
newdat <- cbind(date,stackdat)

## plot it with lattice
library(lattice)
xyplot(price ~ date | symbol,  ## model conditions on 'symbol' to lattice
       data=newdat,            ## data source
       type='l',               ## line
       layout=c(nsymbol,1))    ## put it on a single line

## or plot it with ggplot2
library(ggplot2)
qplot(date, price, data = newdat, geom="line") + facet_grid(. ~ symbol)

2
另请参阅重塑包中的melt函数,用于将宽格式转换为长格式。 - hadley

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接