автоматизация lm-тестов со всеми возможными комбинациями переменных и получением значений для: shapiro.test(), bptest(),vif() в R

Я потратил дни на поиск оптимальных моделей, которые удовлетворяли бы всем стандартным предположениям OLS (нормальное распределение, гомоскедастичность, отсутствие мультиколлинеарности) в R, но с 12 переменными невозможно найти оптимальную комбинацию переменных. Поэтому я пытался создать скрипт, который бы автоматизировал этот процесс.

Вот пример кода для вычислений:

x1 <- runif(100, 0, 10)
x2 <- runif(100, 0, 10)
x3 <- runif(100, 0, 10)
x4 <- runif(100, 0, 10)
x5 <- runif(100, 0, 10)

df <- as.data.frame(cbind(x1,x2,x3,x4,x5))

library(lmtest)
library(car)

model <- lm(x1~x2+x3+x4+x5, data = df)

# check for normal distribution (Shapiro-Wilk-Test)
rs_sd <- rstandard(model)
shapiro.test(rs_sd)

# check for heteroskedasticity (Breusch-Pagan-Test)
bptest(model)

# check for multicollinearity
vif(model)

#-------------------------------------------------------------------------------
# models without outliers
# identify outliers (calculating the Cooks distance, if x > 4/(n-k-1) --> outlier
cooks <- round(cooks.distance(model), digits = 4)
df_no_out <- cbind(df, cooks)
df_no_out <- subset(df_no_out, cooks < 4/(100-4-1))

model_no_out <- lm(x1~x2+x3+x4+x5, data = df_no_out)

# check for normal distribution
rs_sd_no_out<- rstandard(model_no_out)
shapiro.test(rs_sd_no_out)

# check for heteroskedasticity
bptest(model_no_out)

# check for multicollinearity
vif(model_no_out)

Что я имею в виду, так это перебрать все комбинации переменных и получить P-ЗНАЧЕНИЯ для shapiro.test() и bptest() или значения VIF для всех созданных моделей, чтобы я мог сравнить значения значимости или мультиколлинеарность соотв. (в моем наборе данных мультиколлинеарность не должна быть проблемой, и поскольку для проверки мультиколлинеарности тест VIF выдает больше значений (для каждого фактора var 1xVIF), которые, вероятно, будет сложнее реализовать в коде), p-значения для shapiro.test + bptest() будет достаточно…).

Я пытался написать несколько скриптов, которые бы автоматизировали процесс, но безуспешно (к сожалению, я не программист). Я знаю, что уже есть несколько тем, посвященных этой проблеме.

Как запустить lm модели, использующие все возможные комбинации нескольких переменных и фактора

Поиск наилучшей комбинации переменных для высоких Значения R-квадрата

но я не нашел скрипт, который также вычислял бы ТОЛЬКО P-ЗНАЧЕНИЯ.

Особенно важны тесты для моделей без выбросов, потому что после удаления выбросов во многих случаях выполняются предположения МНК.

Я был бы очень признателен за любые предложения или помощь в этом.


person Mapos    schedule 16.11.2018    source источник
comment
Ради себя не делайте as.data.frame(cbind(.)). В этом случае проблем нет, но если одна из переменных класса "character", то все они станут символами, как только вы cbind их вместе.   -  person Rui Barradas    schedule 16.11.2018


Ответы (2)


вы царапаете поверхность того, что сейчас называется статистическим обучением. вступительный текст - «Статистическое обучение с приложениями в R», а текст уровня выпускника - «Элементы статистического обучения». чтобы сделать то, что вам нужно, вы используете функцию regsubsets() из пакета "Leaps". Однако, если вы прочтете хотя бы главу 6 из вводной книги, вы узнаете о перекрестной проверке и начальной загрузке, которые являются современным способом выбора модели.

person Stephen    schedule 16.11.2018
comment
Я знаком с пошаговой регрессией и начальной загрузкой, и забава regsubsets() возвращает лучшую модель, но я ищу модели с незначительными тестами Шапиро Уилка и Бреуша-Пагана… Но спасибо за рекомендацию книг. Они оба очень полезны. - person Mapos; 17.11.2018

