AIM_PIbd-31_Tabeev_A.P/lab_2/lab2.ipynb

28 KiB
Raw Blame History

Загрузка датасетов и вывод информации

In [2]:
import pandas as pd
df = pd.read_csv("datasets_lab2/coffee.csv")
df2 = pd.read_csv("datasets_lab2/Stores.csv")
df3 = pd.read_csv("datasets_lab2/StudentsPerformance.csv")
df.info()
df2.info()
df3.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 8036 entries, 0 to 8035
Data columns (total 7 columns):
 #   Column     Non-Null Count  Dtype  
---  ------     --------------  -----  
 0   Date       8036 non-null   object 
 1   Open       8036 non-null   float64
 2   High       8036 non-null   float64
 3   Low        8036 non-null   float64
 4   Close      8036 non-null   float64
 5   Adj Close  8036 non-null   float64
 6   Volume     8036 non-null   int64  
dtypes: float64(5), int64(1), object(1)
memory usage: 439.6+ KB
<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: 1000 entries, 0 to 999
Data columns (total 8 columns):
 #   Column                       Non-Null Count  Dtype 
---  ------                       --------------  ----- 
 0   gender                       1000 non-null   object
 1   race/ethnicity               1000 non-null   object
 2   parental level of education  1000 non-null   object
 3   lunch                        1000 non-null   object
 4   test preparation course      1000 non-null   object
 5   math score                   1000 non-null   int64 
 6   reading score                1000 non-null   int64 
 7   writing score                1000 non-null   int64 
dtypes: int64(3), object(5)
memory usage: 62.6+ KB

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

Первый датасет coffee.csv позволяет спрогнозировать будущие показатели акций кофейни Starbucks. Второй датасет Stores.csv - магазины. Третий датасет StudentsPerformance.csv - успеваемость студентов на экзамене

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

Объекты:

  1. Акции
  2. Магазины
  3. Успеваемость студентов Атрибуты:
  4. Дата; начальная цена за день; максимальная цена; минимальная цена; цена на момент закрытия продаж; скорректированая цена на момент закрытия; объем торговли акций за день.
  5. Идентификатор магазина; физическая площадь; кол-во доступных товаров; количество покупателей, посетивших магазины в среднем за месяц; продажив магазинах (в долларах США).
  6. Пол, раса/этническая принадлежность, уровень образования родителей, обед, курс подготовки к тестированию, оценка по математике, оценка по чтению, оценка по письму.

Бизнес цели

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

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

На входе будут переданы следующие датасеты, целевые признаки для каждого:

  1. coffee.csv — максимальная цена акций за день.
  2. Stores.csv — выручка магазина.
  3. StudentsPerformance.csv — общий средний балл (среднее значение по математике, чтению и письму).

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

  1. Устаревшие данные: Важно проверять актуальность данных, особенно для финансовых временных рядов и данных о магазинах. Для этого старые записи могут быть удалены или обновлены, если доступны более свежие данные.
  2. Выбросы: Необходимо выявить аномалии, такие как резкие изменения в ценах акций или посещаемости, и принять решение об их удалении или сглаживании, исходя из размера выборки.

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

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

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

In [3]:
print(df.describe())
print(df2.describe())
print(df3.describe())
              Open         High          Low        Close    Adj Close  \
count  8036.000000  8036.000000  8036.000000  8036.000000  8036.000000   
mean     30.054280    30.351487    29.751322    30.058857    26.674025   
std      33.615577    33.906613    33.314569    33.615911    31.728090   
min       0.328125     0.347656     0.320313     0.335938     0.260703   
25%       4.392031     4.531250     4.304922     4.399610     3.414300   
50%      13.325000    13.493750    13.150000    13.330000    10.352452   
75%      55.250000    55.722501    54.852499    55.267499    47.464829   
max     126.080002   126.320000   124.809998   126.059998   118.010414   

             Volume  
count  8.036000e+03  
mean   1.470459e+07  
std    1.340021e+07  
min    1.504000e+06  
25%    7.817750e+06  
50%    1.169815e+07  
75%    1.778795e+07  
max    5.855088e+08  
        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  
       math score  reading score  writing score
count  1000.00000    1000.000000    1000.000000
mean     66.08900      69.169000      68.054000
std      15.16308      14.600192      15.195657
min       0.00000      17.000000      10.000000
25%      57.00000      59.000000      57.750000
50%      66.00000      70.000000      69.000000
75%      77.00000      79.000000      79.000000
max     100.00000     100.000000     100.000000

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

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

