Термин «несколько» в множественной линейной регрессии представляет отношение между двумя или более независимыми входными переменными и переменной ответа.
Множественная линейная регрессия необходима, когда одной переменной недостаточно для создания хорошей модели и точных прогнозов.
Давайте начнем понимать это с набора данных о жилье….
Постановка задачи.
Рассмотрим компанию по недвижимости, у которой есть набор данных, содержащий цены на недвижимость в регионе Дели. Он хочет использовать данные для оптимизации продажных цен на недвижимость на основе таких важных факторов, как площадь, спальни, парковка и т. Д.
По сути, компания хочет -
- Чтобы определить переменные, влияющие на цены на жилье, например площадь, количество комнат, санузлов и т. д.
- Чтобы создать линейную модель, которая количественно связывает цены на жилье с такими переменными, как количество комнат, площадь, количество ванных комнат и т. Д.
- Чтобы знать точность модели, то есть насколько хорошо эти переменные могут предсказать цены на жилье.
Приступим к кодированию ...
Шаг 1. Чтение и понимание данных
Давайте сначала импортируем NumPy и Pandas и прочитаем набор данных жилья.
Импорт необходимых библиотек
# importing required libraries import numpy as np import pandas as pd
Чтение набора данных
housing = pd.read_csv("Housing.csv")
Отображение набора данных
# Check the head of the dataset housing.head()
Шаг 2: Визуализация данных
А теперь давайте займемся самым важным шагом - пониманием данных.
- Если есть какая-то очевидная мультиколлинеарность, это первое место, где ее можно уловить.
- Здесь вы также сможете определить, имеют ли некоторые предикторы прямую связь с переменной результата.
Мы будем визуализировать наши данные с помощью matplotlib
и seaborn
.
import matplotlib.pyplot as plt import seaborn as sns
Визуализация числовых переменных
Давайте сделаем парный график всех числовых переменных.
sns.pairplot(housing) plt.show()
Визуализация категориальных переменных
Как вы могли заметить, есть также несколько категориальных переменных. Давайте построим диаграмму для некоторых из этих переменных.
plt.figure(figsize=(20, 12)) plt.subplot(2,3,1) sns.boxplot(x = 'mainroad', y = 'price', data = housing) plt.subplot(2,3,2) sns.boxplot(x = 'guestroom', y = 'price', data = housing) plt.subplot(2,3,3) sns.boxplot(x = 'basement', y = 'price', data = housing) plt.subplot(2,3,4) sns.boxplot(x = 'hotwaterheating', y = 'price', data = housing) plt.subplot(2,3,5) sns.boxplot(x = 'airconditioning', y = 'price', data = housing) plt.subplot(2,3,6) sns.boxplot(x = 'furnishingstatus', y = 'price', data = housing) plt.show()
Мы также можем визуализировать некоторые из этих категориальных характеристик параллельно, используя аргумент hue
. Ниже приведен график для furnishingstatus
с airconditioning
в качестве оттенка.
plt.figure(figsize = (10, 5)) sns.boxplot(x = 'furnishingstatus', y = 'price', hue=airconditioning', data = housing) plt.show()
Шаг 3: Подготовка данных
- Как видите, в нашем наборе данных много столбцов со значениями «Да» или «Нет».
- Но для того, чтобы соответствовать линии регрессии, нам потребуются числовые значения, а не строка. Следовательно, нам нужно преобразовать их в единицы и нули, где 1 - это «Да», а 0 - «Нет».
# List of variables to map varlist = ['mainroad', 'guestroom', 'basement', 'hotwaterheating', 'airconditioning', 'prefarea'] # Defining the map function def binary_map(x): return x.map({'yes': 1, "no": 0}) # Applying the function to the housing list housing[varlist] = housing[varlist].apply(binary_map) # Check the housing dataframe now housing.head()
Фиктивные переменные
Переменная furnishingstatus
имеет три уровня. Нам также нужно преобразовать эти уровни в целые числа.
Для этого мы будем использовать что-то под названием dummy variables
.
# Get the dummy variables for the feature 'furnishingstatus' and store it in a new variable - 'status' status = pd.get_dummies(housing['furnishingstatus'])
Здесь мы создаем фиктивные переменные для переменной «Furnishingstatus».
# Check what the dataset 'status' looks like status.head()
Теперь вам не нужны три столбца. Вы можете опустить столбец furnished
, так как тип меблировки можно определить только по последним двум столбцам, где -
00
будет соответствоватьfurnished
01
будет соответствоватьunfurnished
10
будет соответствоватьsemi-furnished
Это сделано для того, чтобы избежать эффектов избыточности и мультиколлинеарности.
# Let's drop the first column from status df using 'drop_first = True' status = pd.get_dummies(housing['furnishingstatus'], drop_first = True)
здесь мы отбрасываем переменную снабженная
# Add the results to the original housing dataframe housing = pd.concat([housing, status], axis = 1)
объединение новых фиктивных переменных с набором данных
# Drop 'furnishingstatus' as we have created the dummies for it housing.drop(['furnishingstatus'], axis = 1, inplace = True)
Отбрасываем Furnishingstatus, поскольку мы уже создали для него фиктивные переменные.
housing.head()
Шаг 4: Разделение данных на наборы для обучения и тестирования
Как вы знаете, первый базовый шаг к регрессии - это разделение на поезд и тест.
from sklearn.model_selection import train_test_split # We specify this so that the train and test data set always have the same rows, respectively np.random.seed(0) df_train, df_test = train_test_split(housing, train_size = 0.7, test_size = 0.3, random_state = 100)
Изменение масштаба функций
Здесь мы видим, что, за исключением area
, все столбцы имеют небольшие целочисленные значения. Поэтому чрезвычайно важно масштабировать переменные так, чтобы они имели сопоставимый масштаб.
Если у нас нет сопоставимых шкал, то некоторые коэффициенты, полученные путем подбора регрессионной модели, могут быть очень большими или очень маленькими по сравнению с другими коэффициентами.
Это может сильно раздражать во время оценки модели. Поэтому рекомендуется использовать стандартизацию или нормализацию, чтобы единицы полученных коэффициентов находились в одной шкале.
Как мы знаем, есть два распространенных способа масштабирования:
- Мин-макс масштабирование
- Стандартизация (среднее-0, сигма-1)
На этот раз мы будем использовать масштабирование MinMax.
from sklearn.preprocessing import MinMaxScaler
Примените scaler () ко всем столбцам, кроме переменных «да-нет» и «фиктивные».
scaler = MinMaxScaler() # Apply scaler() to all the columns except the 'yes-no' and 'dummy' variables num_vars = ['area', 'bedrooms', 'bathrooms', 'stories', 'parking','price'] df_train[num_vars] = scaler.fit_transform(df_train[num_vars])
Давайте проверим коэффициенты корреляции, чтобы увидеть, какие переменные сильно коррелированы.
# Let's check the correlation coefficients to see which variables are highly correlated plt.figure(figsize = (16, 10)) sns.heatmap(df_train.corr(), annot = True, cmap="YlGnBu") plt.show()
Как вы могли заметить, area
кажется наиболее коррелированным с price
. Давайте посмотрим на график пары area
против price
.
plt.figure(figsize=[6,6]) plt.scatter(df_train.area, df_train.price) plt.show()
Итак, мы выбираем area
в качестве первой переменной и попытаемся подогнать под нее линию регрессии.
Разделение на наборы X и Y для построения модели
y_train = df_train.pop('price') X_train = df_train
Шаг 5: Построение линейной модели
Проведите линию регрессии по данным обучения, используя statsmodels
.
Помните, что в statsmodels
вам нужно явно подогнать константу с помощью sm.add_constant(X)
, потому что, если мы не выполним этот шаг, statsmodels
по умолчанию соответствует линии регрессии, проходящей через начало координат.
import statsmodels.api as sm # Add a constant X_train_lm = sm.add_constant(X_train[['area']]) # Create a first fitted model lr = sm.OLS(y_train, X_train_lm).fit()
lr.params возвращает коэффициенты переменной.
# Check the parameters obtained lr.params
# Let's visualise the data with a scatter plot and the fitted regression line plt.scatter(X_train_lm.iloc[:, 1], y_train) plt.plot(X_train_lm.iloc[:, 1], 0.127 + 0.462*X_train_lm.iloc[:, 1], 'r') plt.show()
Распечатаем сводку модели линейной регрессии.
# Print a summary of the linear regression model obtained print(lr.summary())
Полученное значение R-квадрата составляет 0.283
.
Добавление еще одной переменной
Поскольку у нас так много переменных, мы явно можем добиться большего. Итак, давайте продолжим и добавим вторую наиболее коррелированную переменную, то есть bathrooms
.
# Assign all the feature variables to X X_train_lm = X_train[['area', 'bathrooms']]
импорт библиотеки статистики и установка OLS
# Build a linear model import statsmodels.api as sm X_train_lm = sm.add_constant(X_train_lm) lr = sm.OLS(y_train, X_train_lm).fit() lr.params
# Check the summary print(lr.summary())
Мы явно улучшили модель, поскольку значение скорректированного R-квадрата, поскольку его значение выросло до 0.477
с 0.281
. Давайте продолжим и добавим еще одну переменную bedrooms
.
# Assign all the feature variables to X X_train_lm = X_train[['area', 'bathrooms','bedrooms']] # Build a linear model import statsmodels.api as sm X_train_lm = sm.add_constant(X_train_lm) lr = sm.OLS(y_train, X_train_lm).fit() lr.params
# Print the summary of the model print(lr.summary())
Мы снова улучшили скорректированный R-квадрат. Теперь давайте продолжим и добавим все переменные функции.
Добавление всех переменных в модель
# Check all the columns of the dataframe housing.columns
позволяет построить линейную модель
#Build a linear model import statsmodels.api as sm X_train_lm = sm.add_constant(X_train) lr_1 = sm.OLS(y_train, X_train_lm).fit() lr_1.params
print(lr_1.summary())
Глядя на p-значения, кажется, что некоторые из переменных не являются действительно значимыми (в присутствии других переменных).
Может, мы могли бы уронить немного?
Мы могли бы просто отбросить переменную с наивысшим несущественным значением p. Лучше было бы дополнить это информацией VIF.
Проверка VIF
Коэффициент инфляции дисперсии, или VIF, дает базовое количественное представление о том, насколько переменные характеристик коррелированы друг с другом. Это чрезвычайно важный параметр для тестирования нашей линейной модели. Формула для расчета VIF
:
VIF = 1/1-R².
# Check for the VIF values of the feature variables. from statsmodels.stats.outliers_influence import variance_inflation_factor # Create a dataframe that will contain the names of all the feature variables and their respective VIFs vif = pd.DataFrame() vif['Features'] = X_train.columns vif['VIF'] = [variance_inflation_factor(X_train.values, i) for i in range(X_train.shape[1])] vif['VIF'] = round(vif['VIF'], 2) vif = vif.sort_values(by = "VIF", ascending = False) vif
Обычно мы хотим, чтобы VIF был меньше 5. Поэтому явно есть некоторые переменные, которые нам нужно отбросить.
Почему мы вообще рассматриваем цифру 5 ??? Я объясню…
Предположим, VIF = 5. т.е.
1/1-R² = 5
1-R² = 1/5
1-R² = 0.2
R² = 0.8
Это означает, что любая переменная с показателем VIF выше 5 объясняет более 80% дисперсии данных.
В этом случае мы можем столкнуться с проблемами мультиколлинеарности.
Отсюда и цифра 5.
Удаление переменной и обновление модели
Как видно из сводки и фрейма данных VIF, некоторые переменные по-прежнему не имеют значения. Одна из этих переменных - semi-furnished
, поскольку она имеет очень высокое значение p, равное 0.938
. Давайте продолжим и отбросим эти переменные.
# Dropping highly correlated variables and insignificant variables X = X_train.drop('semi-furnished', 1,) # Build a third fitted model X_train_lm = sm.add_constant(X) lr_2 = sm.OLS(y_train, X_train_lm).fit() # Print the summary of the model print(lr_2.summary())
Давайте снова посчитаем оценку vif.
# Calculate the VIFs again for the new model vif = pd.DataFrame() vif['Features'] = X.columns vif['VIF'] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])] vif['VIF'] = round(vif['VIF'], 2) vif = vif.sort_values(by = "VIF", ascending = False) vif
Удаление переменной и обновление модели
Как вы можете заметить, некоторые переменные имеют высокие значения VIF, а также высокие значения p. Такие переменные несущественны, и их следует отбросить.
Как вы могли заметить, переменная bedroom
имеет значительно высокий VIF (6.6
) и высокое значение p (0.206
). Следовательно, эта переменная не очень полезна, и ее следует отбросить.
# Dropping highly correlated variables and insignificant variables X = X.drop('bedrooms', 1) # Build a second fitted model X_train_lm = sm.add_constant(X) lr_3 = sm.OLS(y_train, X_train_lm).fit() # Print the summary of the model print(lr_3.summary())
# Calculate the VIFs again for the new model vif = pd.DataFrame() vif['Features'] = X.columns vif['VIF'] = [variance_inflation_factor(X.values, i) for i in range(X.shape[1])] vif['VIF'] = round(vif['VIF'], 2) vif = vif.sort_values(by = "VIF", ascending = False) vif
Удаление переменной и обновление модели
Как вы могли заметить, при падении semi-furnised
VIF также уменьшился до mainroad
, так что теперь он меньше 5.
Но из резюме мы все еще можем видеть, что некоторые из них имеют высокое значение p. basement
, например, имеет p-значение 0,03. Мы также должны отбросить эту переменную.
X = X.drop('basement', 1) # Build a fourth fitted model X_train_lm = sm.add_constant(X) lr_4 = sm.OLS(y_train, X_train_lm).fit() lr_4.params
Как видите, значения VIF и p находятся в допустимом диапазоне. Поэтому мы делаем наши прогнозы, используя только эту модель.
Окончательная оценка R² для нашего набора данных о поездах составляет 0,676.
Шаг 7: Остаточный анализ данных поезда
Итак, теперь, чтобы проверить, нормально ли распределены члены ошибок (что на самом деле является одним из основных предположений линейной регрессии),
давайте построим гистограмму ошибок и посмотрим, как она выглядит.
y_train_price = lr_4.predict(X_train_lm) # Plot the histogram of the error terms fig = plt.figure() sns.distplot((y_train - y_train_price), bins = 20) fig.suptitle('Error Terms', fontsize = 20) plt.xlabel('Errors', fontsize = 18)
Шаг 8: Прогнозы с использованием окончательной модели
Теперь, когда мы подобрали модель и проверили нормальность ошибок, пора сделать прогнозы, используя окончательную, то есть четвертую модель.
Применение масштабирования к тестовым наборам
num_vars = ['area', 'bedrooms', 'bathrooms', 'stories', 'parking','price'] df_test[num_vars] = scaler.transform(df_test[num_vars])
Разделение на X_test и y_test
y_test = df_test.pop('price') X_test = df_test # Adding constant variable to test dataframe X_test_m4 = sm.add_constant(X_test) # Creating X_test_m4 dataframe by dropping variables from X_test_m4 X_test_m4 = X_test_m4.drop(["bedrooms", "semi-furnished", "basement"], axis = 1) # Making predictions using the fourth model y_pred_m4 = lr_4.predict(X_test_m4)
Вычисление оценки R² для тестового набора данных
from sklearn.metrics import r2_score r2_score(y_true=y_test,y_pred=y_pred_m4)
Окончательная оценка R² для нашего набора данных о поездах составляет 0,676.
Для нашего тестового набора данных мы получили 0,66 балла R².
Это означает, что наша модель также хорошо работает на тестовом наборе данных.
Шаг 9: Оценка модели
Давайте теперь построим график зависимости фактических значений от прогнозируемых.
# Plotting y_test and y_pred to understand the spread fig = plt.figure() plt.scatter(y_test, y_pred_m4) fig.suptitle('y_test vs y_pred', fontsize = 20) plt.xlabel('y_test', fontsize = 18) plt.ylabel('y_pred', fontsize = 16)
Мы видим, что уравнение нашей наиболее подходящей линии выглядит следующим образом:
𝑝𝑟𝑖𝑐𝑒=0.236×𝑎𝑟𝑒𝑎+0.202×𝑏𝑎𝑡ℎ𝑟𝑜𝑜𝑚𝑠+0.11×𝑠𝑡𝑜𝑟𝑖𝑒𝑠+0.05×𝑚𝑎𝑖𝑛𝑟𝑜𝑎𝑑+0.04×𝑔𝑢𝑒𝑠𝑡𝑟𝑜𝑜𝑚+0.0876×ℎ𝑜𝑡𝑤𝑎𝑡𝑒𝑟ℎ𝑒𝑎𝑡𝑖𝑛𝑔+0.0682×𝑎𝑖𝑟𝑐𝑜𝑛𝑑𝑖𝑡𝑖𝑜𝑛𝑖𝑛𝑔+0.0629×𝑝𝑎𝑟𝑘𝑖𝑛𝑔+0.0637×𝑝𝑟𝑒𝑓𝑎𝑟𝑒𝑎−0.0337×𝑢𝑛𝑓𝑢𝑟𝑛𝑖𝑠ℎ𝑒𝑑.
Примечание. Чтобы отбросить переменные, которые имеют низкую корреляцию с целевой переменной, мы можем рекурсивное исключение функций, предоставляемое sklearn.
Вывод:
Надеюсь, читатели получат интуитивное представление о множественной линейной регрессии.