Следующее автоматизирует подгонку моделей и последующие тесты.

Есть одна функция, которая подходит для всех возможных моделей. Затем серия вызовов функций *apply позволит получить нужные вам значения.

library(lmtest)
library(car)


fitAllModels <- function(data, resp, regr){
  f <- function(M){
    apply(M, 2, function(x){
      fmla <- paste(resp, paste(x, collapse = "+"), sep = "~")
      fmla <- as.formula(fmla)
      lm(fmla, data = data)
    })
  }
  regr <- names(data)[names(data) %in% regr]
  regr_list <- lapply(seq_along(regr), function(n) combn(regr, n))
  models_list <- lapply(regr_list, f)
  unlist(models_list, recursive = FALSE)
}

Теперь данные.

# Make up a data.frame to test the function above.
# Don't forget to set the RNG seed to make the
# results reproducible
set.seed(7646)
x1 <- runif(100, 0, 10)
x2 <- runif(100, 0, 10)
x3 <- runif(100, 0, 10)
x4 <- runif(100, 0, 10)
x5 <- runif(100, 0, 10)

df <- data.frame(x1, x2, x3, x4, x5)

Сначала подберите все модели с "x1" в качестве ответа и другими переменными в качестве возможных регрессоров. Функция может быть вызвана с одним ответом и любым количеством возможных регрессоров, которые вы хотите.

fit_list <- fitAllModels(df, "x1", names(df)[-1])

А теперь последовательность испытаний.

# Normality test, standardized residuals
rs_sd_list <- lapply(fit_list, rstandard)
sw_list <- lapply(rs_sd_list, shapiro.test)
sw_pvalues <- sapply(sw_list, '[[', 'p.value')

# check for heteroskedasticity (Breusch-Pagan-Test)
bp_list <- lapply(fit_list, bptest)
bp_pvalues <- sapply(bp_list, '[[', 'p.value')

# check for multicollinearity, 
# only models with 2 or more regressors
vif_values <- lapply(fit_list, function(fit){
  regr <- attr(terms(fit), "term.labels")
  if(length(regr) < 2) NA else vif(fit)
})

Заметка о расстоянии Кука. В вашем коде вы подмножаете исходный data.frame, создавая новый без выбросов. Это приведет к дублированию данных. Я выбрал только список индексов строк df. Если вы предпочитаете дублированные data.frames, раскомментируйте строку в анонимной функции ниже и закомментируйте последнюю.

# models without outliers
# identify outliers (calculating the 
# Cooks distance, if x > 4/(n - k - 1) --> outlier

df_no_out_list <- lapply(fit_list, function(fit){
  cooks <- cooks.distance(fit)
  regr <- attr(terms(fit), "term.labels")
  k <- length(regr)
  inx <- cooks < 4/(nrow(df) - k - 1)
  #df[inx, ]
  which(inx)
})

# This tells how many rows have the df's without outliers
sapply(df_no_out_list, NROW)

# A data.frame without outliers. This one is the one 
# for model number 8. 
# The two code lines could become a one-liner.
i <- df_no_out_list[[8]]
df[i, ]
person Rui Barradas    schedule 16.11.2018
comment
Я только что протестировал ваш скрипт на своем наборе данных и сделал несколько проверок, и он работает хорошо. Это действительно очень полезно, а также применимо для множества других тестов. Это экономит много времени. Действительно ценю это! Благодарю вас! - person Mapos; 17.11.2018
comment
Я запускаю код только для df с выбросами, чтобы получить векторы p-значений для bptest() и shapiro.test(), а затем просто объединяю эти два вектора и создаю подмножество, где p-значение из обоих столбцов > 0,05. Как я могу получить эти 2 вектора для df без выбросов? Я не знаю, как мне запустить последовательность тестов для df без выбросов. Я полагаю, мне следует изменить параметр fit_list? Не могли бы вы помочь, как изменить код? Заранее спасибо. - person Mapos; 20.11.2018