【问题标题】:Find missing values by linear interpolation (time serie)通过线性插值(时间序列)查找缺失值
【发布时间】:2019-01-15 15:55:57
【问题描述】:

我有这些 data.frame 称为 df1 代表三年内的每个月(36 行 x 4 列):

       Year Month       v1       v2       v3
1  2015     1 15072.73 2524.102 17596.83
2  2015     2 15249.54 2597.265 17846.80
3  2015     3 15426.35 2670.427 18096.78
4  2015     4 15603.16 2743.590 18346.75
5  2015     5 15779.97 2816.752 18596.72
6  2015     6 15956.78 2889.915 18846.69
7  2015     7 16133.59 2963.077 19096.67
8  2015     8 16310.40 3036.240 19346.64
9  2015     9 16487.21 3109.402 19596.61
10 2015    10 16664.02 3182.565 19846.58
11 2015    11 16840.83 3255.727 20096.56
12 2015    12 17017.64 3328.890 20346.53
13 2016     1 17018.35 3328.890 20347.24
14 2016     2 17019.05 3328.890 20347.94
15 2016     3 17019.76 3328.890 20348.65
16 2016     4 17020.47 3328.890 20349.36
17 2016     5 17021.17 3328.890 20350.06
18 2016     6 17021.88 3328.890 20350.77
19 2016     7 17022.58 3328.890 20351.47
20 2016     8 17023.29 3328.890 20352.18
21 2016     9 17024.00 3328.890 20352.89
22 2016    10 17024.70 3328.890 20353.59
23 2016    11 17025.41 3328.890 20354.30
24 2016    12 17026.12 3328.890 20355.01
25 2017     1 17023.94 3328.890 20352.83
26 2017     2 17021.76 3328.890 20350.65
27 2017     3 17019.58 3328.890 20348.47
28 2017     4 17017.40 3328.890 20346.29
29 2017     5 17015.22 3328.890 20344.11
30 2017     6 17013.04 3328.890 20341.93
31 2017     7 17010.86 3328.890 20339.75
32 2017     8 17008.68 3328.890 20337.57
33 2017     9 17006.50 3328.890 20335.39
34 2017    10 17004.32 3328.890 20333.21
35 2017    11 17002.14 3328.890 20331.03
36 2017    12 17002.14 3328.890 20331.03

我想对所有这些值进行插值,以获得每个月所有日子的插值。它们位于data.frame 中,称为df2 (1096 x 1)。

df2 看起来像:

  seq(start, end, by = "days")
1                   2015-01-01
2                   2015-01-02
3                   2015-01-03
4                   2015-01-04
5                   2015-01-05
6                   2015-01-06

通过这种方式,我应该获得一个名为 results 的输出 data.frame 1096 行(365 天(2015)+ 366 天(2016)+ 365 天(2017))和 4 列。

我试过approx:

results <- as.data.frame(approx(x = df1, y = NULL, xout = df2 ,
                             method = "linear"))

但它会返回:

         x  y
1 2015-01-01 NA
2 2015-01-02 NA
3 2015-01-03 NA
4 2015-01-04 NA
5 2015-01-05 NA
6 2015-01-06 NA

感谢您的帮助!

【问题讨论】:

  • “所以至少需要两个完整的 (x, y) 对(对于方法 =“线性”,否则一个)。从帮助。您应该提供 y。我不确定它是否会一次完成所有列,您应该使用lapply。考虑使用正常的回归函数,如 lmlmer 和定义的模型

标签: r dataframe time-series linear-interpolation


【解决方案1】:

为了完整起见,这里有一个使用data.table的解决方案。

OP 提供了 2015 年到 2017 年每个月的数据点。他没有定义这些值所归属的月份中的哪一天。此外,他还没有具体说明他期望的插值类型。

因此,给定的数据如下所示(为简单起见,仅显示v1):

请注意,故意将每月值分配给该月的第一天。

different ways 可以插入数据。我们将研究其中的两个。

分段常数插值

由于每个月只给出一个数据点,我们可以放心地假设该值代表了相应月份的每一天:

(使用geom_step() 绘制)

对于插值,使用基本 R 函数approx()。在lapply() 的帮助下,approx() 应用于所有值列 v1v2v3

但首先我们需要将年月转换为完整的日期(包括日)。本月的第一天是特意选择的。现在,df1 中的数据点归属于日期 2015-01-01 到 2017-12-01。请注意,2017-12-31 或 2018-01-01 没有给定值。

library(data.table)
library(magrittr)
# create date (assuming the 1st of month)
setDT(df1)[, date := as.IDate(paste(Year, Month, 1, sep = "-"))]
# create sequence of days covering the whole period
ds <- seq(as.IDate("2015-01-01"), as.IDate("2017-12-31"), by = "1 day")
# perform interpolation
cols = c("v1", "v2", "v3")
results <- df1[, c(.(date = ds), lapply(.SD, function(y) 
  approx(x = date, y = y, xout = ds, method = "constant", rule = 2)$y)), 
  .SDcols = cols]
results
            date       v1       v2       v3
   1: 2015-01-01 15072.73 2524.102 17596.83
   2: 2015-01-02 15072.73 2524.102 17596.83
   3: 2015-01-03 15072.73 2524.102 17596.83
   4: 2015-01-04 15072.73 2524.102 17596.83
   5: 2015-01-05 15072.73 2524.102 17596.83
  ---                                      
