AIM-PIbd-31-LOBASHOV-I-D/lab_2/lab_2.ipynb
2024-10-26 01:23:18 +04:00

33 KiB
Raw Blame History

Lab2 PIbd-31 Lobashov

Три датасета:

  1. Цена на автомобили (17 вариант)
  2. Магазины (9 вариант)
  3. Цены на золото (14 вариант)
In [44]:
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()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 19237 entries, 0 to 19236
Data columns (total 18 columns):
 #   Column            Non-Null Count  Dtype  
---  ------            --------------  -----  
 0   ID                19237 non-null  int64  
 1   Price             19237 non-null  int64  
 2   Levy              19237 non-null  int64  
 3   Manufacturer      19237 non-null  object 
 4   Model             19237 non-null  object 
 5   Prod. year        19237 non-null  int64  
 6   Category          19237 non-null  object 
 7   Leather interior  19237 non-null  object 
 8   Fuel type         19237 non-null  object 
 9   Engine volume     19237 non-null  object 
 10  Mileage           19237 non-null  int64  
 11  Cylinders         19237 non-null  float64
 12  Gear box type     19237 non-null  object 
 13  Drive wheels      19237 non-null  object 
 14  Doors             19237 non-null  object 
 15  Wheel             19237 non-null  object 
 16  Color             19237 non-null  object 
 17  Airbags           19237 non-null  int64  
dtypes: float64(1), int64(6), object(11)
memory usage: 2.6+ MB
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 896 entries, 0 to 895
Data columns (total 5 columns):
 #   Column                Non-Null Count  Dtype
---  ------                --------------  -----
 0   Store ID              896 non-null    int64
 1   Store_Area            896 non-null    int64
 2   Items_Available       896 non-null    int64
 3   Daily_Customer_Count  896 non-null    int64
 4   Store_Sales           896 non-null    int64
dtypes: int64(5)
memory usage: 35.1 KB
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1718 entries, 0 to 1717
Data columns (total 81 columns):
 #   Column         Non-Null Count  Dtype  
