Как итеративно обучать модели прогноза (GAM, MARS, ) на основе выбранных дней и вычислять важность переменной за период времени

У меня есть таблица данных, в которой всегда разное количество столбцов и имен столбцов, а также числовая переменная с именем days (эта переменная также отличается; сейчас/здесь: 50):

library(data.table)
library(caret)

days -> 50  
## Create random data table: ##
dt.train <- data.table(date = seq(as.Date('2020-01-01'), by = '1 day', length.out = 366),
                       "DE" = rnorm(366, 35, 1), "Wind" = rnorm(366, 5000, 2), "Solar" = rnorm(366, 3, 2),
                       "Nuclear" = rnorm(366, 100, 5), "ResLoad" = rnorm(366, 200, 3),  check.names = FALSE)

Я моделирую/обучаю линейную модель (= LM), где я хочу предсказать столбец DE, и я вычисляю важность переменной по отношению к переменной days. См. следующий фрагмент кода:

## MODEL FITTING: ##
## Linear Model: ##

## Function that calculates the iteratively prediction: ##
calcPred <- function(data){
  ## Model fitting: ##
  xgbModel <- stats::lm(DE ~ .-1-date, data = data)
  ## Model training: ##
  stats::predict.lm(xgbModel, data)
}

## Function that calculates the iteratively variable importance: ##
varImportance <- function(data){
  ## Model fitting: ##
  xgbModel <- stats::lm(DE ~ .-1-date, data = data)
  
  terms <- attr(xgbModel$terms , "term.labels")
  varimp <- caret::varImp(xgbModel)
  importance <- data[, .(date, imp = t(varimp))]
} 


## Train Data PREDICTION with iteratively xgbModel: ##
dt.train <- dt.train[, c('prediction') := calcPred(.SD), by = seq_len(nrow(dt.train)) %/% days]

## Iteratively variable importance:##
dt.importance <- data.table::copy(dt.train[, c("prediction") := NULL])
dt.importance <- dt.importance[, varImportance(.SD), by = seq_len(nrow(dt.train)) %/% days]

Что здесь происходит: Моя модель всегда обучается 50 дней, а затем именно на этот период времени делается прогноз этих обученных 50 дней. И так продолжается до даты окончания моей таблицы. Кроме того, функция varImportance() дает переменные значения предикторов (все столбцы, кроме date и DE) в интервале обучения (здесь для каждых 50 дней).

Первоначально я думал, что могу использовать функции calcPred() и varImportance() для обобщенной аддитивной модели (= GAM) и многовариантного адаптивного регрессионного сплайна (= MARS) или повышения градиента (= GB), но, к сожалению, эти версии работают только с LM.

Теперь я хотел бы кратко описать подгонку модели для трех других моделей в целом, но мне также понадобится ваша помощь здесь, чтобы в конце были рассчитаны модели GAM, MARS и GB, а также LM.

GAM:

## Create data-vector with dates of dt.train: ##
v.trainDate <- dt.train$date
## Delete column "date" of train data for model fitting: ##
dt.train <- dt.train[, c("date") := NULL]

## Preparation for GAM: ##
trainDataNames <- names(dt.train)
responseVar <- trainDataNames[1]
trainDataNames <- trainDataNames[trainDataNames != responseVar]
## Create right-hand side of GAM model in string/character format: ##
formulaRight <- paste('s(', trainDataNames, ')', sep = '', collapse = ' + ')
## Create the whole formula for GAM model in string/character format: ##
formulaGAM <- paste(responseVar, '~', formulaRight, collapse = ' ')
## Coerce to a formula object: ##
formulaGAM <- as.formula(formulaGAM)

## MODEL FITTING: ##
## Generalized Additive Model: ##
xgbModel <- mgcv::gam(formulaGAM, data = dt.train)

## Train and Test Data PREDICTION with xgbModel: ##
dt.train$prediction <- mgcv::predict.gam(xgbModel, dt.train)

## Add date columns to dt.train and dt.test: ##
dt.train <- data.table(date = v.trainDate, dt.train)

МАРС:

## Create vectors with all DE values of train data set: ##
v.trainY <- dt.train$DE
## Save dates of train data in an extra vector: ##
v.trainDate <- dt.train$date
## Create train matrices for GB model fitting: ##
m.trainData <- as.matrix(dt.train[, c("date", "DE") := list(NULL, NULL)])
## Model fitting with grid-search: ##: ##
hyper_grid <- expand.grid(degree = 1:3, 
                          nprune = seq(2, 100, length.out = 10) %>% floor()
              )
              