1092: 2017-12-27 17002.14 3328.890 20331.03
1093: 2017-12-28 17002.14 3328.890 20331.03
1094: 2017-12-29 17002.14 3328.890 20331.03
1095: 2017-12-30 17002.14 3328.890 20331.03
1096: 2017-12-31 17002.14 3328.890 20331.03

通过指定rule = 2approx() 被告知使用最后给定的值(2017-12-01 的值)来完成到 2017-12-31 的序列。

结果可以绘制在给定数据点的顶部。

分段线性插值

要绘制线段,必须给出两个点。为了绘制 36 个间隔(月)的线段,我们需要 37 个数据点。不幸的是,OP 只给出了 36 个数据点。我们需要一个额外的 2018-01-01 数据点来绘制上个月的线。

在这种情况下,一个选项是假设上个月的值是恒定的。这就是指定method = "linear"rule = 2approx() 所做的事情。

library(data.table)
library(magrittr)
# create date (assuming the 1st of month)
setDT(df1)[, date := as.IDate(paste(Year, Month, 1, sep = "-"))]
# create sequence of days covering the whole period
ds <- seq(as.IDate("2015-01-01"), as.IDate("2017-12-31"), by = "1 day")
# perform interpolation
cols = c("v1", "v2", "v3")
results <- df1[, c(.(date = ds), lapply(.SD, function(y) 
  approx(x = date, y = y, xout = ds, method = "linear", rule = 2)$y)), 
  .SDcols = cols]
results

            date       v1       v2       v3
   1: 2015-01-01 15072.73 2524.102 17596.83
   2: 2015-01-02 15078.43 2526.462 17604.89
   3: 2015-01-03 15084.14 2528.822 17612.96
   4: 2015-01-04 15089.84 2531.182 17621.02
   5: 2015-01-05 15095.54 2533.542 17629.08
  ---                                      
1092: 2017-12-27 17002.14 3328.890 20331.03
1093: 2017-12-28 17002.14 3328.890 20331.03
1094: 2017-12-29 17002.14 3328.890 20331.03
1095: 2017-12-30 17002.14 3328.890 20331.03
1096: 2017-12-31 17002.14 3328.890 20331.03

在示例数据集中,2016 年和 2017 年的值相当平坦。无论如何,上个月的常量插值并不引人注目。

【讨论】:

  • 你认为有可能固定每个月的第一天和最后一天,以获得一年中的所有天数吗?在您的回答中,结果只有 335 行而不是 365 行(缺少 12 月)
  • 我们需要n + 1 断点来定义n 间隔。您的示例数据集仅包含 12 个数据点,足以插入 11 个月。如果您要提供 2016 年 1 月 1 日的数据以及 2015 年的其他 12 个数据点,我们也可以插值 12 月。
  • 我刚刚编辑了帖子。你能解释一下你的过程吗
  • @Sebastien_H,我已经完全重写了我的答案。希望,我可以让我的观点更清楚。
【解决方案2】:

你快到了。只是有一些细节需要补充。

首先,我有一个印象,您从数据中省略了年份值。但是,在使用日期时具有年份值很重要。我想,你的数据应该是这样的:

     Year Month   v1      v2          v3
1     2015     1 15072.73 2524.102   17596.83
2     2015     2 15249.54 2597.265   17846.80
3     2015     3 15426.35 2670.427   18096.78
4     2015     4 15603.16 2743.590   18346.75
5     2015     5 15779.97 2816.752   18596.72
6     2015     6 15956.78 2889.915   18846.69
7     2015     7 16133.59 2963.077   19096.67
8     2015     8 16310.40 3036.240   19346.64
9     2015     9 16487.21 3109.402   19596.61
10    2015    10 16664.02 3182.565   19846.58
11    2015    11 16840.83 3255.727   20096.56
12    2015    12 17017.64 3328.890   20346.53

另一个问题是df1 给出的每月值隐含在每月的哪一天。假设今天是该月的第一天。那么可以得到解决方案

data_names <- c("v1", "v2", "v3")
res_set <- lapply(
    function(var_name) approx(
        x = as.Date(paste(df1$Year, df1$Month, "01", sep = "-")), 
        y = df1[, var_name], xout = df2), 
    X = data_names)
# name each item of the list to make further work simpler
names(res_set) <- data_names
print(str(res_set))

请注意,lapply() 的结果是一个列表。需要一些额外的工作来获得理想的格式。如果您需要所有变量的单个数据框,则可以使用:

res_df <- data.frame(x = df2, lapply(res_set,`[[`,  "y"))  

如果您更喜欢包含两列数据的 dframe 列表,则可以选择:

res_list <- lapply(res_set, as.data.frame)

【讨论】:

  • OP 说他希望 data.frame 有四列。您的结果是一个包含 3 个元素的列表,每个元素都有 2 个向量。被as.data.frame(res_set)变成了data.frame,还是6列。
  • 为什么我在运行 lapply 时会出现此错误:大约错误(x = as.Date(paste(df1$Year, df1$Month, "01", sep = "-")) , : (list) object 不能被强制输入 'double' 。我不明白意思
  • @Sebastien_H 这意味着在需要向量的地方提供了一个列表。我假设 df2 是一个向量。如果它是一个列表,则应使用 $ 或 [[.
猜你喜欢
  • 2016-01-16
  • 1970-01-01
  • 2020-10-23
  • 2018-05-14
  • 1970-01-01
  • 2016-07-28
  • 1970-01-01
  • 2018-06-27
  • 1970-01-01
相关资源
最近更新 更多