33 KiB
Lab2 PIbd-31 Lobashov¶
Три датасета:
- Цена на автомобили (17 вариант)
- Магазины (9 вариант)
- Цены на золото (14 вариант)
import pandas as pd
df = pd.read_csv("..\\static\\csv\\car_price_prediction.csv")
df2 = pd.read_csv("..\\static\\csv\\Stores.csv")
df3 = pd.read_csv("..\\static\\csv\\FINAL_USO.csv")
df.info()
df2.info()
df3.info()
Проблемная область¶
Первый датасет позволяет проанализировать данные и понять, какие автомобили имеют превосходство на рынке и какие чаще всего выбирают пользователи. Второй датасет позволяет при помощи данных о магазинах проанализировать их производительность и выявить факторы, влияющие на продажи. Третий датасет позволяет проанализировать данные и спрогнозировать цены на золото на основе различных финансовых показателей.
Анализ набора данных¶
Объекты наблюдения - автомобили, магазины, цены на золото Атрибуты -
- ID, Цена, Налог, Производитель, Модель, Год производства, Категория, Кожаный салон, Тип топлива, Объем двигателя, Пробег, Цилиндры, Тип коробки передач, Приводные колеса, Количество дверей, Руль, Цвет, Подушки безопасности.
- ID магазина, Площадь магазина, Доступные товары, Ежедневное количество покупателей, Продажи магазина.
- Дата, Открытие, Максимум, Минимум, Закрытие, Скорректированное закрытие, Объем торгов, SP_открытие, SP_максимум, SP_минимум, SP_закрытие, SP_скорректированное закрытие, SP_объем, DJ_открытие, DJ_максимум, DJ_минимум, DJ_закрытие, DJ_скорректированное закрытие, DJ_объем, EG_открытие, EG_максимум, EG_минимум, EG_закрытие, EG_скорректированное закрытие, EG_объем, EU_Цена, EU_открытие, EU_максимум, EU_минимум, EU_тренд, OF_Цена, OF_Открытие, OF_Максимум, OF_Минимум, OF_Объем, OF_Тренд, OS_Цена, OS_Открытие, OS_Максимум, OS_Минимум, OS_Тренд, SF_Цена, SF_Открытие, SF_Максимум, SF_Минимум, SF_Объем, SF_Тренд, USB_Цена, USB_Открытие, USB_Максимум, USB_Минимум, USB_Тренд, PLT_Цена, PLT_Открытие, PLT_Максимум, PLT_Минимум, PLT_Тренд, PLD_Цена, PLD_Открытие, PLD_Максимум, PLD_Минимум, PLD_Тренд, RHO_Цена, USDI_Цена, USDI_Открытие, USDI_Максимум, USDI_Минимум, USDI_Объем, USDI_Тренд, GDX_Открытие, GDX_Максимум, GDX_Минимум, GDX_Закрытие, GDX_Скорректированное закрытие, GDX_Объем, USO_Открытие, USO_Максимум, USO_Минимум, USO_Закрытие, USO_Скорректированное закрытие, USO_Объем. Связи между объектами - нет Связи между объектами - нет
Бизнес-цели¶
- Какие модели автомобилей более выгодно продавать магазинам техники. Какие комплектующие наиболее популярны у производителей и покупателей, для дальнейшего увеличения производства данных комплектующих.
- Анализ производительности магазинов для выявления факторов, влияющих на продажи, и оптимизация работы магазинов.
- Прогноз цен на золото для принятия инвестиционных решений и управления рисками.
Примеры целей технического проекта. Что поступает на вход, что является целевым признаком.¶
На входе всегда датасет, целевые признаки:
- Цена автомобиля (Price)
- Продажи магазина (Store_Sales)
- Цена закрытия золота (Close)
Проблемы набора данных и их решения¶
- Возможны устаревшие данные. Для решения данной проблемы требуется удаление самых старых записей и добавление более новых.
- Возможны выбросы. Решить эту проблему помогает удаление таких выбросов. В маленькой выборке не рекомендуется удалять выбросы. В большой выборке выбросы усредняются.
Качество набора данных¶
Наборы данных содержат достаточно примеров и признаков для обучения модели. Учтены различные ситуации проблемной области. Данные соответствуют данным, которые будут подаваться в производственной среде. Все метки согласованы.
Поиск аномалий¶
print(df.describe())
print(df2.describe())
print(df3.describe())
При просмотре вывода не было замечено аномалий в столбцах датасетов.
Проблема пропущенных данных¶
print("DATASET 1")
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("DATASET 2")
for i in df2.columns:
null_rate = df2[i].isnull().sum() / len(df2)*100
if null_rate > 0:
print(f"{i} процент пустых значений: %{null_rate:.2f}")
print("DATASET 3")
for i in df3.columns:
null_rate = df3[i].isnull().sum() / len(df3)*100
if null_rate > 0:
print(f"{i} процент пустых значений: %{null_rate:.2f}")
Во всех датасетах пустых значений не найдено.
Разбиение на выборки¶
Для разбиения на выборке для начала уменьшим количество уникальных значений в столбцах с целевыми признаками, путем добавления новых столбцов с малым количеством уникальных значений.
# Добавление нового столбца для первого датасета с рейтингом от 1 до 5
df['new_rating'] = pd.cut(df['Price'], bins=[0, 40000, 80000, 120000, 180000, 50000000], labels=[1, 2, 3, 4, 5], include_lowest=True)
# Добавление нового столбца для второго датасета с диапазоном цен от 1 до 5
df2['new_high'] = pd.cut(df2['Store_Sales'], bins=[0, 25000, 50000, 75000, 100000, 127000], labels=[1, 2, 3, 4, 5], include_lowest=True)
# Фильтрация третьего датасета по цене и добавление категории цен от 1 до 5
df3_filtered = df3[(df3['Open'] >= 100) & (df3['Open'] <= 160)]
df3_filtered['new_price'] = pd.cut(df3_filtered['Open'], bins=[100, 120, 130, 140, 160, 180], labels=[1, 2, 3, 4, 5], include_lowest=True)
from sklearn.model_selection import train_test_split
def split_stratified_into_train_val_test(
df_input,
stratify_colname="y",
frac_train=0.6,
frac_val=0.15,
frac_test=0.25,
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
Выборки датасетов¶
# Разбиение на выборки для каждого датасета
df_train1, df_val1, df_test1 = split_stratified_into_train_val_test(
df, stratify_colname="new_rating", frac_train=0.60, frac_val=0.20, frac_test=0.20
)
df_train2, df_val2, df_test2 = split_stratified_into_train_val_test(
df2, stratify_colname="new_high", frac_train=0.60, frac_val=0.20, frac_test=0.20
)
df_train3, df_val3, df_test3 = split_stratified_into_train_val_test(
df3_filtered, stratify_colname="new_price", frac_train=0.60, frac_val=0.20, frac_test=0.20
)
# Проверка размеров выборок
print("DATASET 1")
print(f"Train: {df_train1.shape}, Val: {df_val1.shape}, Test: {df_test1.shape}")
print("DATASET 2")
print(f"Train: {df_train2.shape}, Val: {df_val2.shape}, Test: {df_test2.shape}")
print("DATASET 3")
print(f"Train: {df_train3.shape}, Val: {df_val3.shape}, Test: {df_test3.shape}")
Было сделано разбиение на три выборки: 60%, 20% и 20% при помощи библиотеки scikit-learn и функции train_test_split. На взгляд сбалансированные
Приращение методами выборки с избытком (oversampling) и выборки с недостатком (undersampling)¶
from imblearn.over_sampling import ADASYN
from imblearn.under_sampling import RandomUnderSampler
df_train1 = df_train1[['Price', 'Levy', 'Mileage', 'Prod. year', 'Mileage', 'new_rating']].copy()
ada = ADASYN()
undersampler = RandomUnderSampler(random_state=42)
print("Выборка до oversampling и undersampling (датасет 1):", df_train1.shape)
print(df_train1['new_rating'].value_counts())
X_resampled, y_resampled = ada.fit_resample(df_train1, df_train1['new_rating'])
df_train1_adasyn = pd.DataFrame(X_resampled)
print("Выборка после oversampling (датасет 1): ", df_train1_adasyn.shape)
print(df_train1_adasyn.new_rating.value_counts())
X_resampled_under, y_resampled_under = undersampler.fit_resample(df_train1, df_train1['new_rating'])
print("Выборка после undersampling (датасет 1): ", pd.DataFrame(X_resampled_under).shape)
print(pd.DataFrame(X_resampled_under)['new_rating'].value_counts())
from imblearn.over_sampling import SMOTE
df_train2 = df_train2[['Store_Sales', 'Store_Area', 'Items_Available', 'Daily_Customer_Count', 'new_high']].copy()
smote = SMOTE(random_state=42, k_neighbors=2)
undersampler = RandomUnderSampler(random_state=42)
print("Выборка до oversampling и undersampling:", df_train2.shape)
print(df_train2['new_high'].value_counts())
X_resampled, y_resampled = smote.fit_resample(df_train2, df_train2['new_high'])
df_train2_smote = pd.DataFrame(X_resampled, columns=df_train2.columns)
print("Выборка после oversampling:", df_train2_smote.shape)
print(df_train2_smote['new_high'].value_counts())
X_resampled_under2, y_resampled_under2 = undersampler.fit_resample(df_train2, df_train2['new_high'])
df_train2_under = pd.DataFrame(X_resampled_under2, columns=df_train2.columns)
print("Выборка после undersampling:", df_train2_under.shape)
print(df_train2_under['new_high'].value_counts())
from imblearn.over_sampling import RandomOverSampler
df_train3 = df_train3[['Open', 'High', 'Low', 'Close', 'Volume', 'new_price']].copy()
oversampler = RandomOverSampler(random_state=42)
undersampler = RandomUnderSampler(random_state=42)
print("Выборка до oversampling и undersampling (датасет 3):", df_train3.shape)
print(df_train3['new_price'].value_counts())
X_resampled, y_resampled = oversampler.fit_resample(df_train3, df_train3['new_price'])
df_train3_oversampled = pd.DataFrame(X_resampled, columns=df_train3.columns)
print("Выборка после oversampling (датасет 3):", df_train3_oversampled.shape)
print(df_train3_oversampled['new_price'].value_counts())
X_resampled_under, y_resampled_under = undersampler.fit_resample(df_train3, df_train3['new_price'])
df_train3_under = pd.DataFrame(X_resampled_under, columns=df_train3.columns)
print("Выборка после undersampling (датасет 3):", df_train3_under.shape)
print(df_train3_under['new_price'].value_counts())