一行代码对日期插值

2020-10-23 11:47:21 浏览数 (1)

题引入

对日期进行插值是一项非常常见的任务。很多时候我们手头的时间序列都是不完整的,当中总会因为这样那样的原因漏了几天的观测,例如股票停牌了,观测仪器坏了,值班工人生病了等等。在分析时,我们为了获得完整的时间序列就需要“插入”那些丢失的日期。

举一个例子:

这个数据集中有5行观测,2组分类(id等于1和2)。我们看到每个id对应的date都是有缺失的,例如从2001-01-09直接跳到了2001-01-12,当中少了10号和11号。

如何只用一行代码就高效优美地把这些缺失的日期补上呢?

附:生成样例数据集的文件:

# sample dataset

# id变量用于分组

dt <- data.table(id = c(1, 1, 1, 2, 2), date = c(as.Date("2000-01-09"), as.Date("2000-01-12"), as.Date("2000-01-14"), as.Date("2000-02-09"), as.Date("2000-02-12")), val = rnorm(5))

本文需要用到data.table包!

况1:每个group起讫时间相等

首先来说第一种情况,在这种情况下,每个id都对应着“相同”的日期起讫点,例如,全都是从2000-01-08至2000-02-13。此时,我们相当于要构造出一个“平衡的”面板数据。

解决思路是运用data.table包的merge功能。首先我们建立一个CJ(cross join)数据集,这个数据集包含每个id所对应的“完整”日期

# 建立“完整”的日期序列

CJ <- CJ(id = unique(dt$id),

date = seq(as.Date("2000-01-08"),

as.Date("2000-02-13"),

by = "day"))

CJ数据集长这个样子(节选前11行和后11行):

我们看到CJ数据集中,每个id所对应的时间都被填充完整了。

(在建立CJ数据集的过程中,我们使用了seq函数来建立完整的时间序列)

接下来,我们把CJ数据集merge回原来的数据集dt。在merge的过程中,我们指定id和date变量必须匹配,也即on = .(id, date)语句的作用:

# 把CJ函数merge回原始数据集

dt[CJ, on = .(id, date), nomatch = NA]

结果为:

我们看到,原数据集存在观测的那些日期,val值都被保留,而被插入的那些日期,val是NA。当然,我们可以修改上一条语句中的nomatch参数把填充指改成任意值,例如0。

况2:每个group起讫时间不等

另一种情况是每个group的起讫时间不等。例如,在我们的样例数据集sample中,id=1的观测对应的日期最小值的为01-08,最大值为01-14,而我们希望填充这两个日期“之间”的所有值。同理,对于id=2的观测,日期最大值为02-09,最小值为02-12,我希望填充就是02-10,02-11这两天。

思路和情况1类似,我们先构造CJ数据集,只不过在这里我们seq函数的起讫点不再是固定值,而是每个id对应日期的最大值与最小值:

# 建立完整的日期序列

# 注意min和max函数的作用

CJ <- dt[, .(date = seq(min(date),

max(date), by = "day")),

keyby = id]

接下来我们把CJ数据集merge回dt数据集:

# 把CJ数据集merge回dt数据集

dt[CJ, on = .(id, date), nomatch = NA]

结果是:

大功告成!

等等,你不是说可以在一行当中搞定的吗?当然没问题,以上文提到的第二种情况为例,我们可以把两行合并为一行:

# 把两行代码合并成一行

dt[dt[, .(date = seq(min(date),

max(date), by = "day")),

keyby = id],

on = .(id, date),

nomatch = NA]

这也是大猫喜欢data.table的一个原因:由于语法的灵活性,可以少生成很多中间数据集,这样也就不用绞尽脑汁为那些中间数据集命名了。处女座无数次为了给数据集取一个合适的名字心力交瘁……

期预告

根据官网公告,Microsoft R Open 3.4版本将会“coming soon in May”,大猫会在第一时间给大家发布号外~

0 人点赞