2024-12-12 16:14:13 +04:00

51 KiB
Raw Blame History

In [26]:
import pandas as pd

df = pd.read_csv("..//static//csv//ds_salaries.csv")

df.head()
Out[26]:
work_year experience_level employment_type job_title salary salary_currency salary_in_usd employee_residence remote_ratio company_location company_size
0 2023 SE FT Principal Data Scientist 80000 EUR 85847 ES 100 ES L
1 2023 MI CT ML Engineer 30000 USD 30000 US 100 US S
2 2023 MI CT ML Engineer 25500 USD 25500 US 100 US S
3 2023 SE FT Data Scientist 175000 USD 175000 CA 100 CA M
4 2023 SE FT Data Scientist 120000 USD 120000 CA 100 CA M

Бизнес-цели

Задача регрессии: Построить модель для прогноза зарплаты в USD используя атрибуты.
Задача классификации: Определение уровня опыта сотрудника (experience_level) на основе других характеристик, таких как job_title, salary_in_usd, и employment_type.

Проведем обработку данных и сделаем выборки

In [27]:
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.compose import ColumnTransformer
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder

# Удаление выбросов из столбца `salary_in_usd` с использованием IQR
Q1 = df['salary_in_usd'].quantile(0.25)
Q3 = df['salary_in_usd'].quantile(0.75)
IQR = Q3 - Q1
df = df[(df['salary_in_usd'] >= Q1 - 1.5 * IQR) & (df['salary_in_usd'] <= Q3 + 1.5 * IQR)]

# Преобразование категориальных данных в числовые (если потребуется)
if 'remote_ratio' in df.columns:
    df['remote_ratio'] = df['remote_ratio'].astype(int)

# Удаление дубликатов
df.drop_duplicates(inplace=True)

# Определение целевой переменной и признаков
X = df.drop(columns=['salary_in_usd', 'salary_currency', 'job_title'])  # Признаки
y = df['salary_in_usd']  # Целевая переменная для регрессии

# Определение числовых и категориальных признаков
numeric_features = ['work_year', 'remote_ratio']
categorical_features = ['experience_level', 'employment_type', 
                        'employee_residence', 'company_location', 'company_size']

# Обработка числовых данных
numeric_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='median')),  # Заполнение пропусков медианой
    ('scaler', StandardScaler())                   # Нормализация данных
])

# Обработка категориальных данных
categorical_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='most_frequent')),  # Заполнение пропусков модой
    ('onehot', OneHotEncoder(handle_unknown='ignore'))     # Преобразование в One-Hot Encoding
])

# Комбинированный трансформер
preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, numeric_features),   # Применяем числовую обработку
        ('cat', categorical_transformer, categorical_features)  # Применяем категориальную обработку
    ]
)

# Разделение данных на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Применение пайплайна
X_train_transformed = preprocessor.fit_transform(X_train)
X_test_transformed = preprocessor.transform(X_test)

# Проверка результата трансформации
print(f"Transformed feature shape (train): {X_train_transformed.shape}")
print(f"Transformed feature shape (test): {X_test_transformed.shape}")
Transformed feature shape (train): (2029, 151)
Transformed feature shape (test): (508, 151)

Выведим результаты

In [28]:
# Получим имена категориальных признаков после OneHotEncoder
categorical_feature_names = preprocessor.named_transformers_['cat']['onehot'].get_feature_names_out(categorical_features)

# Объединим их с именами числовых признаков
feature_names = list(numeric_features) + list(categorical_feature_names)

# Создадим DataFrame для преобразованных данных
X_train_transformed_df = pd.DataFrame(X_train_transformed.toarray() if hasattr(X_train_transformed, 'toarray') else X_train_transformed, columns=feature_names)

# Выведем первые 5 строк обработанного набора данных
X_train_transformed_df.head()
Out[28]:
work_year remote_ratio experience_level_EN experience_level_EX experience_level_MI experience_level_SE employment_type_CT employment_type_FL employment_type_FT employment_type_PT ... company_location_SI company_location_SK company_location_TH company_location_TR company_location_UA company_location_US company_location_VN company_size_L company_size_M company_size_S
0 -1.747172 1.016983 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0
1 -1.747172 1.016983 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0
2 0.943539 -1.057887 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0
3 -0.401816 1.016983 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0 1.0 0.0
4 -0.401816 -0.020452 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0

