使用 Date/POSIXct 列(base R)将 ts 对象转换为数据框
Posted
技术标签:
【中文标题】使用 Date/POSIXct 列(base R)将 ts 对象转换为数据框【英文标题】:Convert ts object to dataframe with Date/POSIXct column (base R) 【发布时间】:2022-01-15 10:46:12 【问题描述】:我有一个时间序列,我想将其转换为一个数据帧,其中包含包含值的列和一个包含 ttime 的 Date/POSIXct 列。可重现的例子:
data_ts <- ts(matrix(1:14, ncol= 2), start= c(2000, 1), frequency= 6)
预期输出是:
data_df <- data.frame(Series_1= 1:7, Series_2= 8:14, Date= seq(as.Date("2000/1/1"), length.out= nrow(data_ts)*2, by= "month")[seq(1, nrow(data_ts)*2, 2)])
data_df
Series_1 Series_2 Date
1 1 8 2000-01-01
2 2 9 2000-03-01
3 3 10 2000-05-01
4 4 11 2000-07-01
5 5 12 2000-09-01
6 6 13 2000-11-01
7 7 14 2001-01-01
在这个例子中,frequency
是 6
并且开始是 c(2000, 1)
但在我的用例中,我事先不知道这些参数。事实上,frequency
也可能大到我指的是小时/分钟或秒(例如frequency= 365*24*60*60
)。如果频率以年/月/周/天为单位,Date
就足够了,但是对于小时/分钟/秒,该列当然必须更改(更改为 POSIXct
)。所以我正在尝试一个通用的解决方案。我希望它是基础 R。
有一个similar 问题,但答案有一列日期为十进制数字。相反,我需要一个实际的 Date/PSIXct 列。
【问题讨论】:
如果你对 lubridate 没问题transform(as.data.frame(data_ts), Date = as.Date(date_decimal(as.numeric(time(data_ts)))))
@akrun 谢谢,但我的主管要求我坚持使用基础 R。
@G.Grothendieck 我们可以简单地将频率从 365*24*60*60 更改为 365.25*24*60*60 以处理闰年(请参阅here)。无论如何,根据上下文,Rob Hyndman 还建议使用frequency=7 来获取每日数据,在这种情况下,我的尝试没有任何意义,因为我的方法总是参考年份单位和日期。我想我必须重新提出这个问题。但是将频率限制为(多个)月不是一种选择。
@G.Grothendieck 是否可以使用 365.25*24*60*60 而不是 365*24*60*60 因为闰年或不是因为“ ts 仅适用于固定/常数我猜每年的积分数”是您必须与 Rob Hyndman 讨论的问题。我坚持他的描述,因为否则我看不到如何定义一年中的天/小时/分钟/秒的解决方案。是的,“如果它有一个年度频率,你不能只说它是 7”。我的意思是“根据上下文,Rob Hyndman 还建议对每日数据使用频率 = 7”。
我注意到这条评论 - “因为 ***.com/questions/70476759/...而删除我的帐户”。只是想告诉您,您可以使用标记将您的问题与您的帐户取消关联,从而消除负面声誉影响,因此没有理由删除您的帐户。
【参考方案1】:
我想出了一种方法,如果频率是整月、几周或几天,则使用seq
函数,选择相应的by
参数("month"
、"week"
或"day"
)。对于频率不是整月、一周或一天的情况,我计算了频率对应的秒数。比如频率365*24
表示我们需要进入60*60=3600
秒级(因为365*24
表示小时,60*60
秒级是小时级),以此类推。
这一切都提供了以下相当长的解决方案,但它适用于我迄今为止尝试过的所有情况:
ts_to_df <- function(data)
ts_freq <- frequency(data)
# Works for univariate and multivariate time series:
if(is.null(dim(data)))
ts_length <- length(data)
else
ts_length <- nrow(data)
# For full month.
if(ts_freq %in% c(1, 2, 3, 4, 6, 12))
n_months <- 12/ ts_freq
first_month <- as.Date(paste(start(data)[1],
start(data)[2]*n_months-(n_months-1), "1", sep= "/"))
ts_date <- seq(first_month, length.out= ts_length*n_months, by= "month")
ts_date <- ts_date[seq(1, length(ts_date), by= n_months)]
# For full weeks.
else if(ts_freq == 52)
first_week <- seq(as.Date(paste(start(data)[1], "/1/1", sep="")), length.out= start(data)[2], by= "week")[start(data)[2]]
ts_date <- seq(first_week, length.out= ts_length, by= "week")
# For full days.
else if(ts_freq == 365 | ts_freq == 366)
first_day <- as.Date(paste(start(data)[1], "/1/", start(data)[2], sep=""))
ts_date <- seq(first_day, length.out= ts_length, by= "day")
# All other cases.
else
sec_year <- 365*24*60*60
freq_fraction <- 1/ts_freq
sec_steps <- sec_year*freq_fraction
first_sec <- as.POSIXct(paste(start(data)[1], "-01-01 00:00:01", sep="")) + sec_steps*start(data)[2] - sec_steps
ts_date <- first_sec + sec_steps *0:(ts_length-1)
# Make a data.frame.
data_df <- data.frame(as.matrix(data))
data_df$date <- ts_date
data_df
以周为频率将函数应用于ts
:
ts_to_df(ts(matrix(1:14, ncol= 2), start= c(2000, 1), frequency= 52))
Series.1 Series.2 date
1 1 8 2000-01-01
2 2 9 2000-01-08
3 3 10 2000-01-15
4 4 11 2000-01-22
5 5 12 2000-01-29
6 6 13 2000-02-05
7 7 14 2000-02-12
现在以分钟为频率的ts
,从 2009 年的第 10 分钟开始:
ts_to_df(ts(matrix(1:14, ncol= 2), start= c(2009, 10), frequency= 365*24*60))
Series.1 Series.2 date
1 1 8 2009-01-01 00:09:01
2 2 9 2009-01-01 00:10:01
3 3 10 2009-01-01 00:11:01
4 4 11 2009-01-01 00:12:01
5 5 12 2009-01-01 00:13:01
6 6 13 2009-01-01 00:14:01
7 7 14 2009-01-01 00:15:01
等等……
【讨论】:
【参考方案2】:我认为最简单的方法是使用 tsbox 和 base R data.frames。
library(tsbox)
data_ts = ts(matrix(1:14, ncol= 2), start= c(2000, 1), frequency= 6)
ts_df = ts_df(data_ts)
ts_df = dcast(ts_df, time~id, value.var=value)
输出:
> ts_dt
time Series 1 Series 2
1: 2000-01-01 1 8
2: 2000-03-01 2 9
3: 2000-05-01 3 10
4: 2000-07-01 4 11
5: 2000-09-01 5 12
6: 2000-11-01 6 13
7: 2001-01-01 7 14
如果您只使用基本 R,我不知道一个非常方便的解决方案,但这个社区中有比我更好的 R 编码器,也许有人知道没有几十行的解决方案。
更新:
仅使用基础 R 进行尝试。如果有几个月:
data_ts = ts(matrix(1:14, ncol= 2), start= c(2000, 1), frequency= 6)
df_ts = data.frame(data_ts)
df_ts$date = as.numeric(time(data_ts))
df_ts$date = as.Date(paste0(floor(df_ts$date), "-",
sprintf("%02d", 1+round((df_ts$date-floor(df_ts$date))*12)), "-01"))
输出:
> df_ts
Series.1 Series.2 date
1 1 8 2000-01-01
2 2 9 2000-03-01
3 3 10 2000-05-01
4 4 11 2000-07-01
5 5 12 2000-09-01
6 6 13 2000-11-01
7 7 14 2001-01-01
如果有几周:
df_ts = data.frame(data_ts)
df_ts$week = as.numeric(time(data_ts))
df_ts$week = paste0(floor(df_ts$week), "-",
sprintf("%02d", 1+round((df_ts$week-floor(df_ts$week))*52)))
输出:
Series.1 Series.2 week
1 1 15 2000-01
2 2 16 2000-02
3 3 17 2000-03
4 4 18 2000-04
5 5 19 2000-05
6 6 20 2000-06
7 7 21 2000-07
8 8 22 2000-08
9 9 23 2000-09
10 10 24 2000-10
11 11 25 2000-11
12 12 26 2000-12
13 13 27 2000-13
14 14 28 2000-14
如果有天:
data_ts = ts(matrix(1:20, ncol= 2), start= c(2000, 1, 1), frequency= 365)
df_ts = data.frame(data_ts)
df_ts$date = time(data_ts)
df_ts$date = as.Date(paste0(floor(df_ts$date),
"-",
sprintf("%02d", 1+round((df_ts$date-floor(df_ts$date))*365)),
"-01"), "%Y-%j")
输出:
> df_ts
Series.1 Series.2 date
1 1 11 2000-01-01
2 2 12 2000-01-02
3 3 13 2000-01-03
4 4 14 2000-01-04
5 5 15 2000-01-05
6 6 16 2000-01-06
7 7 17 2000-01-07
8 8 18 2000-01-08
9 9 19 2000-01-09
10 10 20 2000-01-10
【讨论】:
以上是关于使用 Date/POSIXct 列(base R)将 ts 对象转换为数据框的主要内容,如果未能解决你的问题,请参考以下文章