399 KiB
Загрузка данных из файла¶
import pandas as pd
df = pd.read_csv("../static/csv/mobile phone price prediction.csv")
attributes = df.columns
for attribute in attributes:
print(attribute)
# Вывод первых 10 строк
print(df.head(10))
Бизнес-цели¶
- Классифицировать мобильные устройства по ценовым категориям (например, бюджетные, средний класс, флагманы).
- Определить, какие характеристики мобильных устройств наиболее сильно влияют на их рейтинг.
Подготовка данных.¶
import numpy as np
# Проверка на пропущенные значения
missing_data = df.isnull().sum()
print("Пропущенные данные по каждому столбцу:")
print(missing_data)
При проверке на шум можно заметить выброс в 75 оценке. Цена там запредельная.
Для удаления выбросов из датасета можно использовать метод межквартильного размаха. Зашумленность не очень высокая. Покрытие данных высокое и подошло бы для поставленной задачи по актуальности.
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
# Загрузка данных
df = pd.read_csv("../static/csv/mobile phone price prediction.csv",delimiter=',')
df.drop(['Unnamed: 0'], axis=1, inplace=True)
df['Price'] = df['Price'].str.replace(',', '').astype(float)
df.describe(include='all')
f, ax = plt.subplots(figsize=(10,6))
sns.despine(f)
sns.scatterplot(data=df, x='Spec_score', y='Price')
import pandas as pd
import matplotlib.pyplot as plt
# Загрузка данных
df = pd.read_csv("..//static//csv//mobile phone price prediction.csv")
df['Spec_score'] = df['Spec_score'].astype(int)
df['Price'] = df['Price'].str.replace(',', '').astype(float)
# Выбор столбцов для анализа
column1 = 'Spec_score'
column2 = 'Price'
# Функция для удаления выбросов
def remove_outliers(df, column):
Q1 = df[column].quantile(0.25)
Q3 = df[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
return df[(df[column] >= lower_bound) & (df[column] <= upper_bound)]
# Удаление выбросов для каждого столбца
df_cleaned = df.copy()
for column in [column1, column2]:
df_cleaned = remove_outliers(df_cleaned, column)
# Построение точечной диаграммы после удаления выбросов
plt.figure(figsize=(10, 6))
plt.scatter(df_cleaned[column1], df_cleaned[column2], alpha=0.5)
plt.xlabel(column1)
plt.ylabel(column2)
plt.title(f'Scatter Plot of {column1} vs {column2} (After Removing Outliers)')
plt.show()
# Вывод количества строк до и после удаления выбросов
print(f"Количество строк до удаления выбросов: {len(df)}")
print(f"Количество строк после удаления выбросов: {len(df_cleaned)}")
Теперь очистим датасет отпустых строк
df.dropna(inplace=True)
print(df.shape)
print(df.isnull().any())
Разбиение данных на обучающую, контрольную и тестовую выборки.¶
import pandas as pd
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
# Разделение признаков (features) и целевой переменной (target)
X = df.drop(columns=['company']) # Признаки (все столбцы, кроме 'сompany')
y = df['company'] # Целевая переменная (сompany)
# Разбиение на обучающую (60%), валидационную (20%) и тестовую (20%) выборки
X_train, X_temp, y_train, y_temp = train_test_split(X, y, test_size=0.4, random_state=42)
X_val, X_test, y_val, y_test = train_test_split(X_temp, y_temp, test_size=0.5, random_state=42)
# Проверка размеров выборок
print(f"Размеры выборок:")
print(f"Обучающая выборка: {X_train.shape[0]} записей")
print(f"Контрольная выборка: {X_val.shape[0]} записей")
print(f"Тестовая выборка: {X_test.shape[0]} записей")
# Визуализация распределения марок в каждой выборке
plt.figure(figsize=(12, 6))
plt.subplot(1, 1, 1)
plt.xticks(rotation=45)
plt.hist(y_train, bins=20, color='blue', alpha=0.7)
plt.title('Обучающая выборка')
plt.xlabel('Марка')
plt.ylabel('Количество')
plt.show()
Данные не сбалансированы, так как существует большая разница в количестве наблюдений для разных марок. Это может привести к тому, что модель будет хуже предсказывать цены для марок, численность которых в выборке меньше, а для других - лучше. Применим методы приращения.
from imblearn.over_sampling import RandomOverSampler
from imblearn.under_sampling import RandomUnderSampler
# Разделение на обучающую и тестовую выборки
train_df, test_df = train_test_split(df, test_size=0.2, random_state=42)
# Разделение обучающей выборки на обучающую и контрольную
train_df, val_df = train_test_split(train_df, test_size=0.25, random_state=42)
def check_balance(df, name):
counts = df['company'].value_counts()
print(f"Распределение company в {name}:")
print(counts)
print()
def oversample(df):
X = df.drop('company', axis=1)
y = df['company']
oversampler = RandomOverSampler(random_state=42)
X_resampled, y_resampled = oversampler.fit_resample(X, y)
resampled_df = pd.concat([X_resampled, y_resampled], axis=1)
return resampled_df
train_df_oversampled = oversample(train_df)
val_df_oversampled = oversample(val_df)
test_df_oversampled = oversample(test_df)
check_balance(train_df_oversampled, "обучающей выборке после oversampling")
check_balance(val_df_oversampled, "контрольной выборке после oversampling")
check_balance(test_df_oversampled, "тестовой выборке после oversampling")
def undersample(df):
X = df.drop('company', axis=1)
y = df['company']
undersampler = RandomUnderSampler(random_state=42) # type: ignore
X_resampled, y_resampled = undersampler.fit_resample(X, y)
resampled_df = pd.concat([X_resampled, y_resampled], axis=1)
return resampled_df
train_df_undersampled = undersample(train_df)
val_df_undersampled = undersample(val_df)
test_df_undersampled = undersample(test_df)
check_balance(train_df_undersampled, "обучающей выборке после undersampling")
check_balance(val_df_undersampled, "контрольной выборке после undersampling")
check_balance(test_df_undersampled, "тестовой выборке после undersampling")
Данные были сбалансированы. Теперь можно перейти к конструированию признаков. Поставлены следующие задачи:
- Классифицировать мобильные устройства по ценовым категориям (например, бюджетные, средний класс, флагманы).
- Определить, какие характеристики мобильных устройств наиболее сильно влияют на их рейтинг.
import pandas as pd
from sklearn.model_selection import train_test_split
from imblearn.over_sampling import RandomOverSampler
# Определение категориальных признаков
categorical_features = [
'Rating', 'Ram',
'Battery', 'Display', 'Camera', 'External_Memory', 'Android_version',
'Price', 'company', 'Inbuilt_memory', 'fast_charging',
'Screen_resolution', 'Processor'
]
# Применение one-hot encoding к обучающей выборке
train_df_resampled_encoded = pd.get_dummies(train_df_undersampled, columns=categorical_features)
# Применение one-hot encoding к контрольной выборке
val_df_encoded = pd.get_dummies(val_df_undersampled, columns=categorical_features)
# Применение one-hot encoding к тестовой выборке
test_df_encoded = pd.get_dummies(test_df_undersampled, columns=categorical_features)
Дискретизация числовых признаков
import pandas as pd
from sklearn.model_selection import train_test_split
from imblearn.over_sampling import RandomOverSampler
import re
# Загрузка данных
df = pd.read_csv("..//static//csv//mobile phone price prediction.csv")
# Извлечение числовых значений из столбца Battery
df['Battery'] = df['Battery'].apply(lambda x: int(re.search(r'\d+', x).group()) if re.search(r'\d+', x) else None)
df['Ram'] = df['Ram'].apply(lambda x: int(re.search(r'\d+', x).group()) if re.search(r'\d+', x) else None)
df['Camera'] = df['Camera'].apply(lambda x: int(re.search(r'\d+', x).group()) if re.search(r'\d+', x) else None)
# Удаление запятых из столбца Price и преобразование в числовой формат
df['Price'] = df['Price'].str.replace(',', '').astype(float)
# Разделение на обучающую и тестовую выборки (например, 70% обучающая, 30% тестовая)
train_df, test_df = train_test_split(df, test_size=0.3, random_state=42)
# Разделение обучающей выборки на обучающую и контрольную (например, 70% обучающая, 30% контрольная)
train_df, val_df = train_test_split(train_df, test_size=0.3, random_state=42)
# Применение upsampling к обучающей выборке (если это необходимо)
X_train = train_df.drop('Price', axis=1) # Отделяем признаки от целевой переменной
y_train = train_df['Price'] # Целевая переменная
# Инициализация RandomOverSampler
ros = RandomOverSampler(random_state=42)
# Применение upsampling
X_train_resampled, y_train_resampled = ros.fit_resample(X_train, y_train)
# Создание нового DataFrame с балансированными данными
train_df_resampled = pd.concat([X_train_resampled, y_train_resampled], axis=1)
# Определение числовых признаков для дискретизации
numerical_features = ['Spec_score', 'Battery', 'Ram', 'Camera' ]
# Функция для дискретизации числовых признаков
def discretize_features(df, features, bins=5, labels=False):
for feature in features:
try:
df[f'{feature}_bin'] = pd.cut(df[feature], bins=bins, labels=labels)
except Exception as e:
print(f"Ошибка при дискретизации признака {feature}: {e}")
return df
# Применение дискретизации к обучающей, контрольной и тестовой выборкам
train_df_resampled = discretize_features(train_df_resampled, numerical_features)
val_df = discretize_features(val_df, numerical_features)
test_df = discretize_features(test_df, numerical_features)
# Вывод размеров выборок
print("Размер обучающей выборки после балансировки:", train_df_resampled.shape)
print("Размер контрольной выборки:", val_df.shape)
print("Размер тестовой выборки:", test_df.shape)
Ручной синтез. Создание новых признаков на основе экспертных знаний и логики предметной области.¶
# Загрузка данных
df = pd.read_csv("..//static//csv//mobile phone price prediction.csv")
# Преобразование столбца Battery в числовой формат
df['Battery'] = df['Battery'].apply(lambda x: int(re.search(r'\d+', x).group()) if re.search(r'\d+', x) else None)
# Преобразование столбцов Camera и Display в числовой формат
df['Camera'] = pd.to_numeric(df['Camera'], errors='coerce')
df['Display'] = pd.to_numeric(df['Display'], errors='coerce')
# Удаление запятых из столбца Price и преобразование в числовой формат
df['Price'] = df['Price'].str.replace(',', '').astype(float)
# Разделение на обучающую и тестовую выборки (например, 70% обучающая, 30% тестовая)
train_df, test_df = train_test_split(df, test_size=0.3, random_state=42)
# Разделение обучающей выборки на обучающую и контрольную (например, 70% обучающая, 30% контрольная)
train_df, val_df = train_test_split(train_df, test_size=0.3, random_state=42)
# Применение upsampling к обучающей выборке (если это необходимо)
X_train = train_df.drop('Price', axis=1) # Отделяем признаки от целевой переменной
y_train = train_df['Price'] # Целевая переменная
# Инициализация RandomOverSampler
ros = RandomOverSampler(random_state=42)
# Применение upsampling
X_train_resampled, y_train_resampled = ros.fit_resample(X_train, y_train)
# Создание нового DataFrame с балансированными данными
train_df_resampled = pd.concat([X_train_resampled, y_train_resampled], axis=1)
# Создание нового признака "Camera_to_Display_Ratio" на основе признаков "Camera" и "Display"
train_df_resampled['Camera_to_Display_Ratio'] = train_df_resampled['Camera'] / train_df_resampled['Display']
val_df['Camera_to_Display_Ratio'] = val_df['Camera'] / val_df['Display']
test_df['Camera_to_Display_Ratio'] = test_df['Camera'] / test_df['Display']
# Вывод размеров выборок
print("Размер обучающей выборки после балансировки:", train_df_resampled.shape)
print("Размер контрольной выборки:", val_df.shape)
print("Размер тестовой выборки:", test_df.shape)
Масштабирование признаков - это процесс преобразования числовых признаков таким образом, чтобы они имели одинаковый масштаб. Это важно для многих алгоритмов машинного обучения, которые чувствительны к масштабу признаков, таких как линейная регрессия, метод опорных векторов (SVM) и нейронные сети.¶
import pandas as pd
from sklearn.model_selection import train_test_split
from imblearn.over_sampling import RandomOverSampler
from sklearn.preprocessing import StandardScaler
# Определение числовых признаков для масштабирования
numerical_features_to_scale = ['Spec_score', 'No_of_sim', 'Ram', 'Battery', 'Display', 'Camera', 'Inbuilt_memory', 'Screen_resolution', 'Camera_to_Display_Ratio']
# Удаление строковых значений из числовых признаков
for feature in numerical_features_to_scale:
train_df_resampled[feature] = pd.to_numeric(train_df_resampled[feature], errors='coerce')
val_df[feature] = pd.to_numeric(val_df[feature], errors='coerce')
test_df[feature] = pd.to_numeric(test_df[feature], errors='coerce')
# Инициализация StandardScaler
scaler = StandardScaler()
# Масштабирование числовых признаков в обучающей выборке
train_df_resampled[numerical_features_to_scale] = scaler.fit_transform(train_df_resampled[numerical_features_to_scale])
# Масштабирование числовых признаков в контрольной и тестовой выборках
val_df[numerical_features_to_scale] = scaler.transform(val_df[numerical_features_to_scale])
test_df[numerical_features_to_scale] = scaler.transform(test_df[numerical_features_to_scale])
# Вывод размеров выборок
print("Размер обучающей выборки после балансировки:", train_df_resampled.shape)
print("Размер контрольной выборки:", val_df.shape)
print("Размер тестовой выборки:", test_df.shape)
Конструирование признаков с применением фреймворка Featuretools
import pandas as pd
from sklearn.model_selection import train_test_split
import featuretools as ft
import re
# Определение сущностей
es = ft.EntitySet(id='mobile_data')
es = es.add_dataframe(dataframe_name='train', dataframe=train_df, index='id')
# Генерация признаков
feature_matrix, feature_defs = ft.dfs(entityset=es, target_dataframe_name='train', max_depth=2)
# Преобразование признаков для контрольной и тестовой выборок
val_feature_matrix = ft.calculate_feature_matrix(features=feature_defs, entityset=es, instance_ids=val_df.index)
test_feature_matrix = ft.calculate_feature_matrix(features=feature_defs, entityset=es, instance_ids=test_df.index)
# Вывод первых нескольких строк для проверки
print("Обучающая выборка после конструирования признаков:")
print(feature_matrix.head())
print("Контрольная выборка после конструирования признаков:")
print(val_feature_matrix.head())
print("Тестовая выборка после конструирования признаков:")
print(test_feature_matrix.head())
Оценка качества¶
import pandas as pd
from sklearn.model_selection import train_test_split
from imblearn.over_sampling import RandomOverSampler
import featuretools as ft
from sklearn.ensemble import RandomForestRegressor
import re
# Оценка важности признаков
X = feature_matrix
y = train_df_resampled['Price']
# Разделение данных на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# Обучение модели
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
# Получение важности признаков
importances = model.feature_importances_
feature_names = feature_matrix.columns
# Сортировка признаков по важности
feature_importance = pd.DataFrame({'feature': feature_names, 'importance': importances})
feature_importance = feature_importance.sort_values(by='importance', ascending=False)
print("Feature Importance:")
print(feature_importance)
# Загрузка данных
df = pd.read_csv("..//static//csv//mobile phone price prediction.csv")
# Преобразование столбца Battery в числовой формат
df['Battery'] = df['Battery'].apply(lambda x: int(re.search(r'\d+', x).group()) if re.search(r'\d+', x) else None)
# Преобразование столбца Display в числовой формат
df['Camera'] = pd.to_numeric(df['Camera'], errors='coerce')
df['Display'] = pd.to_numeric(df['Display'], errors='coerce')
df['Inbuilt_memory'] = pd.to_numeric(df['Inbuilt_memory'], errors='coerce')
df['fast_charging'] = pd.to_numeric(df['fast_charging'], errors='coerce')
# Удаление запятых из столбца Price и преобразование в числовой формат
df['Price'] = df['Price'].str.replace(',', '').astype(float)
# Удаление столбцов с текстовыми значениями, которые не могут быть преобразованы в числа
df = df.drop(columns=['Name', 'company', 'Android_version', 'Processor_name', 'External_Memory', 'No_of_sim', 'Ram', 'Screen_resolution', 'Processor' ])
# Разделение на обучающую и тестовую выборки (например, 70% обучающая, 30% тестовая)
train_df, test_df = train_test_split(df, test_size=0.3, random_state=42)
# Разделение обучающей выборки на обучающую и контрольную (например, 70% обучающая, 30% контрольная)
train_df, val_df = train_test_split(train_df, test_size=0.3, random_state=42)
# Вывод размеров выборок
print("Размер обучающей выборки:", len(train_df))
print("Размер контрольной выборки:", len(val_df))
print("Размер тестовой выборки:", len(test_df))
# Определение сущностей
es = ft.EntitySet(id='mobile_data')
es = es.add_dataframe(dataframe_name='mobile', dataframe=train_df, index='id')
# Генерация признаков с уменьшенной глубиной
feature_matrix, feature_defs = ft.dfs(entityset=es, target_dataframe_name='mobile', max_depth=1)
# Преобразование признаков для контрольной и тестовой выборок
val_feature_matrix = ft.calculate_feature_matrix(features=feature_defs, entityset=es, instance_ids=val_df.index)
test_feature_matrix = ft.calculate_feature_matrix(features=feature_defs, entityset=es, instance_ids=test_df.index)
# Удаление строк с NaN
feature_matrix = feature_matrix.dropna()
val_feature_matrix = val_feature_matrix.dropna()
test_feature_matrix = test_feature_matrix.dropna()
# Разделение данных на обучающую и тестовую выборки
X_train = feature_matrix.drop('Price', axis=1)
y_train = feature_matrix['Price']
X_val = val_feature_matrix.drop('Price', axis=1)
y_val = val_feature_matrix['Price']
X_test = test_feature_matrix.drop('Price', axis=1)
y_test = test_feature_matrix['Price']
# Выбор модели
model = RandomForestRegressor(random_state=42)
# Обучение модели
model.fit(X_train, y_train)
# Предсказание и оценка
y_pred = model.predict(X_test)
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)
print(f"Mean Squared Error: {mse}")
print(f"R2 Score: {r2}")
# Кросс-валидация
scores = cross_val_score(model, X_train, y_train, cv=5, scoring='neg_mean_squared_error')
mse_cv = -scores.mean()
print(f"Cross-validated Mean Squared Error: {mse_cv}")
# Анализ важности признаков
feature_importances = model.feature_importances_
feature_names = X_train.columns
importance_df = pd.DataFrame({'Feature': feature_names, 'Importance': feature_importances})
importance_df = importance_df.sort_values(by='Importance', ascending=False)
plt.figure(figsize=(10, 6))
sns.barplot(x='Importance', y='Feature', data=importance_df)
plt.title('Feature Importance')
plt.show()
# Проверка на переобучение
y_train_pred = model.predict(X_train)
mse_train = mean_squared_error(y_train, y_train_pred)
r2_train = r2_score(y_train, y_train_pred)
print(f"Train Mean Squared Error: {mse_train}")
print(f"Train R2 Score: {r2_train}")
# Визуализация результатов
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()], 'k--', lw=2)
plt.xlabel('Actual Price')
plt.ylabel('Predicted Price')
plt.title('Actual vs Predicted Price')
plt.show()