(Серия ISLR на Python)

Эта статья является частью нашей серии ISLR, в которой мы постараемся охватить несколько примеров из упражнений из книги Введение в статистическое обучение и обсудить их подробно. Идеальной аудиторией для этих статей будет тот, кто начинает с регресса или хочет освежиться.

В этой статье в основном рассматривается использование диагностических графиков при анализе и улучшении моделей линейной регрессии.

Предположение. Читатель имеет базовые представления о Python и статистике.

Предложение: хотя само руководство является законченным, его можно лучше понять, прочитав вместе с Главой 3 ISLRhttp://faculty.marshall.usc.edu/ Гарет-Джеймс

Ссылка на блокнот jupyter приведена в конце статьи.

Автоматический набор данных содержит информацию о 392 автомобилях. Наша цель будет заключаться в прогнозировании миль на галлон на основе лошадиных сил с использованием линейной регрессии и ее вариантов.

Файлы данных можно скачать с Git здесь.

Описание: Газовый пробег, лошадиные силы и другая информация для 392 автомобилей.

Использование: Авто - Автоматическая установка данных

Формат: фрейм данных с 392 наблюдениями по следующим 9 переменным.

  • миль на галлон: миль на галлон.
  • цилиндры: количество цилиндров от 4 до 8.
  • объем: объем двигателя (куб. дюймы).
  • лошадиные силы: мощность двигателя.
  • вес: вес автомобиля (фунты).
  • ускорение: время для разгона от 0 до 60 миль в час (сек.).
  • год: модельный год (по модулю 100).
  • origin: происхождение автомобиля (1. американское, 2. европейское, 3. японское).
  • name: название автомобиля.

Исходные данные содержали 408 наблюдений, но 16 наблюдений с пропущенными значениями были удалены.

Источник-. Этот набор данных был взят из библиотеки StatLib, которая поддерживается в Университете Карнеги-Меллона. Набор данных использовался в Экспозиции Американской статистической ассоциации 1983 года.

Загрузка и очистка данных

Мы начинаем с загрузки данных в pandas DataFrame и анализа их с помощью функций .head () и .info ().

Использование головы для просмотра первых 5 строк набора данных.

auto_dataset = pd.read_csv(Auto.csv')
auto_dataset.head()

auto_dataset.info()

Хотя значения мощности в первых пяти строках набора данных являются целыми числами, но типом данных для столбца является объект. Следовательно, мы принудительно преобразуем этот столбец в числовые значения, игнорируя любые значения, которые не могут быть преобразованы.

auto_dataset.horsepower = pd.to_numeric(auto_dataset.horsepower, errors='coerce')
auto_dataset.info()

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

Мы установим столбец name как индекс, а набор data_Set среза будет содержать только два интересующих столбца: mpg, и лошадиные силы для простоты использования.

required_df = auto_dataset[['mpg', 'horsepower']]
required_df.describe()

Исследовательский анализ

Прежде чем непосредственно перейти к модели линейной регрессии, мы должны сначала построить график и наблюдать взаимосвязь между двумя переменными. Мы будем использовать regplot из библиотеки seaborn, что позволяет нам построить линию наилучшего соответствия на диаграмме рассеяния.

sns.regplot(x="horsepower", y="mpg", data=required_df, line_kws={'color':'red'})
plt.show()

Из приведенного выше графика видно, что линейная регрессия отражает суть отрицательной взаимосвязи между расходом топлива на галлон и мощностью в лошадиных силах. Однако есть некоторая выпуклость во взаимосвязи, которую линейная регрессия не может уловить. Мы обсудим это подробно при обсуждении диагностики на основе предположений линейной регрессии.

Основы линейной регрессии

В этом разделе мы кратко и интуитивно обсудим обычную регрессию методом наименьших квадратов, не углубляясь в математику.

OLS направлен на поиск наиболее подходящей линии, для которой сумма квадратов вертикального расстояния всех точек от линии минимальна.

Приведенное ниже уравнение показывает, как выглядят линейные отношения, и основные термины.

Линейная регрессия пытается предсказать указанное выше отношение, как показано ниже. В нашем уравнении линейной регрессии нет члена ошибки, поскольку этот член не моделируется. Линейная регрессия направлена ​​на поиск объективных оценок параметров.

Примечание. Линия OLS всегда проходит через точку, обозначенную буквами X и Y.

Линейная регрессия в Python

Чтобы запустить линейную регрессию в Python, мы использовали пакет statsmodel. Когда у нас есть данные в DataFrame, требуется всего две строки кода для запуска и получения сводной информации о модели.

import statsmodels.formula.api as smf
lin_model = smf.ols("mpg ~ horsepower", data=required_df).fit()
lin_model.summary()

Сводка предоставляет исчерпывающую информацию о линейной регрессии. Отправной точкой является всегда смотреть на прогнозируемые параметры и их p-значение.

P-значения позволяют нам проверить значимость отдельных прогнозируемых параметров. P-значения почти равны нулю для обоих параметров, что говорит о том, что мы можем отклонить нулевую гипотезу о том, что либо параметр перехвата, либо параметр наклона равен нулю с высоким уровнем достоверности. В то же время Вероятность (F-статистика) проверяет значимость всей модели в целом. Опять же, близкое к нулю значение этой статистики предполагает, что мы можем отклонить нулевую гипотезу о том, что модель не фиксирует взаимосвязь между зависимыми и независимыми переменными с высоким доверительным интервалом.

Статистика R-квадрата показывает, какую дисперсию наша модель может уловить из общей дисперсии данных. Текущее значение предполагает, что мы можем уловить 60,5% общей дисперсии. Необъяснимая дисперсия может быть по двум причинам: 1) член ошибки - немоделируемая часть 2) Смещение модели - текущая модель слишком проста, чтобы уловить реальную взаимосвязь. В этом примере мы получаем значительный эффект от второго случая, поскольку мы пытаемся смоделировать выпуклые отношения с помощью линейной модели.