In [4]:
print("Первый датасет coffee.csv")
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("Второй датасет Stores.csv")
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("Третий датасет StudentsPerformance.csv")
for i in df3.columns:
    null_rate = df3[i].isnull().sum() / len(df3)*100
    if null_rate > 0:
        print(f"{i} процент пустых значений: %{null_rate:.2f}")
Первый датасет coffee.csv
Второй датасет Stores.csv
Третий датасет StudentsPerformance.csv

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

В первом датасете coffee.csv удалим строки, где год ниже 2000.

In [11]:
df_filtered = df[df['Date'] > '2000-01-01']
print(df_filtered)
           Date       Open       High        Low      Close  Adj Close  \
1899 2000-01-03   2.984375   3.085938   2.906250   3.082031   2.391797   
1900 2000-01-04   3.007813   3.109375   2.968750   2.984375   2.316012   
1901 2000-01-05   2.992188   3.078125   2.960938   3.023438   2.346326   
1902 2000-01-06   3.000000   3.203125   3.000000   3.132813   2.431207   
1903 2000-01-07   3.093750   3.125000   3.031250   3.117188   2.419082   
...         ...        ...        ...        ...        ...        ...   
8031 2024-05-17  75.269997  78.000000  74.919998  77.849998  77.849998   
8032 2024-05-20  77.680000  78.320000  76.709999  77.540001  77.540001   
8033 2024-05-21  77.559998  78.220001  77.500000  77.720001  77.720001   
8034 2024-05-22  77.699997  81.019997  77.440002  80.720001  80.720001   
8035 2024-05-23  80.099998  80.699997  79.169998  79.260002  79.260002   

        Volume  
1899  24232000  
1900  21564800  
1901  28206400  
1902  30825600  
1903  26044800  
...        ...  
8031  14436500  
8032  11183800  
8033   8916600  
8034  22063400  
8035   4651418  

[6137 rows x 7 columns]

Во втором датасете Stores.csv всем магазинам поставим среднее значение площади.

In [14]:
store_area_mean = df2['Store_Area'].mean()
df2['Store_Area'] = store_area_mean
print(df2)
     Store ID   Store_Area  Items_Available  Daily_Customer_Count  Store_Sales
0            1      1500.0             1961                   530        66490
1            2      1500.0             1752                   210        39820
2            3      1500.0             1609                   720        54010
3            4      1500.0             1748                   620        53730
4            5      1500.0             2111                   450        46620
..         ...         ...              ...                   ...          ...
891        892      1500.0             1910                  1080        66390
892        893      1500.0             1663                   850        82080
893        894      1500.0             1436                  1060        76440
894        895      1500.0             1560                   770        96610
895        896      1500.0             1429                  1110        54340

[896 rows x 5 columns]

В третьем датасете StudentsPerformance.csv всем студентам сделаем ланч стандартным.

In [15]:
df3['lunch'] = 'standard'
print(df3)
     gender race/ethnicity parental level of education     lunch  \
0    female        group B           bachelor's degree  standard   
1    female        group C                some college  standard   
2    female        group B             master's degree  standard   
3      male        group A          associate's degree  standard   
4      male        group C                some college  standard   
..      ...            ...                         ...       ...   
995  female        group E             master's degree  standard   
996    male        group C                 high school  standard   
997  female        group C                 high school  standard   
998  female        group D                some college  standard   
999  female        group D                some college  standard   

    test preparation course  math score  reading score  writing score  
0                      none          72             72             74  
1                 completed          69             90             88  
2                      none          90             95             93  
3                      none          47             57             44  
4                      none          76             78             75  
..                      ...         ...            ...            ...  
995               completed          88             99             95  
996                    none          62             55             55  
997               completed          59             71             65  
998               completed          68             78             77  
999                    none          77             86             86  

[1000 rows x 8 columns]
In [17]:
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  # Contains all columns.
    y = df_input[
        [stratify_colname]
    ]  # Dataframe of just the column on which to stratify.

    # Split original dataframe into train and temp dataframes.
    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
    )

    # Split the temp dataframe into val and test dataframes.
    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 [22]:
from sklearn.model_selection import train_test_split

train_df, temp_df = train_test_split(df, test_size=0.4, random_state=42)
val_df, test_df = train_test_split(temp_df, test_size=0.5, random_state=42) 

