使用 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

在这个例子中,frequency6 并且开始是 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 对象转换为数据框的主要内容,如果未能解决你的问题,请参考以下文章

R语言 数据框时间列处理,时区转化

SQL查询丢失信息

使用MySQL伪列生成序号

如何使用 Java 将 base 64 图像插入 BLOB 列? (甲骨文)

Base64 字符串的实体框架列类型

我们可以在“native-base”的页脚组件的列中设置两个按钮,还是可以设置“native-base”的页脚高度?