R Вычисление ежедневного тренда за несколько лет

Я хочу рассчитать тренд каждого дня за несколько лет. Например, тенденция 1 мая с 2000 по 2010 год. Вот мой тестовый кадр данных:

library(lubridate)
date_list = seq(ymd('2000-01-15'),ymd('2010-09-18'),by='day')
testframe = data.frame(Date = date_list)
testframe$Day = substr(testframe$Date, start = 6, stop = 10)
testframe$V1 = rnorm(3900)
testframe$V2 = rnorm(3900)
testframe$V3 = seq(from = 10, to = 25, length.out = 3900)
testframe$V4 = seq(from = 5, to = 45, length.out = 3900)

V1-V4 являются значениями. В testframe$Day я уже вырезал день, чтобы использовать его для группировки строк. Я знаю, что aggregate хорошо подходит для такой группировки, но я совершенно не знаю, как совместить это с линейной моделью.

В конце концов, я хотел бы иметь фрейм данных, в котором есть столбец, содержащий каждый отдельный день (без года, конечно), и столбцы, содержащие тренд/наклон значений от V1 до V4.

Есть идеи?

ОБНОВИТЬ:

Чтобы было понятнее. Я хочу, чтобы вывод выглядел так (тенденции случайны)

Day       V1 Trend   V2 Trend    V3 Trend   V4 Trend
01-01     +0.3          +0.4      +0.9        +0.5
01-02     +0.5          +0.3      +0.8        +0.4
01-03     -0.1          -0.2      +1.0        -0.3
01-04     +0.7          -0.7      +0.9        +0.9
......
......
12-30    -0.3           -0.4      +0.5        +0.8
12-31    -0.7           -0.3      +0.6        +0.9

p-значения, Intercept и все остальное было бы здорово иметь.

Я нашел этот пример, но его все еще нет на выходе, который я хочу иметь:

#Add year for lm    
testframe$Year = as.numeric(format(testframe$Date,'%Y'))
library(plyr)
# Break up d by state, then fit the specified model to each piece and
# return a list
models <- dlply(testframe, "Day", function(df) 
  lm(Year ~ V4, data = df))

# Apply coef to each model and return a data frame
ldply(models, coef)

# Print the summary of each model
l_ply(models, summary, .print = TRUE)

person Mr.Spock    schedule 13.06.2019    source источник


Ответы (2)


Из вашего вывода видно, что для каждого Day вы хотите построить линейную модель формы V ~ Year для каждого V1, V2, V3, V4.

Вот dplyr подход:

library(lubridate)
library(dplyr)

set.seed(23)  # for reproducibility

# data (using your code)
date_list = seq(ymd('2000-01-15'),ymd('2010-09-18'),by='day')
testframe = data.frame(Date = date_list)
testframe$Day = substr(testframe$Date, start = 6, stop = 10)
testframe$V1 = rnorm(3900)
testframe$V2 = rnorm(3900)
testframe$V3 = seq(from = 10, to = 25, length.out = 3900)
testframe$V4 = seq(from = 5, to = 45, length.out = 3900)


testframe %>% 
  mutate(Year = year(Date)) %>%  # extract the year
  select(-Date) %>%              # remove the Date column
  group_by(Day) %>%              # for each day
  summarise_at(vars(matches("V")), ~lm(. ~ Year)$coefficients[2])  # build a model and keep the slope

# # A tibble: 366 x 5
#   Day        V1      V2    V3    V4
#   <chr>   <dbl>   <dbl> <dbl> <dbl>
# 1 01-01  0.108   0.0554  1.41  3.75
# 2 01-02 -0.0543 -0.103   1.41  3.75
# 3 01-03 -0.143  -0.0176  1.41  3.75
# 4 01-04  0.146  -0.0232  1.41  3.75
# 5 01-05 -0.154  -0.0533  1.41  3.75
# 6 01-06 -0.268   0.0470  1.41  3.75
# 7 01-07 -0.164   0.0873  1.41  3.75
# 8 01-08  0.0634  0.266   1.41  3.75
# 9 01-09  0.0115 -0.0320  1.41  3.75
# 10 01-10  0.0576 -0.237   1.41  3.75
# # ... with 356 more rows

Если вы хотите обновить имена столбцов до чего-то вроде v_trend, вы можете использовать это вместо этого:

summarise_at(vars(matches("V")), list(trend = ~lm(. ~ Year)$coefficients[2]))

Альтернатива (получение дополнительной информации о каждой модели)

Если вы хотите получить больше информации о каждой линейной модели, я бы порекомендовал использовать некоторое изменение данных и пакет broom следующим образом:

library(lubridate)
library(tidyverse)
library(broom)