---  ------         --------------  -----  
 0   Date           1718 non-null   object 
 1   Open           1718 non-null   float64
 2   High           1718 non-null   float64
 3   Low            1718 non-null   float64
 4   Close          1718 non-null   float64
 5   Adj Close      1718 non-null   float64
 6   Volume         1718 non-null   int64  
 7   SP_open        1718 non-null   float64
 8   SP_high        1718 non-null   float64
 9   SP_low         1718 non-null   float64
 10  SP_close       1718 non-null   float64
 11  SP_Ajclose     1718 non-null   float64
 12  SP_volume      1718 non-null   int64  
 13  DJ_open        1718 non-null   float64
 14  DJ_high        1718 non-null   float64
 15  DJ_low         1718 non-null   float64
 16  DJ_close       1718 non-null   float64
 17  DJ_Ajclose     1718 non-null   float64
 18  DJ_volume      1718 non-null   int64  
 19  EG_open        1718 non-null   float64
 20  EG_high        1718 non-null   float64
 21  EG_low         1718 non-null   float64
 22  EG_close       1718 non-null   float64
 23  EG_Ajclose     1718 non-null   float64
 24  EG_volume      1718 non-null   int64  
 25  EU_Price       1718 non-null   float64
 26  EU_open        1718 non-null   float64
 27  EU_high        1718 non-null   float64
 28  EU_low         1718 non-null   float64
 29  EU_Trend       1718 non-null   int64  
 30  OF_Price       1718 non-null   float64
 31  OF_Open        1718 non-null   float64
 32  OF_High        1718 non-null   float64
 33  OF_Low         1718 non-null   float64
 34  OF_Volume      1718 non-null   int64  
 35  OF_Trend       1718 non-null   int64  
 36  OS_Price       1718 non-null   float64
 37  OS_Open        1718 non-null   float64
 38  OS_High        1718 non-null   float64
 39  OS_Low         1718 non-null   float64
 40  OS_Trend       1718 non-null   int64  
 41  SF_Price       1718 non-null   int64  
 42  SF_Open        1718 non-null   int64  
 43  SF_High        1718 non-null   int64  
 44  SF_Low         1718 non-null   int64  
 45  SF_Volume      1718 non-null   int64  
 46  SF_Trend       1718 non-null   int64  
 47  USB_Price      1718 non-null   float64
 48  USB_Open       1718 non-null   float64
 49  USB_High       1718 non-null   float64
 50  USB_Low        1718 non-null   float64
 51  USB_Trend      1718 non-null   int64  
 52  PLT_Price      1718 non-null   float64
 53  PLT_Open       1718 non-null   float64
 54  PLT_High       1718 non-null   float64
 55  PLT_Low        1718 non-null   float64
 56  PLT_Trend      1718 non-null   int64  
 57  PLD_Price      1718 non-null   float64
 58  PLD_Open       1718 non-null   float64
 59  PLD_High       1718 non-null   float64
 60  PLD_Low        1718 non-null   float64
 61  PLD_Trend      1718 non-null   int64  
 62  RHO_PRICE      1718 non-null   int64  
 63  USDI_Price     1718 non-null   float64
 64  USDI_Open      1718 non-null   float64
 65  USDI_High      1718 non-null   float64
 66  USDI_Low       1718 non-null   float64
 67  USDI_Volume    1718 non-null   int64  
 68  USDI_Trend     1718 non-null   int64  
 69  GDX_Open       1718 non-null   float64
 70  GDX_High       1718 non-null   float64
 71  GDX_Low        1718 non-null   float64
 72  GDX_Close      1718 non-null   float64
 73  GDX_Adj Close  1718 non-null   float64
 74  GDX_Volume     1718 non-null   int64  
 75  USO_Open       1718 non-null   float64
 76  USO_High       1718 non-null   float64
 77  USO_Low        1718 non-null   float64
 78  USO_Close      1718 non-null   float64
 79  USO_Adj Close  1718 non-null   float64
 80  USO_Volume     1718 non-null   int64  
dtypes: float64(58), int64(22), object(1)
memory usage: 1.1+ MB

Проблемная область

Первый датасет позволяет проанализировать данные и понять, какие автомобили имеют превосходство на рынке и какие чаще всего выбирают пользователи. Второй датасет позволяет при помощи данных о магазинах проанализировать их производительность и выявить факторы, влияющие на продажи. Третий датасет позволяет проанализировать данные и спрогнозировать цены на золото на основе различных финансовых показателей.

Анализ набора данных

Объекты наблюдения - автомобили, магазины, цены на золото Атрибуты -

  1. ID, Цена, Налог, Производитель, Модель, Год производства, Категория, Кожаный салон, Тип топлива, Объем двигателя, Пробег, Цилиндры, Тип коробки передач, Приводные колеса, Количество дверей, Руль, Цвет, Подушки безопасности.
  2. ID магазина, Площадь магазина, Доступные товары, Ежедневное количество покупателей, Продажи магазина.
  3. Дата, Открытие, Максимум, Минимум, Закрытие, Скорректированное закрытие, Объем торгов, 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_Объем. Связи между объектами - нет Связи между объектами - нет

Бизнес-цели

  1. Какие модели автомобилей более выгодно продавать магазинам техники. Какие комплектующие наиболее популярны у производителей и покупателей, для дальнейшего увеличения производства данных комплектующих.
  2. Анализ производительности магазинов для выявления факторов, влияющих на продажи, и оптимизация работы магазинов.
  3. Прогноз цен на золото для принятия инвестиционных решений и управления рисками.

Примеры целей технического проекта. Что поступает на вход, что является целевым признаком.

На входе всегда датасет, целевые признаки:

  1. Цена автомобиля (Price)
  2. Продажи магазина (Store_Sales)
  3. Цена закрытия золота (Close)

Проблемы набора данных и их решения

  1. Возможны устаревшие данные. Для решения данной проблемы требуется удаление самых старых записей и добавление более новых.
  2. Возможны выбросы. Решить эту проблему помогает удаление таких выбросов. В маленькой выборке не рекомендуется удалять выбросы. В большой выборке выбросы усредняются.