Последняя часть сводки под выделенным красным полем дает информацию об условиях ошибки. Тест Дарбина-Ватсона проверяет наличие автокорреляции, а тест Жарка-Бера - для проверки того, соответствуют ли члены ошибок нормальному распределению. Мы обсудим важность этого в следующем разделе.

Диагностические графики

Используя диагностические графики, мы проверяем, соответствует ли наша модель различным предположениям о линейной регрессии. Эти тесты предназначены для проверки правильности модели и предоставления информации о том, как улучшить модель, чтобы точно определить взаимосвязь между зависимыми и независимыми переменными.

Ниже приведен список предположений, которые мы будем проверять:

  • Линейность: в остатках не должно быть никакой моделируемой информации.
  • Эндогенность: остаток не должен быть связан с независимой переменной.
  • Нормальность: остатки должны быть распределены нормально.
  • Гомоскедастичность: разброс значений ошибок должен быть постоянным.
  • Выбросы и высокое кредитное плечо. В наборе данных не должно быть сильной точки влияния, которая могла бы исказить нашу модель.

Для подробного понимания сделанных выше предположений обратитесь к теореме Гаусс-Марков.

Линейность. Остаточные значения не должны зависеть от прогнозируемых значений. На графике остаточных и подобранных значений не должно быть тенденции, а значения должны быть случайным образом распределены по оси абсцисс.

Любой узор на графике подразумевает некоторое количество моделируемой информации, не захваченной моделью.

В линейной модели уже вычислены как остаточные, так и аппроксимированные значения.

temp_data = pd.DataFrame(dict(fitted_values=lin_model.fittedvalues, residual=lin_model.resid))
graph = sns.lmplot(x='fitted_values', y='residual', data=temp_data, lowess=True, line_kws={'color':'red'})
plt.show()

Мы можем наблюдать выпуклый паттерн в остатке, который наша линейная модель не улавливает. Это согласуется с нашим первоначальным наблюдением, что соотношение между расходом топлива на галлон и мощностью в лошадиных силах имеет некоторую выпуклость.

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

temp_data = pd.DataFrame(dict(horsepower=required_df.horsepower, residual=lin_model.resid))
graph = sns.lmplot(x='horsepower', y='residual', data=temp_data, lowess=True, line_kws={'color':'red'})
plt.show()

Мы можем наблюдать такое же нарушение предположения, как линейность. Это означает, что предполагаемые параметры не могут быть объективной оценкой фактических параметров; следовательно, наша модель необъективна.

Нормальность. Допущение нормальности может привести к ошибкам в отношении точности модели, но оно сильно влияет на значения проверки гипотез. P-значения могут быть занижены или завышены, если остатки слишком сильно отклоняются от нормального распределения. Мы можем использовать QQ-Plot, чтобы проверить нормальность остатков.

sm.qqplot(lin_model.resid, dist=stats.t, fit=True, line='45', ax=ax[0])
sns.distplot(lin_model.get_influence().resid_studentized_internal)
plt.show()

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

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

Гетероскедастичность использует график «масштаб-расположение». Для гомоскедастичности линия тренда на этом графике должна быть горизонтальной.

Мы также можем наблюдать гетероскедастичность, глядя на остаточные графики выше. Опять же, воронкообразное распределение показывает гораздо больше отклонений в остатке вправо, чем в левой части графика.

qrt_std_residual = np.sqrt(np.abs(lin_model.get_influence().resid_studentized_internal))
temp_data = pd.DataFrame(dict(fitted_values=lin_model.fittedvalues,
                              sqrt_std_residual=sqrt_std_residual))
graph = sns.lmplot(x='fitted_values', y='sqrt_std_residual', data=temp_data, lowess=True, line_kws={'color':'red'})
plt.show()

Линия тренда не плоская; следовательно, остатки по своей природе гетероскедастичны.