5 rows × 151 columns

Обучим три модели

In [29]:
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.ensemble import RandomForestRegressor
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.model_selection import RandomizedSearchCV
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer

# Задание случайного состояния
random_state = 42

# Модели и параметры
models_regression = {
    "LinearRegression": LinearRegression(),
    "RandomForestRegressor": RandomForestRegressor(random_state=random_state),
    "GradientBoostingRegressor": GradientBoostingRegressor(random_state=random_state)
}

param_grids_regression = {
    "LinearRegression": {},
    "RandomForestRegressor": {
        'model__n_estimators': [50, 100, 200],
        'model__max_depth': [None, 10, 20],
        'model__min_samples_split': [2, 5, 10]
    },
    "GradientBoostingRegressor": {
        'model__n_estimators': [50, 100, 200],
        'model__learning_rate': [0.01, 0.1, 0.2],
        'model__max_depth': [3, 5, 10]
    }
}

# Результаты
results_regression = {}

# Перебор моделей
for name, model in models_regression.items():
    print(f"Training {name}...")
    
    # Создание пайплайна
    pipeline = Pipeline(steps=[
        ('preprocessor', preprocessor),  # Используем уже созданный preprocessor
        ('model', model)
    ])
    
    # Определение параметров для RandomizedSearchCV
    param_grid = param_grids_regression[name]
    search = RandomizedSearchCV(pipeline, param_distributions=param_grid, 
                                cv=5, scoring='neg_mean_absolute_error', 
                                n_jobs=-1, random_state=random_state, n_iter=20)
    search.fit(X_train, y_train)
    
    # Лучшая модель
    best_model = search.best_estimator_
    y_pred = best_model.predict(X_test)
    
    # Метрики
    mae = mean_absolute_error(y_test, y_pred)
    rmse = np.sqrt(mean_squared_error(y_test, y_pred))
    r2 = r2_score(y_test, y_pred)
    
    # Сохранение результатов
    results_regression[name] = {
        "Best Params": search.best_params_,
        "MAE": mae,
        "RMSE": rmse,
        "R2": r2
    }

# Печать результатов
for name, metrics in results_regression.items():
    print(f"\nModel: {name}")
    for metric, value in metrics.items():
        print(f"{metric}: {value}")
