300 lines
24 KiB
Markdown
300 lines
24 KiB
Markdown
|
|
|||
|
# Лабораторная работа 4. Вариант 15
|
|||
|
|
|||
|
### Задание
|
|||
|
Использовать алгоритм кластеризации *K-means*, самостоятельно сформулировав задачу. Интерпретировать результаты и оценить, насколько хорошо он подходит для решения сформулированной задачи.
|
|||
|
|
|||
|
### Как запустить лабораторную работу
|
|||
|
Для запуска программы необходимо с помощью командной строки в корневой директории файлов прокета прописать:
|
|||
|
```
|
|||
|
python main.py
|
|||
|
```
|
|||
|
### Какие технологии использовали
|
|||
|
- Библиотека *numpy* для работы с массивами.
|
|||
|
- Библиотека *pandas* для работы с данными в формате таблицы.
|
|||
|
- Библиотека *matplotlib pyplot* - для визуализации данных.
|
|||
|
- Библиотека *sklearn*:
|
|||
|
- *KMeans* для использования алгоритма кластеризации K-средних.
|
|||
|
- *train_test_split* для разделения набора данных на обучающую и тестовую выборки.
|
|||
|
- *LinearRegression* для создания и работы с моделью Линейной регрессии.
|
|||
|
- RFE для рекурсивного отбора признаков
|
|||
|
|
|||
|
### Описание лабораторной работы
|
|||
|
#### Сформулированная задача
|
|||
|
Задача анализа, решаемые алгоритмом кластеризации: выделить локации (страны) с похожими характеристиками вакансий для определения региональных тенденций и особенностей рынка труда.
|
|||
|
|
|||
|
#### Оценка важности параметров
|
|||
|
Прежде, чем перейти к решению поставленной задачи. Произведем анализ важности параметров, чтобы выделить наиболее существенные характеристики, которые влияют на выделение лакаций с похожиим характеристиками. Для этого создадим функцию `recursive_feature_elimination()`, где создаем копию исходного датафрейма `data` и присваивается переменной `df`. В `df` удаляем столбцы `"Country"` и `"location"` с помощью метода `drop()`, т.к. на данных столбца `"Country"` будем использовать как целеввые значения (заранее пропишем `y = data['Country']` общий для всех функций, которые создадим в дальнейшем). Данные разделяем на обучающую и тестовую выборки с использованием функции `train_test_split()`. Размер тестовой выборки составляет 20% от исходных данных. Разделенные данные сохраняются в переменные `X_train`, `X_test`, `y_train` и `y_test`. Задаем имена столбцов датасета в переменной `column_names`, которые будем использовать для вывода данных. Далее для ранжирования важности параметров создаем экземпляры модели линейной регрессии `LinearRegression()` и модели RFE, которую обучаем на обучающих данных с помощью метода `fit()`. Для сортировки полученных результатов воспользуемся функцией `rank_to_dict_rfe(ranking, names)` из прошлой лабораторной работы.
|
|||
|
|
|||
|
```python
|
|||
|
def recursive_feature_elimination():
|
|||
|
df = data.copy()
|
|||
|
df.drop(["Country", "location"], axis=1, inplace=True)
|
|||
|
X_train, X_test, y_train, y_test = train_test_split(df, y, test_size=0.2)
|
|||
|
column_names = ['Qualifications', 'Work Type', 'Company Size', 'Preference', 'Job Title', 'Role', 'Job Portal',
|
|||
|
'skills', 'Company', 'Min Experience', 'Max Experience', 'Min Salary',
|
|||
|
'Max Salary', 'Sector', 'Industry', 'City', 'State', 'Ticker', 'year', 'month', 'day',
|
|||
|
"'Casual Dress Code, Social and Recreational Activities, Employee Referral Programs, Health and Wellness Facilities, Life and Disability Insurance'",
|
|||
|
"'Childcare Assistance, Paid Time Off (PTO), Relocation Assistance, Flexible Work Arrangements, Professional Development'",
|
|||
|
"'Employee Assistance Programs (EAP), Tuition Reimbursement, Profit-Sharing, Transportation Benefits, Parental Leave'",
|
|||
|
"'Employee Referral Programs, Financial Counseling, Health and Wellness Facilities, Casual Dress Code, Flexible Spending Accounts (FSAs)'",
|
|||
|
"'Flexible Spending Accounts (FSAs), Relocation Assistance, Legal Assistance, Employee Recognition Programs, Financial Counseling'",
|
|||
|
"'Health Insurance, Retirement Plans, Flexible Work Arrangements, Employee Assistance Programs (EAP), Bonuses and Incentive Programs'",
|
|||
|
"'Health Insurance, Retirement Plans, Paid Time Off (PTO), Flexible Work Arrangements, Employee Assistance Programs (EAP)'",
|
|||
|
"'Legal Assistance, Bonuses and Incentive Programs, Wellness Programs, Employee Discounts, Retirement Plans'",
|
|||
|
"'Life and Disability Insurance, Stock Options or Equity Grants, Employee Recognition Programs, Health Insurance, Social and Recreational Activities'",
|
|||
|
"'Transportation Benefits, Professional Development, Bonuses and Incentive Programs, Profit-Sharing, Employee Discounts'",
|
|||
|
"'Tuition Reimbursement, Stock Options or Equity Grants, Parental Leave, Wellness Programs, Childcare Assistance'"]
|
|||
|
|
|||
|
estimator = LinearRegression()
|
|||
|
rfe_model = RFE(estimator)
|
|||
|
rfe_model.fit(X_train.values, y_train.values)
|
|||
|
ranks = rank_to_dict_rfe(rfe_model.ranking_, column_names)
|
|||
|
sorted_dict = dict(sorted(ranks.items(), key=lambda x: x[1], reverse=True))
|
|||
|
print(sorted_dict)
|
|||
|
|
|||
|
def rank_to_dict_rfe(ranking, names):
|
|||
|
n_ranks = [float(1 / i) for i in ranking]
|
|||
|
n_ranks = map(lambda x: round(x, 2), n_ranks)
|
|||
|
return dict(zip(names, n_ranks))
|
|||
|
```
|
|||
|
После запуска функции `recursive_feature_elimination()`, получаем следующий результат:
|
|||
|
```
|
|||
|
{'Qualifications': 1.0, 'Work Type': 1.0, 'Preference': 1.0, 'Job Portal': 1.0, 'Min Experience': 1.0, "'Casual Dress Code, Social and Recreational Activities, Employee Referral Programs, Health and Wellness Facilities, Life and Disability Insurance'": 1.0, "'Childcare Assistance, Paid Time Off (PTO), Relocation Assistance, Flexible Work Arrangements, Professional Development'": 1.0, "'Employee Assistance Programs (EAP), Tuition Reimbursement, Profit-Sharing, Transportation Benefits, Parental Leave'": 1.0, "'Employee Referral Programs, Financial Counseling, Health and Wellness Facilities, Casual Dress Code, Flexible Spending Accounts (FSAs)'": 1.0, "'Flexible Spending Accounts (FSAs), Relocation Assistance, Legal Assistance, Employee Recognition Programs, Financial Counseling'": 1.0, "'Health Insurance, Retirement Plans, Flexible Work Arrangements, Employee Assistance Programs (EAP), Bonuses and Incentive Programs'": 1.0, "'Health Insurance, Retirement Plans, Paid Time Off (PTO), Flexible Work Arrangements, Employee Assistance Programs (EAP)'": 1.0, "'Legal Assistance, Bonuses and Incentive Programs, Wellness Programs, Employee Discounts, Retirement Plans'": 1.0, "'Life and Disability Insurance, Stock Options or Equity Grants, Employee Recognition Programs, Health Insurance, Social and Recreational Activities'": 1.0, "'Transportation Benefits, Professional Development, Bonuses and Incentive Programs, Profit-Sharing, Employee Discounts'": 1.0, "'Tuition Reimbursement, Stock Options or Equity Grants, Parental Leave, Wellness Programs, Childcare Assistance'": 1.0, 'month': 0.5, 'year': 0.33, 'Max Experience': 0.25, 'State': 0.2, 'day': 0.17, 'Sector': 0.14, 'Company': 0.12, 'Ticker': 0.11, 'Job Title': 0.1, 'Role': 0.09, 'Industry': 0.08, 'City': 0.08, 'skills': 0.07, 'Min Salary': 0.07, 'Company Size': 0.06, 'Max Salary': 0.06}
|
|||
|
```
|
|||
|
Как можно заметить, наиболее важными параметрами являются 'Qualifications': 1.0, 'Work Type': 1.0, 'Preference': 1.0, 'Job Portal': 1.0, 'Min Experience': 1.0 и все показатели льгот.
|
|||
|
|
|||
|
#### Оценка количества кластеров
|
|||
|
Для оценки количества кластеров воспользуемся методом локтя - графический метод для оценки оптимального количества кластеров при использовании алгоритма k-means. Данный метод основан на расчете суммы квадратов расстояний между каждым объектом данных и центроидом его кластера. Эта сумма называется инерцией. Чем меньше инерция, тем лучше кластеризация.
|
|||
|
|
|||
|
Сначала создаем копию данных `df`, после чего удаляем ненужных столбцов из `df` с помощью метода `drop()`. Затем данные разделяем на обучающую и тестовую выборки с помощью функции `train_test_split()`.
|
|||
|
Далее инициализируем пустой список `inertias`, который будет хранить значения инерции для различного количества кластеров. В цикле for перебираем значения `k` от 1 до 14. Внутри цикла создаем объект KMeans с параметром `n_clusters=k`, который выполняет кластеризацию обучающей выборки. Значение инерции для текущего количества кластеров добавляется в список `inertias`.
|
|||
|
После завершения цикла строим график метода локтя, где по оси *x* отображается количество кластеров, а по оси *y* - значение инерции.
|
|||
|
|
|||
|
С помощью графика выберем точку, где изменение инерции становится менее значительным по сравнению с предыдущими значениями.
|
|||
|
|
|||
|
``` python
|
|||
|
df = data.copy()
|
|||
|
df.drop(['Country', 'location', 'Company Size', 'Job Title', 'Role',
|
|||
|
'skills', 'Company', 'Max Experience', 'Min Salary',
|
|||
|
'Max Salary', 'Sector', 'Industry', 'City', 'State', 'Ticker', 'year', 'month', 'day'
|
|||
|
],
|
|||
|
axis=1, inplace=True)
|
|||
|
X_train, X_test, y_train, y_test = train_test_split(df, y, test_size=0.2)
|
|||
|
inertias = []
|
|||
|
for k in range(1, 15):
|
|||
|
kmeans = KMeans(n_clusters=k, random_state=1).fit(X_train.values, y_train.values)
|
|||
|
inertias.append(np.sqrt(kmeans.inertia_))
|
|||
|
plt.plot(range(1, 15), inertias, marker='o')
|
|||
|
plt.xlabel('Number of clusters')
|
|||
|
plt.ylabel('Inertia')
|
|||
|
plt.title("Метод локтя")
|
|||
|
plt.savefig('static/charts/ElbowMethod.png')
|
|||
|
plt.close()
|
|||
|
```
|
|||
|
Выполним построение графика:
|
|||
|
|
|||
|
|
|||
|
![График "Метод локтя"](ElbowMethod.png)
|
|||
|
|
|||
|
Таким образом, с помощью метода локтя получилось визуально определить оптимальное количество кластеров для алгоритма k-means на основе значения инерции, равное 9.
|
|||
|
|
|||
|
#### Алгоритм кластеризации *K-means*
|
|||
|
Для работы с алгоритмом кластеризации *K-means* создадим функцию `k_means()` . Сохраняем копию оригинальных данных в переменной `df`. Затем, из этой копии удаляем столбцы, которые имеют наименьшую важность. Далее, данные разделяем на обучающую и тестовую выборки с помощью функции `train_test_split`. Обучающие данные сохраняются в переменные `X_train` и `y_train`, а тестовые данные - в переменные `X_test` и `y_test`. Здесь `y` представляет собой целевую переменную. Затем, создаем объект `kmeans` класса KMeans с параметром `n_clusters=9`, что означает, что алгоритм будет искать 9 кластеров в данных. Обучение модели выполняется с помощью метода `fit`, передавая в него значения `X_train` и `y_train`.Далее, применяется обученная модель к тестовым данным с помощью метода `predict`, чтобы получить метки кластеров. Координаты центроидов кластеров сохраняются в переменной `centroids`.
|
|||
|
|
|||
|
```python
|
|||
|
def k_means():
|
|||
|
df = data.copy()
|
|||
|
df.drop(['Country', 'location', 'Company Size', 'Preference', 'Job Title', 'Role', 'Job Portal',
|
|||
|
'skills', 'Company', 'Min Experience', 'Max Experience', 'Min Salary',
|
|||
|
'Max Salary', 'Sector', 'Industry', 'City', 'State', 'Ticker', 'year', 'month', 'day',
|
|||
|
"'Casual Dress Code, Social and Recreational Activities, Employee Referral Programs, Health and Wellness Facilities, Life and Disability Insurance'",
|
|||
|
"'Childcare Assistance, Paid Time Off (PTO), Relocation Assistance, Flexible Work Arrangements, Professional Development'",
|
|||
|
"'Employee Assistance Programs (EAP), Tuition Reimbursement, Profit-Sharing, Transportation Benefits, Parental Leave'",
|
|||
|
"'Employee Referral Programs, Financial Counseling, Health and Wellness Facilities, Casual Dress Code, Flexible Spending Accounts (FSAs)'",
|
|||
|
"'Flexible Spending Accounts (FSAs), Relocation Assistance, Legal Assistance, Employee Recognition Programs, Financial Counseling'",
|
|||
|
"'Health Insurance, Retirement Plans, Flexible Work Arrangements, Employee Assistance Programs (EAP), Bonuses and Incentive Programs'",
|
|||
|
"'Health Insurance, Retirement Plans, Paid Time Off (PTO), Flexible Work Arrangements, Employee Assistance Programs (EAP)'",
|
|||
|
"'Legal Assistance, Bonuses and Incentive Programs, Wellness Programs, Employee Discounts, Retirement Plans'",
|
|||
|
"'Life and Disability Insurance, Stock Options or Equity Grants, Employee Recognition Programs, Health Insurance, Social and Recreational Activities'",
|
|||
|
"'Transportation Benefits, Professional Development, Bonuses and Incentive Programs, Profit-Sharing, Employee Discounts'",
|
|||
|
"'Tuition Reimbursement, Stock Options or Equity Grants, Parental Leave, Wellness Programs, Childcare Assistance'"],
|
|||
|
axis=1, inplace=True)
|
|||
|
X_train, X_test, y_train, y_test = train_test_split(df, y, test_size=0.2)
|
|||
|
kmeans = KMeans(n_clusters=9)
|
|||
|
kmeans.fit(X_train.values, y_train.values)
|
|||
|
labels = kmeans.predict(X_test.values)
|
|||
|
centroids = kmeans.cluster_centers_
|
|||
|
print("Метки кластеров:", labels)
|
|||
|
print("Координаты центроидов:", centroids)
|
|||
|
plt.scatter(X_test['Qualifications'], X_test['Work Type'], c=labels, cmap='viridis')
|
|||
|
plt.scatter(centroids[:, 0], centroids[:, 1], marker='x', color='red')
|
|||
|
plt.xlabel('Qualifications')
|
|||
|
plt.ylabel('Work Type')
|
|||
|
plt.title('KMeans Clustering')
|
|||
|
plt.savefig('static/charts/KMeansClustering.png')
|
|||
|
plt.close()
|
|||
|
|
|||
|
print("Уникальных Work Type :", data['Work Type'].nunique())
|
|||
|
print("Уникальных Qualifications:", data['Qualifications'].nunique())
|
|||
|
|
|||
|
unique_labels = np.unique(labels)
|
|||
|
for label in unique_labels:
|
|||
|
indices = np.where(labels == label)
|
|||
|
y_values = data_orig.loc[indices, 'Country']
|
|||
|
print(f"Значения y для кластера {label}: {y_values}")
|
|||
|
```
|
|||
|
Выполним построение графика:
|
|||
|
|
|||
|
|
|||
|
![График "Алгоритм кластеризации K-means"](KMeansClustering.png)
|
|||
|
|
|||
|
Также выводим результаты работы алгоритма кластеризации в консоль, где можно увидеть подробные результаты разбиения на кластеры:
|
|||
|
|
|||
|
```
|
|||
|
Значения y для кластера 0: 9 Antigua and Barbuda
|
|||
|
16 San Marino
|
|||
|
21 Tuvalu
|
|||
|
22 Eritrea
|
|||
|
35 New Caledonia
|
|||
|
...
|
|||
|
319899 Nepal
|
|||
|
319912 Uzbekistan
|
|||
|
319927 Colombia
|
|||
|
319933 Spain
|
|||
|
319955 Niger
|
|||
|
Name: Country, Length: 25672, dtype: object
|
|||
|
Значения y для кластера 1: 10 Bahrain
|
|||
|
14 Syrian Arab Republic
|
|||
|
19 Democratic Republic Of Congo
|
|||
|
31 Chile
|
|||
|
40 Cote d'Ivoire
|
|||
|
...
|
|||
|
319904 Mauritania
|
|||
|
319905 Macao SAR, China
|
|||
|
319906 Vietnam
|
|||
|
319929 French Polynesia
|
|||
|
319945 Mauritius
|
|||
|
Name: Country, Length: 31971, dtype: object
|
|||
|
Значения y для кластера 2: 7 Sao Tome and Principe
|
|||
|
15 Yemen
|
|||
|
17 French Polynesia
|
|||
|
20 Azerbaijan
|
|||
|
24 British Virgin Islands
|
|||
|
...
|
|||
|
319935 Dominican Republic
|
|||
|
319937 Japan
|
|||
|
319938 Gambia
|
|||
|
319946 Indonesia
|
|||
|
319949 Latvia
|
|||
|
Name: Country, Length: 38400, dtype: object
|
|||
|
Значения y для кластера 3: 0 Isle of Man
|
|||
|
6 Cayman Islands
|
|||
|
11 Bermuda
|
|||
|
23 Honduras
|
|||
|
30 Jordan
|
|||
|
...
|
|||
|
319931 Sint Maarten (Dutch part)
|
|||
|
319942 Hong Kong SAR, China
|
|||
|
319943 Bangladesh
|
|||
|
319950 Croatia
|
|||
|
319952 Syrian Arab Republic
|
|||
|
Name: Country, Length: 51148, dtype: object
|
|||
|
Значения y для кластера 4: 3 Benin
|
|||
|
4 Chile
|
|||
|
5 Belgium
|
|||
|
12 Jamaica
|
|||
|
18 North Korea
|
|||
|
...
|
|||
|
319922 Gabon
|
|||
|
319925 Korea, Rep.
|
|||
|
319926 Ethiopia
|
|||
|
319941 Zimbabwe
|
|||
|
319951 Korea, Rep.
|
|||
|
Name: Country, Length: 38574, dtype: object
|
|||
|
Значения y для кластера 5: 1 Turkmenistan
|
|||
|
38 Solomon Islands
|
|||
|
42 Virgin Islands (U.S.)
|
|||
|
47 Belize
|
|||
|
59 Republic Of Congo
|
|||
|
...
|
|||
|
319918 Spain
|
|||
|
319924 Paraguay
|
|||
|
319947 Brazil
|
|||
|
319948 Macao SAR, China
|
|||
|
319953 China
|
|||
|
Name: Country, Length: 38422, dtype: object
|
|||
|
Значения y для кластера 6: 37 West Bank and Gaza
|
|||
|
57 Guam
|
|||
|
102 Fiji
|
|||
|
111 Ecuador
|
|||
|
124 Cabo Verde
|
|||
|
...
|
|||
|
319896 Sint Maarten (Dutch part)
|
|||
|
319903 Madagascar
|
|||
|
319914 South Africa
|
|||
|
319940 Netherlands
|
|||
|
319954 Albania
|
|||
|
Name: Country, Length: 25701, dtype: object
|
|||
|
Значения y для кластера 7: 2 Macao SAR, China
|
|||
|
8 Maldives
|
|||
|
13 Gambia
|
|||
|
25 Cuba
|
|||
|
27 Gambia
|
|||
|
...
|
|||
|
319892 Turks and Caicos Islands
|
|||
|
319920 Luxembourg
|
|||
|
319923 Trinidad and Tobago
|
|||
|
319939 China
|
|||
|
319944 Fiji
|
|||
|
Name: Country, Length: 38449, dtype: object
|
|||
|
Значения y для кластера 8: 48 Malta
|
|||
|
49 Panama
|
|||
|
52 Republic Of Congo
|
|||
|
81 Bangladesh
|
|||
|
82 Nepal
|
|||
|
...
|
|||
|
319902 Cambodia
|
|||
|
319921 Barbados
|
|||
|
319932 Lesotho
|
|||
|
319934 St. Kitts and Nevis
|
|||
|
319936 Nigeria
|
|||
|
Name: Country, Length: 31619, dtype: object
|
|||
|
```
|
|||
|
|
|||
|
Также выведем координаты центроидов:
|
|||
|
```
|
|||
|
Координаты центроидов: [[2.49909662 0.4986962 ]
|
|||
|
[6.49757212 3.00005221]
|
|||
|
[1.12332665 3.12941867]
|
|||
|
[6.79715885 0.40238426]
|
|||
|
[3.99726263 3.50030885]
|
|||
|
[8.50117367 3.49953735]
|
|||
|
[4.2877207 1.14426123]
|
|||
|
[8.60271303 1.19776253]
|
|||
|
[0.40101301 0.79759215]]
|
|||
|
```
|
|||
|
|
|||
|
И оценим качество кластеризации, используя силуэтный коэффициент и индекс Дэвиса-Болдина:
|
|||
|
1. Силуэтный коэффициент - это метрика, которая измеряет, насколько точка хорошо соответствует своему собственному кластеру в сравнении с другими кластерами. Он находится в диапазоне от -1 до 1, где значение ближе к 1 указывает на хорошую кластеризацию, а значение ближе к -1 указывает на плохую кластеризацию.
|
|||
|
```python
|
|||
|
# Оценка силуэтного коэффициента
|
|||
|
silhouette = silhouette_score(X_test.values, kmeans.predict(X_test.values))
|
|||
|
print("Силуэтный коэффициент:", silhouette)
|
|||
|
```
|
|||
|
В нашем случае силуэтный коэффициент равен 0.4086103390706535.
|
|||
|
2. Индекс Дэвиса-Болдина - это метрика, которая измеряет сходство между кластерами на основе их средних расстояний и средних расстояний между кластерами. Чем меньше значение этого индекса, тем лучше кластеризация.
|
|||
|
```python
|
|||
|
# Оценка индекса Дэвиса-Болдина
|
|||
|
davies_bouldin = davies_bouldin_score(X_test.values, kmeans.predict(X_test.values))
|
|||
|
print("Индекс Дэвиса-Болдина:", davies_bouldin)
|
|||
|
```
|
|||
|
В нашем случае индекс Дэвиса-Болдина равен 0.8682047121172671.
|
|||
|
|
|||
|
### Вывод
|
|||
|
|
|||
|
Таким образом, с помощью оценок силуэтного коэффициента и индекса Дэвиса-Болдина, можно сделать следующие выводы о качестве кластеризации:
|
|||
|
1. Силуэтный коэффициент равен 0.4086103390706535. В данном случае, значение силуэтного коэффициента выше 0, что говорит о том, что кластеры имеют некоторую степень разделения, но не являются идеально разделимыми. В целом, это может указывать на некоторое качество кластеризации, но не является оптимальным.
|
|||
|
2. Индекс Дэвиса-Болдина равен 0.8682047121172671. Значение этого индекса является положительным числом. В данном случае, значение индекса Дэвиса-Болдина выше 0, что указывает на некоторое сходство между кластерами.
|
|||
|
|
|||
|
Таким образом, на основе предоставленных значений можно сказать, что кластеризация имеет некоторую степень разделения, но не является идеальной.
|