Введение в анализ данных¶
Домашнее задание 10. Анализ вакансий.¶
Правила, прочитайте внимательно:
- Выполненную работу нужно отправить телеграм-боту
@thetahat_ds25_bot
. Для начала работы с ботом каждый раз отправляйте/start
. Дождитесь подтверждения от бота, что он принял файл. Если подтверждения нет, то что-то не так. Работы, присланные иным способом, не принимаются. - Дедлайн см. в боте. После дедлайна работы не принимаются кроме случаев наличия уважительной причины.
- Прислать нужно ноутбук в формате
ipynb
. Если вы строите интерактивные графики, их стоит прислать в формате html. - Следите за размером файлов. Бот не может принимать файлы весом более 20 Мб. Если файл получается больше, заранее разделите его на несколько.
- Выполнять задание необходимо полностью самостоятельно. При обнаружении списывания всем участникам списывания дается штраф -2 балла к итоговой оценке за семестр.
- Решения, размещенные на каких-либо интернет-ресурсах, не принимаются. Кроме того, публикация решения в открытом доступе может быть приравнена к предоставлении возможности списать.
- Обратите внимание на правила использования ИИ-инструментов при решении домашнего задания.
- Код из рассказанных на занятиях ноутбуков можно использовать без ограничений.
- Для выполнения задания используйте этот ноутбук в качестве основы, ничего не удаляя из него. Можно добавлять необходимое количество ячеек.
- Комментарии к решению пишите в markdown-ячейках.
- Выполнение задания (ход решения, выводы и пр.) должно быть осуществлено на русском языке.
- Решение проверяется системой ИИ-проверки
ThetaGrader. Результат проверки валидируется и исправляется человеком, после чего комментарии отправляются студентам.
- Если код будет не понятен проверяющему, оценка может быть снижена.
- Никакой код из данного задания при проверке запускаться не будет. Если код студента не выполнен, недописан и т.д., то он не оценивается.
- Код из рассказанных на занятиях ноутбуков можно использовать без ограничений.
Правила оформления теоретических задач:
- Решения необходимо оформить в виде $\LaTeX$ в markdown-ячейках. Иные способы (в т.ч. фотографии) не принимаются.
- Если вы не знаете $\LaTeX$, используйте ИИ-инструменты для оформления черновика решения. Примеры были показаны на лекции 2 по ИИ-инструментам.
- В решениях поясняйте, чем вы пользуетесь, хотя бы кратко.
- Решение, в котором есть только ответ, и отсутствуют вычисления, оценивается в 0 баллов.
Важно!!! Правила заполнения ноутбука:
- Запрещается удалять имеющиеся в ноутбуке ячейки, менять местами положения задач.
- Сохраняйте естественный линейный порядок повествования в ноутбуке сверху-вниз.
- Отвечайте на вопросы, а также добавляйте новые ячейки в предложенных местах, которые обозначены
...
. - В markdown-ячейка, содержащих описание задачи, находятся специальные отметки, которые запрещается модифицировать.
- При нарушении данных правил работа может получить 0 баллов.
Баллы за задание:
Сложная часть (учитывается только в основной части курса, необходимо на "отл"):
- Задача 1 — 70 баллов
Факультативная часть (учитывается только в факультативной части курса):
- Задача 2 — 30 баллов
- Задача 3 — 70 баллов
Внимание! Указанные баллы являются бонусными. Они не учитываются в максимальных суммах баллов в знаменателе при вычислении процента выполненных заданий. Но все полученные вами баллы будут учтены в числителе. Тем самым, выполняя или не выполняя домашку, вы не сможете понизить оценку, но сможете ее повысить.
# Bot check
# HW_ID: fpmi_ad10
# Бот проверит этот ID и предупредит, если случайно сдать что-то не то.
# Status: not final
# Перед отправкой в финальном решении удали "not" в строчке выше.
# Так бот проверит, что ты отправляешь финальную версию, а не промежуточную.
# Никакие значения в этой ячейке не влияют на факт сдачи работы.
import requests
from time import sleep
import json
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style="whitegrid", font_scale=1.3, palette="Set2")
Второй курс — самое время задуматься о будущей профессии и проанализировать существующие предложения. Дело тут даже не в том, чтобы найти интересную стажировку. В первую очередь сейчас стоит подумать о том, в какую сторону развиваться дальше. Например, если вы хотите работать в какой-либо конкретной профессии, то наверняка стоит развивать какие-то определенные навыки, и даже выбрать подходящую кафедру. Анализ существующих вакансий поможет определить, какие навыки вам нужны.
Внимание! В первую очередь задание призвано помочь вам понять, какие навыки стоит развивать, и может быть даже выбрать подходящую кафедру. Однако, не стоит идти на работу/стажировку раньше времени. Даже только летнюю, абсолютное большинство людей не могут работать круглый год без перерыва. У всех из вас 3 курс достаточно нагруженный, а на DS-потоке нагрузка с первой недели сентября. И если вы идете на стажировку, то каникул у вас может просто не остаться. Даже если вам сейчас/летом/в сентябре кажется "я же справлюсь", к ноябрю-декабрю с большой вероятностью вы выдохнетесь. Таких примеров очень много. Не все, кто-то успешно справляется совмещать работу и учебу, но таких очень мало. Берегите себя и свое здоровье :)
Задание предполагает вариативность исследуемых данных. Вы можете выбрать один из следующих вариантов.
- Стандартный вариант — проанализировать вакансии на сайте hh.ru с использованием официального API. Далее приведен пример работы с этим API. Если вы не достаточно уверены в своих силах, рекомендуется выбрать этот вариант.
- Разобраться с API других сервисов по поиску вакансий, например, Хабр.Карьера или SuperJob.
- Извлечение данных с помощью парсинга HTML напрямую некоторых сайтов. Например, вы можете попробовать напрямую распарсить пул вакансий из тех источников, которые не предоставляют API. Внимание! Обращайте внимание на пользовательские соглашения и на эти рекомендации.
- Комбинирование нескольких подходов. В этом случае нужно понимать, как находить одинаковые вакансии из разных источников для избежания их двойного учета.
- Можно также дополнительно брать архивные данные из недавнего прошлого, например, из этого соревнования.
Перед выполнением задания ознакомьтесь с материалом о сборе данных из открытых источников.
Для получения максимального балла за задание достаточно выполнить все задание по стандартному варианту. Альтернативные варианты, даже более сложные, не дают больше баллов, чем максимальное количество баллов по стандартному варианту.
Работа в некоторой степени творческая, поэтому конкретные детали решения в основном остаются на усмотрение автора решения. Например, если в вакансии зарплата укзана "от ..." или "до ...", то вы сами решаете, как это обрабатывать, но не забывайте про четкую аргументацию. Не забывайте также заглядывать в презентацию по выполнению и оформлению домашних заданий с занятия 2.
Справка по работе с API сервиса hh.ru.¶
Внимание! При работе с API не забывайте делать паузы между запросами, чтобы не задудосить сервер. Если вас заблокируют, это не будет являться уважительной причиной переноса дедлайна.
Мы будем работать только с вакансиями. Для этого не требуется регистрироваться и получать токен. Ниже приведен краткий пример работы с API. Подробное описание работы с вакансиями, включая параметры запросов и формат ответа можно почитать в документации.
Например, мы хотим найти вакансии по запросу Data Scientist
в Москве. Тогда первую страницу поиска из 10 вакансий на страницу мы можем получить с запроса к API:
URL = "https://api.hh.ru/vacancies"
params = {
"text": "Data Scientist",
"area": 1,
"page": 0,
"per_page": 10,
}
req = requests.get(URL, params)
data = json.loads(req.content.decode())
Если все прошло успешно, полученный словарь будет иметь следующие ключи
data.keys()
dict_keys(['items', 'found', 'pages', 'page', 'per_page', 'clusters', 'arguments', 'fixes', 'suggests', 'alternate_url'])
Можем посмотреть на краткое описание первой вакансии
data["items"][0]
{'id': '119624423', 'premium': False, 'name': 'Data Scientist (Middle+/Senior)', 'department': None, 'has_test': False, 'response_letter_required': False, 'area': {'id': '1', 'name': 'Москва', 'url': 'https://api.hh.ru/areas/1'}, 'salary': None, 'salary_range': None, 'type': {'id': 'open', 'name': 'Открытая'}, 'address': {'city': 'Москва', 'street': 'Западный административный округ, Можайский район, Инновационный центр Сколково, Большой бульвар', 'building': '30с1', 'lat': 55.698548, 'lng': 37.359415, 'description': None, 'raw': 'Москва, Западный административный округ, Можайский район, Инновационный центр Сколково, Большой бульвар, 30с1', 'metro': None, 'metro_stations': [], 'id': '4578402'}, 'response_url': None, 'sort_point_distance': None, 'published_at': '2025-04-17T09:58:23+0300', 'created_at': '2025-04-17T09:58:23+0300', 'archived': False, 'apply_alternate_url': 'https://hh.ru/applicant/vacancy_response?vacancyId=119624423', 'branding': {'type': 'CONSTRUCTOR', 'tariff': 'BASIC'}, 'show_logo_in_search': True, 'insider_interview': None, 'url': 'https://api.hh.ru/vacancies/119624423?host=hh.ru', 'alternate_url': 'https://hh.ru/vacancy/119624423', 'relations': [], 'employer': {'id': '4977893', 'name': 'СберМедИИ', 'url': 'https://api.hh.ru/employers/4977893', 'alternate_url': 'https://hh.ru/employer/4977893', 'logo_urls': {'original': 'https://img.hhcdn.ru/employer-logo-original/1372877.png', '90': 'https://img.hhcdn.ru/employer-logo/7111410.png', '240': 'https://img.hhcdn.ru/employer-logo/7111411.png'}, 'vacancies_url': 'https://api.hh.ru/vacancies?employer_id=4977893', 'accredited_it_employer': True, 'employer_rating': {'total_rating': '4.6', 'reviews_count': 9}, 'trusted': True}, 'snippet': {'requirement': 'Опыт работы на позиции <highlighttext>Data</highlighttext> <highlighttext>Scientist</highlighttext> от 2-х лет. Глубокие знания и опыт работы с методами машинного обучения. ', 'responsibility': 'Участвовать в исследовании новых данных, оценивать их качество и полноту. Разрабатывать, тестировать и поддерживать CV/NLP модели медицинского ИИ. '}, 'show_contacts': False, 'contacts': None, 'schedule': {'id': 'remote', 'name': 'Удаленная работа'}, 'working_days': [], 'working_time_intervals': [], 'working_time_modes': [], 'accept_temporary': False, 'fly_in_fly_out_duration': [], 'work_format': [{'id': 'REMOTE', 'name': 'Удалённо'}], 'working_hours': [{'id': 'HOURS_8', 'name': '8\xa0часов'}], 'work_schedule_by_days': [{'id': 'FIVE_ON_TWO_OFF', 'name': '5/2'}], 'night_shifts': False, 'professional_roles': [{'id': '165', 'name': 'Дата-сайентист'}], 'accept_incomplete_resumes': False, 'experience': {'id': 'between3And6', 'name': 'От 3 до 6 лет'}, 'employment': {'id': 'full', 'name': 'Полная занятость'}, 'employment_form': {'id': 'FULL', 'name': 'Полная'}, 'internship': False, 'adv_response_url': None, 'is_adv_vacancy': False, 'adv_context': None}
Сколько всего найдено вакансий
data["found"]
283
Количество страниц в результатах поиска
data["pages"]
29
Из результатов можем сделать удобную таблицу, причем в дальнейшем можно оставить только те колонки, которые необходимы для анализа.
df = pd.json_normalize(data["items"])
df.head()
id | premium | name | department | has_test | response_letter_required | salary | salary_range | response_url | sort_point_distance | ... | salary.gross | salary_range.from | salary_range.to | salary_range.currency | salary_range.gross | salary_range.mode.id | salary_range.mode.name | salary_range.frequency | department.id | department.name | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 119624423 | False | Data Scientist (Middle+/Senior) | NaN | False | False | NaN | NaN | None | None | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
1 | 119496066 | False | Data Scientist | NaN | False | False | NaN | NaN | None | None | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
2 | 119556787 | False | Data Scientist (AI Stylist) | NaN | False | False | NaN | NaN | None | None | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
3 | 119495959 | False | Junior ML Engineer / Data Scientist | NaN | False | False | NaN | NaN | None | None | ... | True | 120000.0 | 200000.0 | RUR | True | MONTH | За месяц | NaN | NaN | NaN |
4 | 119647385 | False | Data Scientist (Middle) | NaN | False | False | NaN | NaN | None | None | ... | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
5 rows × 95 columns
Для получения полного описания вакансии потребуется задать отдельный запрос, используя ее id
.
vacancy = df["id"].iloc[0]
vacancy_url = f"https://api.hh.ru/vacancies/{vacancy}"
req = requests.get(vacancy_url)
vacancy_info = json.loads(req.content.decode())
vacancy_info
{'id': '119624423', 'premium': False, 'billing_type': {'id': 'standard_plus', 'name': 'Стандарт плюс'}, 'relations': [], 'name': 'Data Scientist (Middle+/Senior)', 'insider_interview': None, 'response_letter_required': False, 'area': {'id': '1', 'name': 'Москва', 'url': 'https://api.hh.ru/areas/1'}, 'salary': None, 'salary_range': None, 'type': {'id': 'open', 'name': 'Открытая'}, 'address': {'city': 'Москва', 'street': 'Западный административный округ, Можайский район, Инновационный центр Сколково, Большой бульвар', 'building': '30с1', 'lat': 55.698548, 'lng': 37.359415, 'description': None, 'raw': 'Москва, Западный административный округ, Можайский район, Инновационный центр Сколково, Большой бульвар, 30с1', 'metro': None, 'metro_stations': []}, 'allow_messages': True, 'experience': {'id': 'between3And6', 'name': 'От 3 до 6 лет'}, 'schedule': {'id': 'remote', 'name': 'Удаленная работа'}, 'employment': {'id': 'full', 'name': 'Полная занятость'}, 'department': None, 'show_contacts': False, 'contacts': None, 'description': '<p>Мы стремимся повышать уровень здоровья людей и для этой цели обеспечиваем врачей и медицинские организации актуальными и удобными решениями.</p> <p>Сейчас в нашу команду мы ищем опытного <strong>Data scientist</strong> для разработки алгоритмов CV/NLP для решения различных медицинских задач.</p> <p><strong>Чем предстоит заниматься:</strong></p> <ul> <li>Участвовать в исследовании новых данных, оценивать их качество и полноту;</li> <li>Разрабатывать, тестировать и поддерживать CV/NLP модели медицинского ИИ;</li> <li>Разрабатывать и внедрять AI-агенты на базе большой языковой модели GigaChat;</li> <li>Разрабатывать код обучения для моделей медицинского ИИ;</li> <li>Анализировать и выбирать подходы к решению поставленных задач.</li> <li>Анализировать большие данные и разрабатывать модели для решения конкретных бизнес-задач;</li> <li>Работать со смежными командами для интеграции моделей и решений в промышленные бизнес-процессы.</li> </ul> <p><strong>Наши ожидания от кандидатов:</strong></p> <ul> <li>Опыт работы на позиции Data Scientist от 2-х лет;</li> <li>Глубокие знания и опыт работы с методами машинного обучения;</li> <li>Опыт работы с моделями CV/NLP;</li> <li>Навыки программирования на Python;</li> <li>Опыт работы с библиотеками машинного обучения (TensorFlow, PyTorch, Scikit-Learn, Numpy, Pandas, Polars);</li> <li>Навыки работы с Git;</li> <li>Умение работать с большими данными и базами данных (SQL, NoSQL).</li> </ul> <p><strong>Мы предлагаем:</strong></p> <ul> <li>Работу с социально значимыми проектами в медицинской сфере.</li> <li>Причастность к созданию инновационных продуктов с использованием алгоритмов ИИ.</li> <li>Перспективу профессионального развития и самореализации в перспективном направлении MedTech.</li> <li>Удаленный формат работы либо красивый, просторный и современный<strong> </strong>офис на территории Инновационного центра Сколково.</li> <li>Стабильность и социальную поддержку от крупнейшей компании страны.</li> </ul>', 'branded_description': None, 'vacancy_constructor_template': {'id': 19984, 'name': 'Общий шаблон', 'top_picture': {'height': 560, 'width': 1540, 'path': 'https://img.hhcdn.ru/branding-pictures/3130950.jpeg', 'blurred_path': None}, 'bottom_picture': {'height': 704, 'width': 1540, 'path': 'https://img.hhcdn.ru/branding-pictures/3130964.png', 'blurred_path': None}}, 'key_skills': [{'name': 'Machine learning'}, {'name': 'TensorFlow'}, {'name': 'PyTorch'}, {'name': 'NLP'}, {'name': 'Python'}, {'name': 'SQL'}], 'accept_handicapped': False, 'accept_kids': False, 'archived': False, 'response_url': None, 'specializations': [], 'professional_roles': [{'id': '165', 'name': 'Дата-сайентист'}], 'code': None, 'hidden': False, 'quick_responses_allowed': False, 'driver_license_types': [], 'accept_incomplete_resumes': False, 'employer': {'id': '4977893', 'name': 'СберМедИИ', 'url': 'https://api.hh.ru/employers/4977893', 'alternate_url': 'https://hh.ru/employer/4977893', 'logo_urls': {'original': 'https://img.hhcdn.ru/employer-logo-original/1372877.png', '90': 'https://img.hhcdn.ru/employer-logo/7111410.png', '240': 'https://img.hhcdn.ru/employer-logo/7111411.png'}, 'vacancies_url': 'https://api.hh.ru/vacancies?employer_id=4977893', 'accredited_it_employer': True, 'trusted': True}, 'published_at': '2025-04-17T09:58:23+0300', 'created_at': '2025-04-17T09:58:23+0300', 'initial_created_at': '2025-04-17T09:58:23+0300', 'negotiations_url': None, 'suitable_resumes_url': None, 'apply_alternate_url': 'https://hh.ru/applicant/vacancy_response?vacancyId=119624423', 'has_test': False, 'test': None, 'alternate_url': 'https://hh.ru/vacancy/119624423', 'working_days': [], 'working_time_intervals': [], 'working_time_modes': [], 'accept_temporary': False, 'languages': [], 'approved': True, 'employment_form': {'id': 'FULL', 'name': 'Полная'}, 'fly_in_fly_out_duration': [], 'internship': False, 'night_shifts': False, 'work_format': [{'id': 'REMOTE', 'name': 'Удалённо'}], 'work_schedule_by_days': [{'id': 'FIVE_ON_TWO_OFF', 'name': '5/2'}], 'working_hours': [{'id': 'HOURS_8', 'name': '8\xa0часов'}], 'show_logo_in_search': True}
Сложная часть¶
Задача 1.¶
Исследуем профессию Data Scientist. Найдите как можно больше вакансий по этой профессии в Москве. Учтите, что имеет смысл искать также по другим ключевым словам, например, аналитик данных
.
В полученную выборку некоторые вакансии могли попасть несколько раз. Удалите дубликаты.
Загрузите подробное описание каждой вакансии и создайте удобную для дальнейших действий таблицу данных.
Полученную таблицу необходимо сохранить в формате xlsx и отправить боту вместе с решением.
Вопрос 1. Сколько сейчас доступно вакансий по вашему запросу?
Вопрос 2. Какие навыки чаще всего встречаются в вакансиях по данной специальности?
Для этого найдите соответствующее поле в данных из полного описания вакансий, проанализируйте его и составьте список навыков и количество упоминаний каждого. Визуализируйте полученную информацию по топ-15 навыков.
Вопрос 3. Какую зарплату готовы платить работодатели? Соберите некоторым образом статистику и постройте гистограмму.
При работе с данными о заработной плате обратите внимание на валюту и gross/net. Постоянно подгружать курс валюты не требуется, достаточно фиксировать какой-то один более менее актуальный.
Вопрос 4. Какой формат работы предлагается (в офисе / удаленно / ...)?
Проведите аналогичный анализ для наиболее привлекательной для вас профессии в любом регионе. Если это Data Scientist, то для анализа выберите другую. В данном пункте спокойно можно сделать копипасту кода.
Не забывайте про выводы.
Факультативная часть¶
Задача 2.¶
Для одной из рассмотренных ранее профессий исследуйте, в каком районе Москвы данная вакансия пользуется наибольшим и наименьшим спросом. Не забудьте про визуализацию, в частности, постройте распределений вакансий на карте.
Напоминание. Работа в некоторой степени творческая, поэтому конкретные детали решения в основном остаются на усмотрение автора решения.
Какие выводы можно сделать из построенных графиков?
Задача 3.¶
1. Проверьте, во скольких вакансиях среди выгруженных указана зарплата?
На основе описания вакансий с известной зарплатой попробуйте оценить ожидаемую зарплату для всех остальных вакансий. Для этого на основе текстового описания вакансии можно построить эмбеддинги (любые подходящие нейросетевые или просто one-hot кодирование). По этим эмбеддингам обучите некоторую модель предсказывать зарплату.
Не забудьте по общий пайплайн ML-моделей, а также про применимость различных методов.
2. Выполните кластеризацию вакансий, используя построенные эмбеддинги. Визуализируйте результаты и проинтерпретируйте кластеры.
Не забывайте про выводы.