Training LinearRegression...
Training RandomForestRegressor...
c:\Users\salih\OneDrive\Рабочий стол\3 курас\МИИ\laba1\AIM-PIbd-31-Yaruskin-S-A\aimenv\Lib\site-packages\sklearn\model_selection\_search.py:320: UserWarning: The total space of parameters 1 is smaller than n_iter=20. Running 1 iterations. For exhaustive searches, use GridSearchCV.
  warnings.warn(
Training GradientBoostingRegressor...

Model: LinearRegression
Best Params: {}
MAE: 35903.74761235383
RMSE: 45746.92374132039
R2: 0.41681042958060477

Model: RandomForestRegressor
Best Params: {'model__n_estimators': 100, 'model__min_samples_split': 10, 'model__max_depth': 20}
MAE: 35382.49447920311
RMSE: 45711.49865435396
R2: 0.41771328994747514

Model: GradientBoostingRegressor
Best Params: {'model__n_estimators': 50, 'model__max_depth': 5, 'model__learning_rate': 0.2}
MAE: 35404.55042553757
RMSE: 45669.354449671955
R2: 0.41878648590699374
In [30]:
# Формирование таблицы метрик из результатов регрессионных моделей
reg_metrics = pd.DataFrame.from_dict(results_regression, orient="index")[
    ["MAE", "RMSE", "R2"]
]

# Визуализация результатов с помощью стилизации
styled_metrics = (
    reg_metrics.sort_values(by="RMSE")
    .style.background_gradient(cmap="viridis", low=1, high=0.3, subset=["RMSE", "MAE"])
    .background_gradient(cmap="plasma", low=0.3, high=1, subset=["R2"])
)

# Отобразим таблицу
styled_metrics
Out[30]:
  MAE RMSE R2
GradientBoostingRegressor 35404.550426 45669.354450 0.418786
RandomForestRegressor 35382.494479 45711.498654 0.417713
LinearRegression 35903.747612 45746.923741 0.416810

Чтото слабоватые модели получились. Даже 50% нет, нужно попробовать улучшить данные.

In [38]:
# Функция для приведения выбросов к среднему значению
def handle_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
    
    mean_value = df[column].mean()
    df[column] = np.where((df[column] < lower_bound) | (df[column] > upper_bound), mean_value, df[column])
    return df

# Приведение выбросов в столбце `salary_in_usd` к среднему значению
df = handle_outliers(df, 'salary_in_usd')

# Преобразование категориальных данных в строковые для корректной обработки
if 'remote_ratio' in df.columns:
    df['remote_ratio'] = df['remote_ratio'].astype(str)

# Удаление дубликатов
df.drop_duplicates(inplace=True)

# Определение целевой переменной и признаков
X = df.drop(columns=['salary_in_usd', 'salary_currency', 'job_title'])  # Признаки
y = df['salary_in_usd']  # Целевая переменная для регрессии

# Определение числовых и категориальных признаков
numeric_features = ['work_year']  # Убрали 'remote_ratio', так как это категориальный признак
categorical_features = ['experience_level', 'employment_type', 
                        'employee_residence', 'company_location', 'company_size', 'remote_ratio']

# Обработка числовых данных
numeric_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='median')),  # Заполнение пропусков медианой
    ('scaler', StandardScaler())                   # Нормализация данных
])

# Обработка категориальных данных
categorical_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='most_frequent')),  # Заполнение пропусков модой
    ('onehot', OneHotEncoder(handle_unknown='ignore'))     # Преобразование в One-Hot Encoding
])

# Комбинированный трансформер
preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, numeric_features),   # Применяем числовую обработку
        ('cat', categorical_transformer, categorical_features)  # Применяем категориальную обработку
    ]
)

# Разделение данных на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Применение пайплайна
X_train_transformed = preprocessor.fit_transform(X_train)
X_test_transformed = preprocessor.transform(X_test)

# Проверка результата трансформации
print(f"Transformed feature shape (train): {X_train_transformed.shape}")
print(f"Transformed feature shape (test): {X_test_transformed.shape}")

# Определение моделей и их параметров
models = {
    "LinearRegression": LinearRegression(),
    "RandomForestRegressor": RandomForestRegressor(random_state=42),
    "GradientBoostingRegressor": GradientBoostingRegressor(random_state=42)
}

param_grids = {
    "LinearRegression": {},
    "RandomForestRegressor": {
        'model__n_estimators': [100, 200, 300],
        'model__max_depth': [10, 20, None],
        'model__min_samples_split': [2, 5, 10]
    },
    "GradientBoostingRegressor": {
        'model__n_estimators': [100, 200, 300],
        'model__learning_rate': [0.01, 0.1, 0.2],
        'model__max_depth': [3, 5, 7]
    }
}

# Результаты
results = {}

# Обучение моделей с подбором гиперпараметров
for name, model in models.items():
    print(f"Training {name}...")
    pipeline = Pipeline(steps=[
        ('preprocessor', preprocessor),
        ('model', model)
    ])
    param_grid = param_grids[name]
    search = RandomizedSearchCV(pipeline, param_distributions=param_grid, cv=5, 
                                scoring='neg_mean_absolute_error', n_jobs=-1, 
                                random_state=42, n_iter=20)
    search.fit(X_train, y_train)
    
    # Лучшая модель
    best_model = search.best_estimator_
    y_pred = best_model.predict(X_test)
    
    # Метрики
    mae = mean_absolute_error(y_test, y_pred)
    rmse = np.sqrt(mean_squared_error(y_test, y_pred))
    r2 = r2_score(y_test, y_pred)
    
    # Сохранение результатов
    results[name] = {
        "Best Params": search.best_params_,
        "MAE": mae,
        "RMSE": rmse,
        "R2": r2
    }