Качество набора данных

Наборы данных содержат достаточно примеров и признаков для обучения модели. Учтены различные ситуации проблемной области. Данные соответствуют данным, которые будут подаваться в производственной среде. Все метки согласованы.

Поиск аномалий

In [45]:
print(df.describe())
print(df2.describe())
print(df3.describe())
                 ID         Price          Levy    Prod. year       Mileage  \
count  1.923700e+04  1.923700e+04  19237.000000  19237.000000  1.923700e+04   
mean   4.557654e+07  1.855593e+04    935.018662   2010.912824  1.532236e+06   
std    9.365914e+05  1.905813e+05    388.099990      5.668673  4.840387e+07   
min    2.074688e+07  1.000000e+00     87.000000   1939.000000  0.000000e+00   
25%    4.569837e+07  5.331000e+03    730.000000   2009.000000  7.013900e+04   
50%    4.577231e+07  1.317200e+04   1000.000000   2012.000000  1.260000e+05   
75%    4.580204e+07  2.207500e+04   1000.000000   2015.000000  1.888880e+05   
max    4.581665e+07  2.630750e+07  11714.000000   2020.000000  2.147484e+09   

          Cylinders       Airbags  
count  19237.000000  19237.000000  
mean       4.582991      6.582627  
std        1.199933      4.320168  
min        1.000000      0.000000  
25%        4.000000      4.000000  
50%        4.000000      6.000000  
75%        4.000000     12.000000  
max       16.000000     16.000000  
        Store ID    Store_Area  Items_Available  Daily_Customer_Count  \
count  896.000000   896.000000       896.000000            896.000000   
mean   448.500000  1485.409598      1782.035714            786.350446   
std    258.797218   250.237011       299.872053            265.389281   
min      1.000000   775.000000       932.000000             10.000000   
25%    224.750000  1316.750000      1575.500000            600.000000   
50%    448.500000  1477.000000      1773.500000            780.000000   
75%    672.250000  1653.500000      1982.750000            970.000000   
max    896.000000  2229.000000      2667.000000           1560.000000   

         Store_Sales  
count     896.000000  
mean    59351.305804  
std     17190.741895  
min     14920.000000  
25%     46530.000000  
50%     58605.000000  
75%     71872.500000  
max    116320.000000  
              Open         High          Low        Close    Adj Close  \
count  1718.000000  1718.000000  1718.000000  1718.000000  1718.000000   
mean    127.323434   127.854237   126.777695   127.319482   127.319482   
std      17.526993    17.631189    17.396513    17.536269    17.536269   
min     100.919998   100.989998   100.230003   100.500000   100.500000   
25%     116.220001   116.540001   115.739998   116.052502   116.052502   
50%     121.915001   122.325001   121.369999   121.795002   121.795002   
75%     128.427494   129.087498   127.840001   128.470001   128.470001   
max     173.199997   174.070007   172.919998   173.610001   173.610001   

             Volume      SP_open      SP_high       SP_low     SP_close  ...  \
count  1.718000e+03  1718.000000  1718.000000  1718.000000  1718.000000  ...   
mean   8.446327e+06   204.490023   205.372637   203.487014   204.491222  ...   
std    4.920731e+06    43.831928    43.974644    43.618940    43.776999  ...   
min    1.501600e+06   122.059998   122.320000   120.029999   120.290001  ...   
25%    5.412925e+06   170.392498   170.962506   169.577499   170.397500  ...   
50%    7.483900e+06   205.464996   206.459999   204.430000   205.529999  ...   
75%    1.020795e+07   237.292500   237.722500   236.147503   236.889996  ...   
max    9.380420e+07   293.089996   293.940002   291.809998   293.579987  ...   

           GDX_Low    GDX_Close  GDX_Adj Close    GDX_Volume     USO_Open  \
