298 KiB
298 KiB
In [10]:
import pandas as pd;
df = pd.read_csv("data/house_data.csv", sep=",", nrows=10000)
df.columns
Out[10]:
Определение бизнес целей:¶
- Прогнозирование цены недвижимости
- Оценка влияния факторов на цену недвижимости
Определение целей технического проекта:¶
- Построить модель, которая будет прогнозировать стоимость недвижимости на основе данных характеристик.
- Провести анализ данных для выявления факторов, которые больше всего влияют на цену
Проверка данных на пропуски¶
In [11]:
for i in df.columns:
null_rate = df[i].isnull().sum() / len(df) * 100
if null_rate > 0:
print(f'{i} Процент пустых значений: %{null_rate:.2f}')
print(df.isnull().sum())
df.isnull().any()
Out[11]:
Пустых значений нет, номинальных значений тоже.
Проверка выбросов, и их усреднение¶
In [12]:
numeric_columns = ['price']
for column in numeric_columns:
if pd.api.types.is_numeric_dtype(df[column]): # Проверяем, является ли колонка числовой
q1 = df[column].quantile(0.25) # Находим 1-й квартиль (Q1)
q3 = df[column].quantile(0.75) # Находим 3-й квартиль (Q3)
iqr = q3 - q1 # Вычисляем межквартильный размах (IQR)
# Определяем границы для выбросов
lower_bound = q1 - 1.5 * iqr # Нижняя граница
upper_bound = q3 + 1.5 * iqr # Верхняя граница
# Подсчитываем количество выбросов
outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]
outlier_count = outliers.shape[0]
# Устраняем выбросы: заменяем значения ниже нижней границы на саму нижнюю границу, а выше верхней — на верхнюю
df[column] = df[column].apply(lambda x: lower_bound if x < lower_bound else upper_bound if x > upper_bound else x)
print(f"Колонка {column}:")
print(f" Есть выбросы: {'Да' if outlier_count > 0 else 'Нет'}")
print(f" Количество выбросов: {outlier_count}")
print(f" Минимальное значение: {df[column].min()}")
print(f" Максимальное значение: {df[column].max()}")
print(df.head())
Создание выборок¶
Так как мы будет предсказывать цену, то и целевым признаком будет параметр цены.
In [13]:
from sklearn.model_selection import train_test_split
#Разделение данных на обучающую и тестовую выборки
train_data, test_data = train_test_split(df, test_size=0.2, random_state=42)
#Разделение обучающей выборки на обучающую и контрольную
train_data, val_data = train_test_split(train_data, test_size=0.2, random_state=42)
print("Размер обучающей выборки:", len(train_data))
print("Размер контрольной выборки:", len(val_data))
print("Размер тестовой выборки:", len(test_data))
Визуализвация цен в выборках¶
In [14]:
import matplotlib.pyplot as plt
import seaborn as sns
def plot_price_dist(data, title):
sns.histplot(data['price'], kde=True)
plt.title(title)
plt.xlabel('Цена')
plt.ylabel('Частота')
plt.show()
plot_price_dist(train_data, 'Распределение цены в обучающей выборке')
plot_price_dist(val_data, 'Распределение цены в контрольной выборке')
plot_price_dist(test_data, 'Распределение цены в тестовой выборке')
print("Средняя цена в обучающей выборке: ", train_data['price'].mean())
print("Средняя цена в контрольной выборке: ", val_data['price'].mean())
print("Средняя цена в тестовой выборке: ", test_data['price'].mean())
Преобразование целевой переменной в категории дискретизацией¶
In [15]:
import numpy as np
labels = ["low", "middle", "high", "very_high"]
num_bins = 4
hist1, bins1 = np.histogram(train_data["price"].fillna(train_data["price"].median()), bins=num_bins)
bins1,hist1
pd.concat([train_data["price"], pd.cut(train_data["price"], list(bins1))], axis=1).head(10)
pd.concat([train_data["price"], pd.cut(train_data["price"], list(bins1), labels=labels)], axis=1).head()
train_data['price_category'] = pd.cut(train_data["price"], list(bins1), labels=labels)
test_data['price_category'] = pd.cut(train_data["price"], list(bins1), labels=labels)
val_data['price_category'] = pd.cut(train_data["price"], list(bins1), labels=labels)
sns.countplot(x=train_data['price_category'])
plt.title('Распределение цены в обучающей выборке')
plt.xlabel('Категория цены')
plt.ylabel('Частота')
plt.show()
Конструирование признаков¶
- Прогнозирование цен недвижимости. Цель технического проекта: Разработка модели машинного обучения для точного прогнозирования рыночной стоимости недвижимости.
- Оценка влияния факторов на цену недвижимости Цель технического проекта: Разработки модели для анализа данных для выявления факторов, которые больше всего влияют на цену
Конструирование признаков¶
Унитарное кодирование - замена категориальных признаков бинарными значениями.
In [16]:
categorical_features = ['price_category']
train_data_encoded = pd.get_dummies(train_data, columns=categorical_features)
val_data_encoded = pd.get_dummies(val_data, columns=categorical_features)
test_data_encoded = pd.get_dummies(test_data, columns=categorical_features)
print("Столбцы train_data_encoded:", train_data_encoded.columns.tolist())
print("Столбцы val_data_encoded:", val_data_encoded.columns.tolist())
print("Столбцы test_data_encoded:", test_data_encoded.columns.tolist())
Ручной синтез¶
Создание новых признаков на основе экспертных знаний и логики предметной области. Новый признак будет - цена недвижимости за фут.
In [17]:
train_data_encoded['price_per_sqft'] = df['price'] / df['sqft_living']
val_data_encoded['price_per_sqft'] = df['price'] / df['sqft_living']
test_data_encoded['price_per_sqft'] = df['price'] / df['sqft_living']
test_data_encoded
Out[17]:
Масштабирование признаков¶
Это процесс изменения диапазона признаков, чтобы равномерно распределить значения.
In [18]:
from sklearn.preprocessing import StandardScaler
numerical_features = ['bedrooms']
scaler = StandardScaler()
train_data_encoded[numerical_features] = scaler.fit_transform(train_data_encoded[numerical_features])
val_data_encoded[numerical_features] = scaler.transform(val_data_encoded[numerical_features])
test_data_encoded[numerical_features] = scaler.transform(test_data_encoded[numerical_features])
Конструирование признаков с применением фреймворка Featuretools¶
In [21]:
import featuretools as ft
# Удаление дубликатов по идентификатору
train_data_encoded = train_data_encoded.drop_duplicates(subset='id', keep='first')
#Создание EntitySet
es = ft.EntitySet(id='house_data')
#Добавление датафрейма в EntitySet
es = es.add_dataframe(dataframe_name='houses', dataframe=train_data_encoded, index='id')
#Генерация признаков
feature_matrix, feature_defs = ft.dfs(entityset=es, target_dataframe_name='houses', max_depth=2)
feature_matrix
Out[21]:
Оценка качества¶
In [39]:
import time
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
from sklearn.metrics import r2_score, mean_absolute_error
from sklearn.model_selection import cross_val_score
X = feature_matrix.drop('price', axis=1)
y = feature_matrix['price']
#Делим на выборки
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
#Начнем обучение
model = LinearRegression()
start_time = time.time()
model.fit(X_train, y_train)
train_time = time.time() - start_time
#Вычесляем показательную способность
y_pred = model.predict(X_test)
mse = mean_squared_error(y_test, y_pred)
rmse = mean_squared_error(y_test, y_pred, squared=False)
r2 = r2_score(y_test, y_pred)
mae = mean_absolute_error(y_test, y_pred)
print()
print(f"Время обучения: {train_time:.2f} секунд")
print("Метрики:")
print(f"MSE: {mse}")
print(f"RMSE: {rmse}")
print(f"R²: {r2}")
print(f"MAE: {mae} \n")
# Визуализация результатов
plt.figure(figsize=(10, 6))
plt.scatter(y_test, y_pred, alpha=0.5)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], color="black")
plt.xlabel('Фактическая цена')
plt.ylabel('Прогнозируемая цена')
plt.title('Фактическая цена по сравнению с прогнозируемой')
plt.show()