# Печать результатов
for name, metrics in results.items():
    print(f"\nModel: {name}")
    for metric, value in metrics.items():
        print(f"{metric}: {value}")

# Формирование таблицы метрик из результатов регрессионных моделей
reg_metrics = pd.DataFrame.from_dict(results, orient="index")[
    ["MAE", "RMSE", "R2"]
]

# Визуализация результатов с помощью стилизации
styled_metrics = (
    reg_metrics.sort_values(by="RMSE")
    .style.background_gradient(cmap="viridis", low=1, high=0.3, subset=["RMSE", "MAE"])
    .background_gradient(cmap="plasma", low=0.3, high=1, subset=["R2"])
)

# Отобразим таблицу
styled_metrics
Transformed feature shape (train): (1886, 41)
Transformed feature shape (test): (472, 41)
Training LinearRegression...
Training RandomForestRegressor...
c:\Users\salih\OneDrive\Рабочий стол\3 курас\МИИ\laba1\AIM-PIbd-31-Yaruskin-S-A\aimenv\Lib\site-packages\sklearn\model_selection\_search.py:320: UserWarning: The total space of parameters 1 is smaller than n_iter=20. Running 1 iterations. For exhaustive searches, use GridSearchCV.
  warnings.warn(
Training GradientBoostingRegressor...

Model: LinearRegression
Best Params: {}
MAE: 37037.92339197355
RMSE: 45540.3787622507
R2: 0.4345993379829327

Model: RandomForestRegressor
Best Params: {'model__n_estimators': 300, 'model__min_samples_split': 10, 'model__max_depth': 10}
MAE: 36990.476148447306
RMSE: 45909.387319133624
R2: 0.4253994599029146

Model: GradientBoostingRegressor
Best Params: {'model__n_estimators': 100, 'model__max_depth': 3, 'model__learning_rate': 0.1}
MAE: 37119.53085174526
RMSE: 45945.52202948378
R2: 0.42449458199205714
Out[38]:
  MAE RMSE R2
LinearRegression 37037.923392 45540.378762 0.434599
RandomForestRegressor 36990.476148 45909.387319 0.425399
GradientBoostingRegressor 37119.530852 45945.522029 0.424495

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

Приступим к задаче классификации

In [39]:
import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split, RandomizedSearchCV
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.metrics import accuracy_score, confusion_matrix, f1_score

# Преобразование целевой переменной для классификации
y_class = df['Leather interior'].map({'Yes': 1, 'No': 0})  # Преобразуем в 0/1
X = df.drop(columns=['Leather interior', 'ID'])  # Признаки

# Определение числовых и категориальных признаков
numeric_features = ['work_year']  # Пример числового признака
categorical_features = ['experience_level', 'employment_type', 'employee_residence', 
                        'company_location', 'company_size', 'remote_ratio']

# Обработка числовых данных
numeric_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='median')),
    ('scaler', StandardScaler())
])

# Обработка категориальных данных
categorical_transformer = Pipeline(steps=[
    ('imputer', SimpleImputer(strategy='most_frequent')),
    ('onehot', OneHotEncoder(handle_unknown='ignore'))
])

# Комбинированный трансформер
preprocessor = ColumnTransformer(
    transformers=[
        ('num', numeric_transformer, numeric_features),
        ('cat', categorical_transformer, categorical_features)
    ]
)

# Разделение данных на обучающую и тестовую выборки
X_train_clf, X_test_clf, y_train_clf, y_test_clf = train_test_split(X, y_class, test_size=0.2, random_state=42)

# Определение моделей и их гиперпараметров
models_classification = {
    "LogisticRegression": LogisticRegression(max_iter=1000),
    "RandomForestClassifier": RandomForestClassifier(random_state=42),
    "KNN": KNeighborsClassifier()
}

param_grids_classification = {
    "LogisticRegression": {
        'model__C': [0.1, 1, 10]
    },
    "RandomForestClassifier": {
        "model__n_estimators": [100, 200, 300],
        "model__max_features": ["sqrt", "log2", None],
        "model__max_depth": [5, 10, 15, None],
        "model__criterion": ["gini", "entropy"]
    },
    "KNN": {
        'model__n_neighbors': [3, 5, 7, 9],
        'model__weights': ['uniform', 'distance']
    }
}

