123 KiB
123 KiB
Бизнес цели:
- Оптимизация ценовой стратегии: анализ факторов, влияющих на стоимость недвижимости, чтобы помочь продавцам устанавливать конкурентоспособные цены и увеличивать прибыль.
- Улучшение инвестиционных решений: предоставление аналитики для инвесторов, чтобы они могли определить наиболее выгодные районы и типы недвижимости для вложений.
Цели технического проекта:
- Создание модели машинного обучения для прогнозирования стоимости недвижимости на основе таких характеристик, как площадь дома, количество спален и ванных комнат, расположение, возраст дома, наличие бассейна и других факторов.
- Разработка системы, которая анализирует волатильность цен (показатель изменчивости цены актива за определённый период времени) на недвижимость в разных районах, учитывая исторические данные о продажах, сезонные колебания и демографические изменения, чтобы выявить наиболее стабильные и перспективные зоны для инвестиций.
In [68]:
import pandas as pd
from sklearn.model_selection import train_test_split
from imblearn.under_sampling import RandomUnderSampler
df = pd.read_csv("data/kc_house_data.csv")
print(df)
# Преобразование даты продажи в числовой формат (кол-во дней с 01.01.1970)
df['date'] = pd.to_datetime(df['date'])
df['date_numeric'] = (df['date'] - pd.Timestamp('1970-01-01')).dt.days
print(df['date_numeric'])
In [69]:
from sklearn.preprocessing import OneHotEncoder
encoder = OneHotEncoder(sparse_output=False, drop="first")
encoded_values = encoder.fit_transform(df[["bathrooms", "bedrooms"]])
encoded_columns = encoder.get_feature_names_out(["bathrooms", "bedrooms"])
encoded_values_df = pd.DataFrame(encoded_values, columns=encoded_columns)
encoded_values_df
Out[69]:
In [70]:
# Функция для разбиение на выборки
def split_stratified_into_train_val_test(
df_input,
stratify_colname="y",
frac_train=0.6,
frac_val=0.2,
frac_test=0.2,
random_state=None,
):
if frac_train + frac_val + frac_test != 1.0:
raise ValueError(
"fractions %f, %f, %f do not add up to 1.0"
% (frac_train, frac_val, frac_test)
)
if stratify_colname not in df_input.columns:
raise ValueError("%s is not a column in the dataframe" % (stratify_colname))
X = df_input
y = df_input[[stratify_colname]]
df_train, df_temp, y_train, y_temp = train_test_split(
X, y, stratify=y, test_size=(1.0 - frac_train), random_state=random_state
)
relative_frac_test = frac_test / (frac_val + frac_test)
df_val, df_test, y_val, y_test = train_test_split(
df_temp,
y_temp,
stratify=y_temp,
test_size=relative_frac_test,
random_state=random_state,
)
assert len(df_input) == len(df_train) + len(df_val) + len(df_test)
return df_train, df_val, df_test
# Определение бинов для цены
bins = [
df["price"].min(),
df["price"].quantile(0.33),
df["price"].quantile(0.66),
df["price"].max(),
]
labels = ["Low", "Medium", "High"]
df["price_binned"] = pd.cut(df["price"], bins=bins, labels=labels)
df = df.dropna()
# Стратифицированное (для сохранения пропорций)
# разбиение на обучающую, валидационную и тестовую выборки
df_train, df_val, df_test = split_stratified_into_train_val_test(
df, stratify_colname="price_binned", frac_train=0.60, frac_val=0.20, frac_test=0.20
)
print(df_train.columns)
print("Обучающая выборка: ", df_train.shape)
print(df_train["price"].value_counts())
print("Валидационная выборка: ", df_val.shape)
print(df_val["price"].value_counts())
print("Тестовая выборка: ", df_test.shape)
print(df_test["price"].value_counts())
print("Обучающая выборка по категориям: ", df_train["price_binned"].value_counts())
print("Валидационная выборка по категориям: ", df_val["price_binned"].value_counts())
print("Тестовая выборка по категориям: ", df_test["price_binned"].value_counts())
In [71]:
rus = RandomUnderSampler(random_state=42)
X_resampled, y_resampled = rus.fit_resample(df_train, df_train["price_binned"])
# Создание датафрейма для результирующей выборки
df_train_rus = pd.DataFrame(X_resampled)
# Добавление целевой переменной в новый датафрейм
df_train_rus["price_binned"] = y_resampled
print("Обучающая выборка после undersampling: ", df_train_rus.shape)
print(df_train_rus["price_binned"].value_counts())
In [72]:
# Преобразование категориального столбца price_binned в дамми-переменные
# Добавляем новые столбцы (Low, Medium, High) и выставляем 1 или 0 в зависмоитси от цены
df_train = pd.get_dummies(df_train, columns=["price_binned"])
# Binning площади в 4 категории на основе квартилей
# Новый столбец с со значением 0, если оно подходит первому квартилю (категории значений) и тд
df_train["Area_binned"] = pd.qcut(df_train["sqft_living"], q=4, labels=False)
# Вывод обновленного датафрейма
print(df_train)
In [73]:
from sklearn.preprocessing import StandardScaler
# Нормализация значений для указанных столбцов
# чтобы значения разных столбов в среднем были 0, а стандартное отклонение - 1
scaler = StandardScaler()
df_train[["price", "sqft_living", "sqft_lot", "floors", "waterfront"]] = (
scaler.fit_transform(
df_train[["price", "sqft_living", "sqft_lot", "floors", "waterfront"]]
)
)
# Расчет волатильности (разница между площадью дома и площадью участка)
df_train["Volatility"] = (
df_train["sqft_living"] - df_train["sqft_lot"]
)
# Вывод обновленного датафрейма
print(df_train)
In [74]:
import featuretools as ft
# Убеждаемся, что столбец 'date' в формате datetime
df["date"] = pd.to_datetime(df["date"])
# Установка индекса на столбец 'id' (предполагается, что 'id' уникален)
df.set_index("id", inplace=True)
# Создание EntitySet для объединения разных датасетов для удобного использования
es = ft.EntitySet(id="house_sales")
es = es.add_dataframe(
dataframe_name="house_data",
dataframe=df,
index="id", # уникальный идентификатор
)
# Генерация признаков
feature_matrix, feature_defs = ft.dfs(entityset=es, target_dataframe_name="house_data")
# Показать определение признаков
print(feature_defs)
In [75]:
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, mean_squared_error
# Копирование датафрейма для регрессионного анализа
df_train_regression = df_train.copy()
# Определение признаков и целевой переменной
X_train = df_train_regression.drop(
["price", "date"], axis=1
)
y_train = df_train_regression["price"] # Целевая переменная
X_test = df_test.drop(
["price", "date"], axis=1
)
y_test = df_test["price"] # Целевая переменная
# Преобразование категориальных признаков в дамми-переменные
# (создание столбцов значений со значениями 0 или 1, если это булевой столбец)
X_train_encoded = pd.get_dummies(X_train, drop_first=True)
X_test_encoded = pd.get_dummies(X_test, drop_first=True)
# Устранение различий в количестве столбцов между обучающей и тестовой выборками
X_test_encoded = X_test_encoded.reindex(columns=X_train_encoded.columns, fill_value=0)
# Проверка типов данных
print(X_train_encoded.dtypes)
# Обучение модели линейной регрессии (поиск зависимостей между признаками)
model = LinearRegression()
model.fit(X_train_encoded, y_train)
# Предсказание цены на тестовой выборке
y_pred = model.predict(X_test_encoded)
# Оценка качества модели
mae = mean_absolute_error(y_test, y_pred)
mse = mean_squared_error(y_test, y_pred)
rmse = mse**0.5 # Корень из MSE для RMSE
print("MAE:", mae)
print("MSE:", mse)
print("RMSE:", rmse)
In [ ]:
In [76]:
# Оценка скорости вычисления
import time
start_time = time.time()
model.fit(X_train_encoded, y_train)
training_time = time.time() - start_time
start_time = time.time()
predictions = model.predict(X_test_encoded)
prediction_time = time.time() - start_time
print(
f"Время, затраченное на обучение модели: {training_time}.\nВремя, затраченное на предсказание: {prediction_time}"
)
In [77]:
# Оценка корреляции
import seaborn as sns
import matplotlib.pyplot as plt
corr_matrix = df_train_regression.corr()
sns.heatmap(corr_matrix, annot=False)
plt.show()