testframe %>% 
  mutate(Year = year(Date)) %>%  
  select(-Date) %>% 
  gather(v, value, -Day, -Year) %>%
  group_by(Day, v) %>%
  nest() %>%
  mutate(dd = map(data, ~tidy(lm(value ~ Year, data = .)))) %>%
  unnest(dd) %>%
  arrange(Day)

# # A tibble: 2,928 x 7
#     Day   v     term          estimate  std.error  statistic  p.value
#   <chr> <chr> <chr>            <dbl>      <dbl>      <dbl>    <dbl>
# 1 01-01 V1    (Intercept)  -217.     162.           -1.34  2.16e- 1
# 2 01-01 V1    Year            0.108    0.0806        1.34  2.16e- 1
# 3 01-01 V2    (Intercept)  -112.     196.           -0.570 5.84e- 1
# 4 01-01 V2    Year            0.0554   0.0976        0.567 5.86e- 1
# 5 01-01 V3    (Intercept) -2800.       0.260    -10756.    6.25e-30
# 6 01-01 V3    Year            1.41     0.000130  10824.    5.94e-30
# 7 01-01 V4    (Intercept) -7489.       0.694    -10787.    6.11e-30
# 8 01-01 V4    Year            3.75     0.000346  10824.    5.94e-30
# 9 01-02 V1    (Intercept)   109.     238.            0.458 6.59e- 1
# 10 01-02 V1    Year           -0.0543   0.119        -0.458 6.59e- 1
# # ... with 2,918 more rows

Затем вы можете запросить этот набор данных и получить все, что хотите. Например, если вы сохраните приведенный выше вывод как testframe2, вы можете получить тренд/наклон для дня 01-01, столбец V1 следующим образом:

testframe2 %>% filter(Day == "01-01" & v == "V1" & term == "Year") %>% pull(estimate)

и значение p этого наклона, как это:

testframe2 %>% filter(Day == "01-01" & v == "V1" & term == "Year") %>% pull(p.value)
person AntoniosK    schedule 13.06.2019
comment
Спасибо! Это то, что я хочу! Есть ли способ изменить вывод таким образом, что у меня есть сводка (мод) [['коэффициенты']]['(перехват)','Оценка'], сводка (мод)[['коэффициенты']][' x','Оценка'], summary(mod)[['коэффициенты']]['x','Pr(›|t|)']) для каждого столбца? - person Mr.Spock; 13.06.2019
comment
И еще вопрос: vars(matches(V) не работает для моего реального фрейма данных, так как имена столбцов не начинаются с V. Есть ли способ выбрать столбцы по номерам столбцов? - person Mr.Spock; 13.06.2019
comment
Я не уверен, что понял ваш первый вопрос, но я обновил свой ответ, чтобы предоставить вам всю информацию о каждой линейной модели. Для второго вопроса я бы рекомендовал вам использовать фактические имена столбцов, чтобы сделать ваш процесс более надежным (т.е. вы никогда не знаете, может ли порядок столбцов измениться в будущем). Вы можете использовать vars() и указать имена столбцов. - person AntoniosK; 13.06.2019

Это обеспечивает отдельную точку пересечения и наклон для каждого дня года для каждого столбца V. (yday — это день года 0, 1, 2, ... и ydayf — то же самое, но в качестве множителя, а yr — четырехзначный год.)

m <- as.matrix(testframe[-(1:2)])
yday <- as.POSIXlt(testframe$Date)$yday
ydayf <- factor(yday)
yr <- as.numeric(format(testframe$Date, "%Y"))

fm2 <- lm(m ~ ydayf + ydayf:yr + 0)
coef(fm2)
dummy.coef(fm2) # expand coefficients
summary(fm2)
broom::tidy(fm2) # data frame

Если вам нужны отдельные наклоны, но только один перехват, используйте для каждого столбца V.

fm3 <- lm(m ~ ydayf:yr)
coef(fm3) 
dummy.coef(fm3) # expands coefficients
summary(fm3)
broom::tidy(fm3)  # data frame

Если вам нужны отдельные точки пересечения, но только один наклон на столбец V, тогда:

fm4 <- lm(m ~ ydayf + yr + 0)
coef(fm4) 
dummy.coef(fm4) # expands coefficients
summary(fm4)
broom::tidy(fm4)  # data frame

Книга Современная прикладная статистика с S Plus является хорошим справочником по lm формулам.

person G. Grothendieck    schedule 13.06.2019
comment
Спасибо! Это не совсем то, что я хочу иметь. Я обновил свой вопрос, чтобы сделать его более ясным. - person Mr.Spock; 13.06.2019
comment
Спасибо, так намного лучше выглядит, но что такое /ydayf или *ydayf? Я не понимаю эту часть. - person Mr.Spock; 13.06.2019
comment
Обновили ответ и перенесли к нему комментарии. - person G. Grothendieck; 13.06.2019