train_df2, temp_df2 = train_test_split(df2, test_size=0.4, random_state=42)
val_df2, test_df2 = train_test_split(temp_df2, test_size=0.5, random_state=42)

train_df3, temp_df3 = train_test_split(df3, test_size=0.4, random_state=42)
val_df3, test_df3 = train_test_split(temp_df3, test_size=0.5, random_state=42)
print(f"Train df: {train_df.shape}, Validation df: {val_df.shape}, Test df: {test_df.shape}")
print(f"Train df2: {train_df2.shape}, Validation df2: {val_df2.shape}, Test df2: {test_df2.shape}")
print(f"Train df3: {train_df3.shape}, Validation df3: {val_df3.shape}, Test df3: {test_df3.shape}")
Train df: (4821, 7), Validation df: (1607, 7), Test df: (1608, 7)
Train df2: (537, 5), Validation df2: (179, 5), Test df2: (180, 5)
Train df3: (600, 8), Validation df3: (200, 8), Test df3: (200, 8)

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

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

In [26]:
from imblearn.over_sampling import RandomOverSampler
from imblearn.under_sampling import RandomUnderSampler

df = pd.read_csv("datasets_lab2/coffee.csv")
df['High_category'] = pd.cut(df['High'], bins=[0.1, 20, 60, 105, float('inf')],
                              labels=['Low', 'Medium', 'High', 'Luxury'])

y = df['High_category']
X = df.drop(columns=['High', 'High_category'])

oversampler = RandomOverSampler(random_state=42)
X_resampled, y_resampled = oversampler.fit_resample(X, y)

undersampler = RandomUnderSampler(random_state=42)
X_resampled_under, y_resampled_under = undersampler.fit_resample(X, y)

print("Выборка после oversampling (df):")
print(pd.Series(y_resampled).value_counts())

print("Выборка после undersampling (df):")
print(pd.Series(y_resampled_under).value_counts())
Выборка после oversampling (df):
High_category
Low       4835
Medium    4835
High      4835
Luxury    4835
Name: count, dtype: int64
Выборка после undersampling (df):
High_category
Low       321
Medium    321
High      321
Luxury    321
Name: count, dtype: int64
In [27]:
df2 = pd.read_csv("datasets_lab2/Stores.csv")

df2['Sales_category'] = pd.cut(df2['Store_Sales'], bins=[0, 50000, 100000, 200000, float('inf')],
                                labels=['Low', 'Medium', 'High', 'Luxury'])

y2 = df2['Sales_category']
X2 = df2.drop(columns=['Store_Sales', 'Sales_category'])

oversampler2 = RandomOverSampler(random_state=42)
X_resampled_2, y_resampled_2 = oversampler2.fit_resample(X2, y2)

undersampler2 = RandomUnderSampler(random_state=42)
X_resampled_2_under, y_resampled_2_under = undersampler2.fit_resample(X2, y2)

print("Выборка после oversampling (df2):")
print(pd.Series(y_resampled_2).value_counts())

print("Выборка после undersampling (df2):")
print(pd.Series(y_resampled_2_under).value_counts())
Выборка после oversampling (df2):
Sales_category
Low       598
Medium    598
High      598
Luxury      0
Name: count, dtype: int64
Выборка после undersampling (df2):
Sales_category
Low       7
Medium    7
High      7
Luxury    0
Name: count, dtype: int64
In [32]:
df3 = pd.read_csv("datasets_lab2/StudentsPerformance.csv")

df3['reading_score_category'] = pd.cut(df3['reading score'], bins=[0, 20, 50, 100, float('inf')],
                              labels=['Low', 'Medium', 'High', 'Luxury'])

y3 = df3['reading_score_category']
X3 = df3.drop(columns=['reading score', 'reading_score_category'])

oversampler3 = RandomOverSampler(random_state=42)
X_resampled_3, y_resampled_3 = oversampler3.fit_resample(X3, y3)

undersampler3 = RandomUnderSampler(random_state=42)
X_resampled_3_under, y_resampled_3_under = undersampler3.fit_resample(X3, y3)

print("Выборка после oversampling (df3):")
print(pd.Series(y_resampled_3).value_counts())

print("Выборка после undersampling (df3):")
print(pd.Series(y_resampled_3_under).value_counts())
Выборка после oversampling (df3):
reading_score_category
Low       903
Medium    903
High      903
Luxury      0
Name: count, dtype: int64
Выборка после undersampling (df3):
reading_score_category
Low       1
Medium    1
High      1
Luxury    0
Name: count, dtype: int64