## MODEL FITTING: ##
## Multivariate Adaptive Regression Spline: ##
xgbModel <- caret::train(x = m.trainData, 
                         y = v.trainY,
                         method = "earth",
                         metric = "RMSE",
                         trControl = trainControl(method = "cv", number = 10),
                                       tuneGrid = hyper_grid
              )
              
              
## Train Data PREDICTION with xgbModel: ##
dt.train$prediction <- stats::predict(xgbModel, dt.train)

ГБ:

## Create vectors with all DE values of train data set: ##
v.trainY <- dt.train$DE
## Save dates of train data in an extra vector: ##
v.trainDate <- dt.train$date
## Create train matrices for GB model fitting: ##
m.trainData <- as.matrix(dt.train[, c("date", "DE") := list(NULL, NULL)])

## Gradient Boosting with hyper parameter tuning: ##
xgb_trcontrol <- caret::trainControl(method = "cv",
                                     number = 3,
                                     allowParallel = TRUE,
                                     verboseIter = TRUE,
                                     returnData = FALSE
)

xgbgrid <- base::expand.grid(nrounds = c(15000), # 15000
                             max_depth = c(2),
                             eta = c(0.01),
                             gamma = c(1),
                             colsample_bytree = c(1),
                             min_child_weight = c(2),
                             subsample = c(0.6)
)

## MODEL FITTING: ##
## Gradient Boosting: ##
xgbModel <- caret::train(x = m.trainData, 
                         y = v.trainY,
                         trControl = xgb_trcontrol,
                         tuneGrid = xgbgrid,
                         method = "xgbTree"
)

## Train data PREDICTION with xgbModel: ##
dt.train$prediction <- stats::predict(xgbModel, m.trainData)

## Add DE and date columns to dt.train: ##
dt.train <- data.table(DE = v.trainY, dt.train)
dt.train <- data.table(date = v.trainDate, dt.train)

Как мне рассчитать то же самое для трех других моделей, что и для LM? Надеюсь, кто-нибудь сможет мне помочь. Извините, что вопрос получился таким длинным.


person MikiK    schedule 17.02.2021    source источник


Ответы (1)


Вы можете определить модель как функцию, которую вы передаете в качестве аргумента calcPred и varImportance.

Например, с LM

model <- function(data) {stats::lm(DE ~ .-1-date, data = data)}

С GAM

model <- function(data) {mgcv::gam(formulaGAM, data = data)}

с MARS:

model <- function(data) {
  hyper_grid <- expand.grid(degree = 1:3, 
                            nprune = seq(2, 100, length.out = 10) %>% floor())
  caret::train(x = subset(data, select = -DE),
               y = data$DE,
               method = "earth",
               metric = "RMSE",
               trControl = trainControl(method = "cv", number = 10),
               tuneGrid = hyper_grid)
}

Я обновил код, чтобы учесть этот новый аргумент:

library(data.table)
library(caret)
library(magrittr)


days <- 50
## Create random data table: ##
dt.train <- data.table(date = seq(as.Date('2020-01-01'), by = '1 day', length.out = 366),
                       "DE" = rnorm(366, 35, 1), "Wind" = rnorm(366, 5000, 2), "Solar" = rnorm(366, 3, 2),
                       "Nuclear" = rnorm(366, 100, 5), "ResLoad" = rnorm(366, 200, 3),  check.names = FALSE)

dt.importance <- data.table::copy(dt.train)

## Define model & prediction functions ##

model <- function(data) {stats::lm(DE ~ .-1-date, data = data)}

predict <- function(data,model) {stats::predict(model, data)}

calcPred <- function(data,model){
  if (nrow(data)==days) {
  stats::predict(model,data) } else {
  NULL }
}

## Function that calculates the iteratively variable importance: ##
varImportance <- function(data,model){
  cat(nrow(data),'\n')
  if (nrow(data)==days) {
  terms <- attr(model$terms , "term.labels")
  varimp <- caret::varImp(model)
  importance <- data[, .(date, imp = t(varimp))]} else
  { NULL }
}


## Train Data PREDICTION with iteratively xgbModel: ##
dt.train <- dt.train[, c('prediction') := calcPred(.SD,model(.SD)), by = (seq_len(nrow(dt.train))-1) %/% days]

## Iteratively variable importance:##

dt.importance <- dt.importance[, varImportance(.SD,model(.SD)), by = (seq_len(nrow(dt.train))-1) %/% days]

