Найти пропущенные значения с помощью линейной интерполяции (время серии) - PullRequest
0 голосов
/ 14 января 2019

У меня есть эти data.frame, называемые df1, которые представляют каждый месяц в течение трех лет (36 строк х 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 х 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

Таким образом, я должен получить вывод data.frame с именем results из 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

Спасибо за помощь!

Ответы [ 2 ]

0 голосов
/ 14 января 2019

Для полноты картины, вот решение, которое использует data.table.

ОП предоставил точки данных для каждого месяца с 2015 по 2017 год. Он не определил день месяца, к которому относятся значения. Кроме того, он не уточнил, какой тип интерполяции он ожидает.

Итак, приведенные данные выглядят следующим образом (только v1 показано для простоты):

enter image description here

Обратите внимание, что месячное значение намеренно было присвоено первому дню месяца.

Существует различных способов для интерполяции данных. Мы рассмотрим два из них.

Кусочно-постоянная интерполяция

Поскольку указывается только одна точка данных в месяц, мы можем смело предположить, что значение является репрезентативным для каждого дня соответствующего месяца:

enter image description here

(нанесено с geom_step())

Для интерполяции используется базовая функция R approx(). approx() применяется ко всем столбцам значений v1, v2, v3 с помощью lapply().

Но сначала нам нужно превратить год-месяц в полную дату (включая день). Первый день месяца был выбран сознательно. Теперь точки данных в 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.

Результат может быть нанесен поверх данных точек.

enter image description here

кусочно-линейная интерполяция

Для рисования сегмента линии необходимо указать две точки. Чтобы нарисовать отрезки для 36 интервалов (месяцев), нам нужно 37 точек данных. К сожалению, ОП дала только 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

enter image description here

В образце набора данных значения на 2016 и 2017 годы довольно плоские. В любом случае, постоянная интерполяция за последний месяц не бросается в глаза.

0 голосов
/ 14 января 2019

Вы почти у цели. Есть только некоторые детали, которые следует добавить.

Прежде всего, у меня сложилось впечатление, что вы не указали значение года в ваших данных. Однако при работе с датами важно иметь значение года. Я полагаю, ваши данные должны выглядеть так:

     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"))  

Если вы предпочитаете список двухкадровых данных, то опция:

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