通过线性插值查找缺失值(时间序列)
Find missing values by linear interpolation (time serie)
我有这些 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"))
但是 returns:
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
感谢帮助!
你快到了。只有一些细节需要补充。
首先,我的印象是,您在数据中省略了年份值。但是,在处理日期时使用年份值很重要。我想,你的数据应该是这样的:
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"))
如果您更喜欢两列数据 dframes 的列表,那么一个选项是:
res_list <- lapply(res_set, as.data.frame)
为了完整起见,这是一个使用 data.table
.
的解决方案
OP 提供了 2015 年至 2017 年每个月的数据点。他没有定义这些值归因于哪一天。此外,他没有指定他期望的插值类型。
因此,给定的数据如下所示(为简单起见仅显示 v1
):
请注意,有意将每月值分配给该月的第一天。
有different ways个插值数据。我们将看看其中的两个。
分段常数插值
由于每个月只给出一个数据点,我们可以安全地假设该值代表相应月份的每一天:
(用geom_step()
绘制)
对于插值,使用基本 R 函数 approx()
。 approx()
在 lapply()
.
的帮助下应用于所有值列 v1
、v2
、v3
但首先我们需要把年月变成一个完整的日期(包括日)。这个月的第一天是特意选择的。现在,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 = 2
,approx()
被告知使用最后给定的值(2017-12-01 的值)来完成截至 2017-12-31 的序列。
结果可以绘制在给定数据点之上。
分段线性插值
要绘制线段,必须给出两个点。为了绘制 36 个间隔(月)的线段,我们需要 37 个数据点。不幸的是,OP 只给出了 36 个数据点。我们需要 2018-01-01 的额外数据点来为上个月画一条线。
在这种情况下,一种选择是假设上个月的值保持不变。这就是 approx()
在指定 method = "linear"
和 rule = 2
时所做的。
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 年的值相当平坦。反正最近一个月的常量插值不抢眼
我有这些 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"))
但是 returns:
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
感谢帮助!
你快到了。只有一些细节需要补充。
首先,我的印象是,您在数据中省略了年份值。但是,在处理日期时使用年份值很重要。我想,你的数据应该是这样的:
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"))
如果您更喜欢两列数据 dframes 的列表,那么一个选项是:
res_list <- lapply(res_set, as.data.frame)
为了完整起见,这是一个使用 data.table
.
OP 提供了 2015 年至 2017 年每个月的数据点。他没有定义这些值归因于哪一天。此外,他没有指定他期望的插值类型。
因此,给定的数据如下所示(为简单起见仅显示 v1
):
请注意,有意将每月值分配给该月的第一天。
有different ways个插值数据。我们将看看其中的两个。
分段常数插值
由于每个月只给出一个数据点,我们可以安全地假设该值代表相应月份的每一天:
(用geom_step()
绘制)
对于插值,使用基本 R 函数 approx()
。 approx()
在 lapply()
.
v1
、v2
、v3
但首先我们需要把年月变成一个完整的日期(包括日)。这个月的第一天是特意选择的。现在,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 = 2
,approx()
被告知使用最后给定的值(2017-12-01 的值)来完成截至 2017-12-31 的序列。
结果可以绘制在给定数据点之上。
分段线性插值
要绘制线段,必须给出两个点。为了绘制 36 个间隔(月)的线段,我们需要 37 个数据点。不幸的是,OP 只给出了 36 个数据点。我们需要 2018-01-01 的额外数据点来为上个月画一条线。
在这种情况下,一种选择是假设上个月的值保持不变。这就是 approx()
在指定 method = "linear"
和 rule = 2
时所做的。
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 年的值相当平坦。反正最近一个月的常量插值不抢眼