Чтобы использовать другие модели, просто используйте функцию модели, которую вы хотите, в приведенном выше коде. Это работает с LM или GAM в предоставленном вами наборе данных.

К сожалению, varImp не работает с вашим набором данных с MARS, хотя это кажется возможным .

person Waldi    schedule 22.02.2021
comment
Я уже пробовал что-то подобное, но при использовании вашей кодировки и calcPred() для GAM и MARS я получаю следующие сообщения об ошибках. Ошибка для GAM: Model has more coefficients than data. Ошибка для MARS: In nominalTrainWorkflow(x = x, y = y, wts = weights, info = trainInfo, : There were missing values in resampled performance measures. Значение переменной не работает для GAM и MARS. Ваш код работает, когда вы его запускаете? - person MikiK; 22.02.2021
comment
Вы правы, я сделал это слишком быстро, и varImp в настоящее время не работает для MARS, хотя должен: topepo.github.io/caret/variable-importance.html. Я пытаюсь решить это и вернуться к вам - person Waldi; 22.02.2021
comment
В моем случае это также не работает для GAM! varImp() не работает для LM, когда у меня более 12 предикторов, что немного странно. Выброшенная ошибка: j doesn't evaluate to the same number of columns for each group data table R. Вы знаете, почему это происходит? - person MikiK; 22.02.2021
comment
Спасибо большое, за помощь!! - person MikiK; 22.02.2021
comment
Приведенный выше код теперь работает с GAM : ошибки исходили от последнего фрагмента, в котором недостаточно строк (16 вместо 50) - person Waldi; 22.02.2021
comment
Спасибо, теперь ошибка больше не появляется. Но теперь последние 16 строк не прогнозируются. Почему мы не можем просто повторять каждые 50 дней, а затем взять оставшиеся дни (в моем случае последние 16 дней), которые остались, и вычислить прогноз и важность переменной для этих последних оставшихся дней? - person MikiK; 23.02.2021
comment
Кажется, это связано с тем, как рассчитывается модель. Например caret::varImp(model(dt.train[321:366])) работает, а caret::varImp(model(dt.train[322:366])) нет. Таким образом, представляется необходимым 45/46 точек данных (см. caret::varImp(model(dt.train[1:45]))), вероятно, связанных с используемой формулой. Вы можете использовать frollaply для более чем 46 точек, чтобы получить прогноз для последних точек. Было бы дольше использовать его для всего набора данных. - person Waldi; 23.02.2021
comment
О, хорошо, когда у меня есть другие столбцы для таблицы данных dt.train, и я использую LM, я получаю для calcPred() следующую ошибку: In predict.lm(model, data) : prediction from a rank-deficient fit may be misleading. И всегда, когда я получаю эту ошибку через прогноз, я получаю ошибку для varImpt(): j doesn't evaluate to the same number of columns for each group data table R. Почему это так? - person MikiK; 23.02.2021
comment
При использовании функции rollapply() возникают те же сообщения об ошибках. Прогноз работает, но я также получаю предупреждающее сообщение с rank-deficient fit, как я уже упоминал ранее, а varImp() все равно не работает. - person MikiK; 23.02.2021
comment
Это по предоставленным вами данным или по другим данным? Каждая модель имеет свои особенности, и проверка ее соответствия вашим данным при любых обстоятельствах может быть утомительной. - person Waldi; 23.02.2021
comment
Это к данным с другими предикторами. Всего у меня 87 возможных предикторов, но я не хочу, чтобы моя модель всегда соответствовала одному и тому же, поэтому я немного различаю предикторы. Может ли это сообщение об ошибке появиться из-за мультиколлинеарности? - person MikiK; 23.02.2021
comment
Я так и думал, мультиколинеарности вполне могут быть причиной. - person Waldi; 23.02.2021
comment
Это будет означать, что мне нужно использовать Ridge, Lasso или Elastic Net Regression? - person MikiK; 23.02.2021
comment
Трудно сказать без реальных данных. Как обычно, наука о данных на 80% состоит из утомительной тяжелой работы по подготовке данных и выбору правильных моделей ;-) - person Waldi; 23.02.2021
comment
Да, это сложно. К сожалению, varImp() в настоящее время не работает для модели MARS. - person MikiK; 24.02.2021
comment
Да, это то, что я все еще хотел добавить к своему ответу: он должен работать в соответствии с документом, но это не так... - person Waldi; 24.02.2021
comment
Ок, спасибо! - person MikiK; 24.02.2021