# Результаты
results_classification = {}

# Перебор моделей
for name, model in models_classification.items():
    print(f"Training {name}...")
    pipeline = Pipeline(steps=[
        ('preprocessor', preprocessor),
        ('model', model)
    ])
    param_grid = param_grids_classification[name]
    grid_search = RandomizedSearchCV(pipeline, param_distributions=param_grid, cv=5, scoring='f1', n_jobs=-1, random_state=42)
    grid_search.fit(X_train_clf, y_train_clf)

    # Лучшая модель
    best_model = grid_search.best_estimator_
    y_pred = best_model.predict(X_test_clf)

    # Метрики
    acc = accuracy_score(y_test_clf, y_pred)
    f1 = f1_score(y_test_clf, y_pred)

    # Вычисление матрицы ошибок
    c_matrix = confusion_matrix(y_test_clf, y_pred)

    # Сохранение результатов
    results_classification[name] = {
        "Best Params": grid_search.best_params_,
        "Accuracy": acc,
        "F1 Score": f1,
        "Confusion_matrix": c_matrix
    }

# Печать результатов
for name, metrics in results_classification.items():
    print(f"\nModel: {name}")
    for metric, value in metrics.items():
        print(f"{metric}: {value}")
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
File c:\Users\salih\OneDrive\Рабочий стол\3 курас\МИИ\laba1\AIM-PIbd-31-Yaruskin-S-A\aimenv\Lib\site-packages\pandas\core\indexes\base.py:3805, in Index.get_loc(self, key)
   3804 try:
-> 3805     return self._engine.get_loc(casted_key)
   3806 except KeyError as err:

File index.pyx:167, in pandas._libs.index.IndexEngine.get_loc()

File index.pyx:196, in pandas._libs.index.IndexEngine.get_loc()

File pandas\\_libs\\hashtable_class_helper.pxi:7081, in pandas._libs.hashtable.PyObjectHashTable.get_item()

File pandas\\_libs\\hashtable_class_helper.pxi:7089, in pandas._libs.hashtable.PyObjectHashTable.get_item()

KeyError: 'Leather interior'

The above exception was the direct cause of the following exception:

KeyError                                  Traceback (most recent call last)
Cell In[39], line 14
     11 from sklearn.metrics import accuracy_score, confusion_matrix, f1_score
     13 # Преобразование целевой переменной для классификации
---> 14 y_class = df['Leather interior'].map({'Yes': 1, 'No': 0})  # Преобразуем в 0/1
     15 X = df.drop(columns=['Leather interior', 'ID'])  # Признаки
     17 # Определение числовых и категориальных признаков

File c:\Users\salih\OneDrive\Рабочий стол\3 курас\МИИ\laba1\AIM-PIbd-31-Yaruskin-S-A\aimenv\Lib\site-packages\pandas\core\frame.py:4102, in DataFrame.__getitem__(self, key)
   4100 if self.columns.nlevels > 1:
   4101     return self._getitem_multilevel(key)
-> 4102 indexer = self.columns.get_loc(key)
   4103 if is_integer(indexer):
   4104     indexer = [indexer]

File c:\Users\salih\OneDrive\Рабочий стол\3 курас\МИИ\laba1\AIM-PIbd-31-Yaruskin-S-A\aimenv\Lib\site-packages\pandas\core\indexes\base.py:3812, in Index.get_loc(self, key)
   3807     if isinstance(casted_key, slice) or (
   3808         isinstance(casted_key, abc.Iterable)
   3809         and any(isinstance(x, slice) for x in casted_key)
   3810     ):
   3811         raise InvalidIndexError(key)
-> 3812     raise KeyError(key) from err
   3813 except TypeError:
   3814     # If we have a listlike key, _check_indexing_error will raise
   3815     #  InvalidIndexError. Otherwise we fall through and re-raise
   3816     #  the TypeError.
   3817     self._check_indexing_error(key)

KeyError: 'Leather interior'