Выбросы и высокое кредитное плечо:. При линейной регрессии мы часто слишком много внимания уделяем выбросам. На самом деле точки с высоким кредитным плечом, которые также являются выбросами, намного опаснее, чем просто выбросы. Точки высокого кредитного плеча - это точки данных, которые находятся на крайнем значении независимых переменных (X).

Мы стремимся к тому, чтобы ни одна точка в наборе данных не оказала существенного влияния на нашу модель. Такие точки будут проблемой, если они являются исключительным случаем или неверны, поскольку это изменит модель в сторону от исходной взаимосвязи.

График влияния из модуля statsmodel.api или непосредственно при измерении расстояния Кука можно найти наиболее важные точки влияния.

sm.graphics.influence_plot(lin_model, alpha  = 0.05, ax=ax, criterion="cooks")
plt.show()

На приведенном выше графике ось Y представляет выбросы с точки зрения стандартного отклонения. Обычно считается, что точки ниже или выше трех стандартных отклонений являются выбросами. Ось X определяет горизонтальное плечо каждой точки, а размер пузыря - это влияние каждой точки на модель на основе меры Кука.

Эмпирическое правило состоит в том, что точки данных с расстоянием Кука больше 4 / (количество точек данных) являются точками данных, вызывающими озабоченность.

cooks_threshold = 4/len(X.index)
df = influence_summary[influence_summary.cooks_d>cooks_threshold][['cooks_d']]
df.style.bar(subset=['cooks_d'], color='Red')

Мы должны посмотреть на все эти точки данных, чтобы решить, верны ли они и должны ли они быть частью нашей модели или нет. Еще одна часто используемая матрица для поиска точек влияния - DFFITS.

Таким образом, мы рассмотрели все диагностические графики и наблюдали, как наша текущая модель не работает, исходя из предположений о линейной регрессии. В следующем разделе мы кратко рассмотрим, как можно интуитивно решить эти проблемы, чтобы уловить взаимосвязь между миль на галлон и лошадиными силами лучше.

Решение проблем с помощью диагностических графиков

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

quad_model = smf.ols("mpg ~ horsepower + np.square(horsepower)", data=auto_dataset[['mpg','horsepower']].reset_index()).fit()
quad_model.summary()

Из сводной статистики видно, что p-значение для квадратного члена близко к нулю, и, следовательно, его значимость. Мы также можем видеть, что точность нашей модели увеличилась, поскольку член R-квадрат теперь составляет ~ 68%, что на 8% выше, чем раньше.

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

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

В текущем сценарии нам нужно преобразование вогнутой формы, и лучше всего начать с преобразования журнала.

log_quad_model = smf.ols("np.log(mpg) ~ horsepower + np.square(horsepower)", data=auto_dataset[['mpg','horsepower']].reset_index()).fit()
log_quad_model.summary()

Приведенная выше сводная статистика регрессии показывает, что преобразование зависимой переменной увеличивает точность модели на ~ 4,5%.

Мы проигнорировали все точки влияния, превышающие пороговое значение для дистанции Кука, для простоты нашего обсуждения.

cooks_distance = influence_summary[influence_summary.cooks_d>4/len(X.index)][['cooks_d']]

auto_dataset_removed_outliers = auto_dataset.drop(cooks_distance.index)
log_quad_model = smf.ols("np.log(mpg) ~ horsepower + np.square(horsepower)", data=auto_dataset_removed_outliers[['mpg','horsepower']].reset_index()).fit()
log_quad_model.summary()

После удаления важных точек точность модели увеличивается на ~ 4%, как показывает R-квадрат.

Диагностические графики на окончательной модели

Из приведенных выше диагностических графиков видно, что наша окончательная модель решает все проблемы с допущениями о линейной регрессии. В процессе решения этих проблем мы также изменили соотношение между миль на галлон и лошадиными силами.

ПРЕДЛАГАЕМАЯ НАЧАЛЬНАЯ МОДЕЛЬ

КОНЕЧНАЯ ПРОИЗВОДНАЯ МОДЕЛЬ

Заключительное примечание

В этой статье я попытался коснуться поверхности линейной регрессии. Однако линейная регрессия по-прежнему остается самым мощным инструментом, используемым в аналитике. Итак, прежде чем переходить к более модной модели, обязательно погрузитесь в линейную регрессию. Замечено, что следование структурированному подходу при анализе результата линейной регрессии может помочь выявить гораздо более сложные взаимосвязи, как показано выше.

Прокомментируйте или свяжитесь со мной в случае обратной связи или каких-либо конкретных тем, которые вы хотели бы, чтобы я затронул в следующих статьях.

Спасибо, что нашли время, чтобы прочитать эту статью, и удачного обучения.

  • * Особая благодарность Нимешане за помощь в написании различных частей этой статьи. **

Ссылка на блокнот Jupyter: https://github.com/quantsbin/ISLRpybin/tree/main/islrpybin/notebooks/chapter3