count  1718.000000  1718.000000    1718.000000  1.718000e+03  1718.000000   
mean     26.384575    26.715012      25.924624  4.356515e+07    22.113417   
std      10.490908    10.603110       9.886570  2.909151e+07    11.431056   
min      12.400000    12.470000      12.269618  4.729000e+06     7.820000   
25%      20.355000    20.585000      20.180950  2.259968e+07    11.420000   
50%      22.870001    23.054999      22.677604  3.730465e+07    16.450000   
75%      26.797500    27.317500      26.478154  5.697055e+07    34.419998   
max      56.770000    57.470001      54.617039  2.321536e+08    41.599998   

          USO_High      USO_Low    USO_Close  USO_Adj Close    USO_Volume  
count  1718.000000  1718.000000  1718.000000    1718.000000  1.718000e+03  
mean     22.307148    21.904657    22.109051      22.109051  1.922313e+07  
std      11.478671    11.373997    11.432787      11.432787  1.575743e+07  
min       8.030000     7.670000     7.960000       7.960000  1.035100e+06  
25%      11.500000    11.300000    11.392500      11.392500  6.229500e+06  
50%      16.635001    16.040000    16.345000      16.345000  1.613015e+07  
75%      34.667499    34.110000    34.417499      34.417499  2.672375e+07  
max      42.299999    41.299999    42.009998      42.009998  1.102657e+08  

[8 rows x 80 columns]

При просмотре вывода не было замечено аномалий в столбцах датасетов.

Проблема пропущенных данных

In [46]:
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}")
DATASET 1
DATASET 2
DATASET 3

Во всех датасетах пустых значений не найдено.

Разбиение на выборки

Для разбиения на выборке для начала уменьшим количество уникальных значений в столбцах с целевыми признаками, путем добавления новых столбцов с малым количеством уникальных значений.

In [47]:
# Добавление нового столбца для первого датасета с рейтингом от 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)
C:\Users\goldfest\AppData\Local\Temp\ipykernel_12052\2802807477.py:9: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  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)
In [48]:
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

Выборки датасетов

In [49]:
# Разбиение на выборки для каждого датасета
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}")
DATASET 1
Train: (11542, 19), Val: (3847, 19), Test: (3848, 19)
DATASET 2
Train: (537, 6), Val: (179, 6), Test: (180, 6)
DATASET 3
Train: (929, 82), Val: (310, 82), Test: (310, 82)

Было сделано разбиение на три выборки: 60%, 20% и 20% при помощи библиотеки scikit-learn и функции train_test_split. На взгляд сбалансированные

Приращение методами выборки с избытком (oversampling) и выборки с недостатком (undersampling)

In [50]:
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())
Выборка до oversampling и undersampling (датасет 1): (11542, 6)
new_rating
1    10485
2      919
3      102
4       27
5        9
Name: count, dtype: int64
Выборка после oversampling (датасет 1):  (52450, 6)
new_rating
2    10509
3    10490
1    10485
5    10484
4    10482
Name: count, dtype: int64
Выборка после undersampling (датасет 1):  (45, 6)
new_rating
1    9
2    9
3    9
4    9
5    9
Name: count, dtype: int64
In [51]:
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())
Выборка до oversampling и undersampling: (537, 5)
new_high
3    253
2    167
4    105
1      8
5      4
Name: count, dtype: int64
Выборка после oversampling: (1265, 5)
new_high
1    253
2    253
3    253
4    253
5    253
Name: count, dtype: int64
Выборка после undersampling: (20, 5)
new_high
1    4
2    4
3    4
4    4
5    4
Name: count, dtype: int64
In [52]:
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())
Выборка до oversampling и undersampling (датасет 3): (929, 6)
new_price
1    428
2    366
4     98
3     37
5      0
Name: count, dtype: int64
Выборка после oversampling (датасет 3): (1712, 6)
new_price
1    428
2    428
3    428
4    428
5      0
Name: count, dtype: int64
Выборка после undersampling (датасет 3): (148, 6)
new_price
1    37
2    37
3    37
4    37
5     0
Name: count, dtype: int64