Содержание¶

I. Постановка задачи и план исследования
II. Формирование базы данных
III. Предобработка и очистка данных
IV. Исследовательский анализ данных (EDA)

  • Блок 1. Общий обзор рынка
    1. Топ работодателей: ТОП-10 после консолидации брендов
    2. Востребованность опыта: Какие грейды наиболее востребованы
    3. География поиска: Сравнение крупнейших хабов
  • Блок 2. Деньги и прозрачность
    4. Прозрачность условий: Где чаще указывают зарплату
    5. География зарплат: Расчет средней и медианной зарплаты
    6. Карьерный рост: Зависимость дохода от опыта
  • Блок 3. Условия работы
    7. Формат и гибкость: Типы занятости и графики
    8. Связь между опытом и доступностью удаленной занятости
  • Блок 4. Навыки и бонус
    9. Профиль компетенций: Сравнение частоты Hard Skills
    10. [Bonus Insight] Тайминг публикаций: Активность по дням недели

Итоговое резюме

I. Постановка задачи и план исследования¶

В рамках данного проекта проведено исследование рынка вакансий по направлениям «Аналитик данных» и «Системный аналитик» (включая англоязычные аналоги запросов).

Цель исследования: Сформировать объективный обзор рынка для сравнения условий оплаты труда, графиков работы и требований к ключевым навыкам в разрезе двух специальностей.


Ход работы:¶

  1. Сбор данных: Самостоятельное извлечение актуальных вакансий напрямую через API HH.ru.
  2. Обработка: Очистка сырых данных, работа с дубликатами и создание структурированной базы для анализа.
  3. Анализ и визуализация:
    • Общий обзор рынка: объем вакансий и распределение по ролям.
    • Деньги и прозрачность: анализ предлагаемых зарплат и частоты их указания.
    • Условия работы: сравнение графиков и типов занятости.
    • Навыки: выявление технологического ядра и популярных связок инструментов.

↑ Вернуться к содержанию¶

II. Формирование базы данных¶

На данном этапе будет реализован сбор данных с платформы HH.ru.

Технические особенности этапа:

  • Инструмент: Написан собственный парсер для взаимодействия с публичным API HeadHunter.
  • Область поиска: Вакансии по ключевым запросам «Аналитик данных» и «Системный аналитик» (включая англоязычные вариации).
  • Методология: Ввиду лимитов API на выдачу (до 2000 записей на запрос), логика сбора настроена таким образом, чтобы получить максимально широкую и актуальную выборку.

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

Подготовка окружения и импорт библиотек¶

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

In [1]:
import requests # Отправка запросов к API HH.ru
import pandas as pd # Основной инструмент для работы с таблицами
import numpy as np # Математические операции и работа с массивами
import re # Регулярные выражения для поиска навыков в тексте
import sys # Для доступа к системным параметрам Python
import matplotlib.pyplot as plt # Базовая визуализация
import seaborn as sns # Улучшенные статистические графики
from IPython.display import display, HTML, clear_output # Для очистки вывода в ячейке
import time # Контроль пауз между запросами к API

# Настройка отображения: выводим все столбцы без ограничений
pd.set_option('display.max_columns', None)
In [2]:
# Проверка рабоспособности и доступа к API и просмотр списка столбцов и выбора необходимых для анализа.

url = 'https://api.hh.ru/vacancies'

# Используем мои данные для авторизации
# Формат: Название_приложения/Версия (email)

user_email = 'fatkulinedik@yandex.ru'
app_name = 'MyDataAnalysisProject'

headers = {
    'User-Agent': f'{app_name}/1.0 ({user_email})'
}

params = {
    'text': 'Data Analyst', # Возьмем один запрос
    'area': 1,      # Москва
    'per_page': 1  # Возьмем 1 вакансию
}

# Делаем запрос
res = requests.get(url, params=params, headers=headers)
if res.status_code == 200:
    df_test = pd.DataFrame(res.json()['items'])
    print("Список всех доступных колонок:")
    print(list(df_test.columns))
else:
    print(f"Статус: {res.status_code}")
    print(f"Текст ошибки: {res.text}")
Список всех доступных колонок:
['id', 'premium', 'name', 'department', 'has_test', 'response_letter_required', 'area', 'salary', 'salary_range', 'type', 'address', 'response_url', 'sort_point_distance', 'published_at', 'created_at', 'archived', 'apply_alternate_url', 'branding', 'show_logo_in_search', 'show_contacts', 'insider_interview', 'url', 'alternate_url', 'relations', 'employer', 'snippet', 'contacts', 'schedule', 'working_days', 'working_time_intervals', 'working_time_modes', 'accept_temporary', 'fly_in_fly_out_duration', 'work_format', 'working_hours', 'work_schedule_by_days', 'accept_labor_contract', 'civil_law_contracts', 'night_shifts', 'professional_roles', 'accept_incomplete_resumes', 'experience', 'employment', 'employment_form', 'internship', 'adv_response_url', 'is_adv_vacancy', 'adv_context']

Выбор целевых атрибутов и логика сбора данных¶

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

Основные поля вакансии:

  • id — уникальный номер вакансии (необходим для исключения дубликатов).
  • name — название вакансии.
  • salary — данные о доходе (будут разобраны на составляющие: «от», «до» и «валюта»).
  • area — фактический город размещения.
  • published_at — дата публикации (для оценки актуальности рынка).
  • employer — название компании.
  • experience — требуемый опыт работы (Нет опыта, от 1 до 3 лет и т.д.).
  • schedule — график работы (удаленка, полный день и др.).
  • employment — тип занятости (полная, проектная и т.д.).
  • snippet (поле requirement) — краткое описание требований, из которого будет извлекаться стек технологий (SQL, Python и т.д.).

Дополнительные расчетные колонки:

  • target_city — город, по которому ведется поиск.
  • target_query — поисковый запрос (data analyst, аналитик данных и т.д.).

Зачем нужен target_city? > Хотя в данных есть area, я создаю дополнительный столбец для фильтрации. HH.ru часто подмешивает в московскую выдачу вакансии из ближайшего пригорода (Одинцово, Химки). Это поле поможет оставить только релевантные данные.

Аналогичная логика для target_query. По запросу Аналитик данных HH.ru выдает похожие названия, как Аналитик хранилищ, Аналитик баз данных и т.д.. Этот столбец поможет в дальнейшем удобнее сгруппировать направление, которое мы будем искать.

А также в дальнейшем по ходу предобработки будут созданы дополнительные столбцы для Навыков и Рассчета ЗП.


План реализации¶

Для исследования выбрана тройка наиболее активных IT-хабов: Москва, Санкт-Петербург и Казань.

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

  1. аналитик данных
  2. data analyst
  3. системный аналитик
  4. system analyst

На этом этапе мы собираем сырой массив данных, который в дальнейшем будет очищен от лишних записей и приведен к финальному виду.

In [3]:
# Вводные данные
cities = {'Москва': 1, 'Санкт-Петербург': 2, 'Казань': 88}
queries = ['аналитик данных', 'data analyst', 'системный аналитик', 'system analyst']

all_vacancies = [] # Сюда будем складывать всё найденное

print("Начинаю сбор данных...")

for city_name, city_id in cities.items():
    for query in queries:
        print(f"Парсим: {city_name} | Запрос: {query}")
        
        # Листаем страницы (от 0 до 19, т.к. максимум 2000 вакансий)
        for page in range(20):
            params = {
                'text': query,
                'area': city_id,
                'per_page': 100, # Максимум вакансий на страницу
                'page': page
            }
            
            res = requests.get(url, params=params, headers=headers)
            
            if res.status_code != 200:
                print(f"Ошибка на странице {page}: {res.status_code}")
                break
            
            items = res.json().get('items', [])
            
            # Если вакансии на странице закончились — выходим из цикла страниц
            if not items:
                break
            
            # Добавляем в каждую вакансию метки города и запроса (для будущего анализа)
            for item in items:
                item['target_city'] = city_name
                item['target_query'] = query
                all_vacancies.append(item)
            
            # Маленькая пауза, чтобы HH не забанил (0.2 сек)
            time.sleep(0.2)
            
            # Очищаем вывод, чтобы не спамить в Jupyter, и пишем статус
            clear_output(wait=True)
            print(f"Собрано всего: {len(all_vacancies)} вакансий...")

# Превращаем список в одну таблицу
df_full = pd.DataFrame(all_vacancies)
print("Таблица создана.")
display(df_full.head())
Собрано всего: 8764 вакансий...
Таблица создана.
id premium name department has_test response_letter_required area salary salary_range type address response_url sort_point_distance published_at created_at archived apply_alternate_url show_contacts insider_interview url alternate_url relations employer snippet contacts schedule working_days working_time_intervals working_time_modes accept_temporary fly_in_fly_out_duration work_format working_hours work_schedule_by_days accept_labor_contract civil_law_contracts night_shifts professional_roles accept_incomplete_resumes experience employment employment_form internship adv_response_url is_adv_vacancy adv_context target_city target_query branding show_logo_in_search brand_snippet
0 131549046 False Аналитик данных None False False {'id': '1', 'name': 'Москва', 'url': 'https://... None None {'id': 'open', 'name': 'Открытая'} {'city': 'Москва', 'street': 'Раменский бульва... None None 2026-04-12T16:22:43+0300 2026-04-12T16:22:43+0300 False https://hh.ru/applicant/vacancy_response?vacan... False None https://api.hh.ru/vacancies/131549046?host=hh.ru https://hh.ru/vacancy/131549046 [] {'id': '11941855', 'name': 'ИКС БИО ТЕХНОЛОДЖИ... {'requirement': '...к <highlighttext>аналитике... None {'id': 'fullDay', 'name': 'Полный день'} [] [] [] True [] [{'id': 'HYBRID', 'name': 'Гибрид'}] [{'id': 'HOURS_8', 'name': '8 часов'}, {'id': ... [{'id': 'FIVE_ON_TWO_OFF', 'name': '5/2'}] True [{'id': 'SELF_EMPLOYED', 'name': 'с самозаняты... False [{'id': '156', 'name': 'BI-аналитик, аналитик ... False {'id': 'between3And6', 'name': 'От 3 до 6 лет'} {'id': 'full', 'name': 'Полная занятость'} {'id': 'FULL', 'name': 'Полная'} False None False None Москва аналитик данных NaN NaN NaN
1 131889061 False Аналитик баз данных None False False {'id': '1', 'name': 'Москва', 'url': 'https://... {'from': 80000, 'to': 120000, 'currency': 'RUR... {'from': 80000, 'to': 120000, 'currency': 'RUR... {'id': 'open', 'name': 'Открытая'} {'city': 'Москва', 'street': 'Хорошёвское шосс... None None 2026-04-12T20:11:58+0300 2026-04-12T20:11:58+0300 False https://hh.ru/applicant/vacancy_response?vacan... False None https://api.hh.ru/vacancies/131889061?host=hh.ru https://hh.ru/vacancy/131889061 [] {'id': '1156064', 'name': 'Фортис Технологии',... {'requirement': 'Навыки работы с СУБД: построе... None {'id': 'remote', 'name': 'Удаленная работа'} [] [] [] False [] [{'id': 'ON_SITE', 'name': 'На месте работодат... [{'id': 'HOURS_8', 'name': '8 часов'}] [{'id': 'FIVE_ON_TWO_OFF', 'name': '5/2'}] False [] False [{'id': '156', 'name': 'BI-аналитик, аналитик ... False {'id': 'between3And6', 'name': 'От 3 до 6 лет'} {'id': 'full', 'name': 'Полная занятость'} {'id': 'FULL', 'name': 'Полная'} False None False None Москва аналитик данных NaN NaN NaN
2 131978902 False Аналитик данных None False False {'id': '1', 'name': 'Москва', 'url': 'https://... {'from': 120000, 'to': 150000, 'currency': 'RU... {'from': 120000, 'to': 150000, 'currency': 'RU... {'id': 'open', 'name': 'Открытая'} {'city': 'Москва', 'street': 'Спартаковский пе... None None 2026-04-12T10:27:56+0300 2026-04-12T10:27:56+0300 False https://hh.ru/applicant/vacancy_response?vacan... True None https://api.hh.ru/vacancies/131978902?host=hh.ru https://hh.ru/vacancy/131978902 [] {'id': '2630490', 'name': 'Медиабюро Статус пр... {'requirement': 'Высшее образование. Аналитиче... None {'id': 'fullDay', 'name': 'Полный день'} [] [] [] False [] [{'id': 'ON_SITE', 'name': 'На месте работодат... [{'id': 'HOURS_8', 'name': '8 часов'}] [{'id': 'FIVE_ON_TWO_OFF', 'name': '5/2'}] True [] False [{'id': '54', 'name': 'Координатор отдела прод... False {'id': 'between1And3', 'name': 'От 1 года до 3... {'id': 'full', 'name': 'Полная занятость'} {'id': 'FULL', 'name': 'Полная'} False None False None Москва аналитик данных NaN NaN NaN
3 131232738 False Аналитик данных None False False {'id': '1', 'name': 'Москва', 'url': 'https://... None None {'id': 'open', 'name': 'Открытая'} None None None 2026-04-12T07:11:54+0300 2026-04-12T07:11:54+0300 False https://hh.ru/applicant/vacancy_response?vacan... False None https://api.hh.ru/vacancies/131232738?host=hh.ru https://hh.ru/vacancy/131232738 [] {'id': '588914', 'name': 'Aviasales.ru', 'url'... {'requirement': 'Опыт в дата или продуктовой <... None {'id': 'remote', 'name': 'Удаленная работа'} [] [] [] False [] [{'id': 'REMOTE', 'name': 'Удалённо'}] [{'id': 'HOURS_9', 'name': '9 часов'}] [{'id': 'FIVE_ON_TWO_OFF', 'name': '5/2'}] False [] False [{'id': '156', 'name': 'BI-аналитик, аналитик ... False {'id': 'between3And6', 'name': 'От 3 до 6 лет'} {'id': 'full', 'name': 'Полная занятость'} {'id': 'FULL', 'name': 'Полная'} False None False None Москва аналитик данных NaN NaN NaN
4 131868762 False Аналитик платформы данных None False False {'id': '1', 'name': 'Москва', 'url': 'https://... None None {'id': 'open', 'name': 'Открытая'} {'city': 'Москва', 'street': 'Летниковская ули... None None 2026-04-12T12:47:45+0300 2026-04-12T12:47:45+0300 False https://hh.ru/applicant/vacancy_response?vacan... True None https://api.hh.ru/vacancies/131868762?host=hh.ru https://hh.ru/vacancy/131868762 [] {'id': '11660779', 'name': 'Цифровой аудит', '... {'requirement': 'Опыт работы в хранилищах <hig... None {'id': 'fullDay', 'name': 'Полный день'} [] [] [] False [] [{'id': 'HYBRID', 'name': 'Гибрид'}] [{'id': 'HOURS_8', 'name': '8 часов'}] [{'id': 'FIVE_ON_TWO_OFF', 'name': '5/2'}] True [] False [{'id': '156', 'name': 'BI-аналитик, аналитик ... False {'id': 'between3And6', 'name': 'От 3 до 6 лет'} {'id': 'full', 'name': 'Полная занятость'} {'id': 'FULL', 'name': 'Полная'} False None False None Москва аналитик данных NaN NaN NaN

На текущем этапе сформирован первичный датасет df_full с помощью скрипта для парсинга вакансий. Данный массив является сырым и требует этапа предобработки перед началом глубокого анализа.

↑ Вернуться к содержанию

III. Предобработка и очистка данных¶

Ключевые проблемы исходных данных:

  • Наличие дубликатов: Так как поиск велся по пересекающимся запросам на русском и английском языках, в общую выдачу попали идентичные вакансии.
  • Сложная структура: Данные в столбце salary и других полях требуют распаковки и приведения типов.
  • Избыточность: Массив содержит большое количество технических колонок, не влияющих на результат исследования.

Первоочередная задача: Очистка датасета от дубликатов по уникальному идентификатору вакансии (id).

In [4]:
# Проверим сколько сейчас вакансий.

print(f"Строки до удаления дубликатов: {len(df_full)}")

# Удаляем дубликаты по ID вакансии
df_full = df_full.drop_duplicates(subset='id')

# Смотрим, сколько стало
print(f"Стало строк после удаления дубликатов: {len(df_full)}")
Строки до удаления дубликатов: 8764
Стало строк после удаления дубликатов: 6177

Оптимизация структуры данных¶

После удаления повторов необходимо привести таблицу к рабочему виду. На этом этапе мы:

  • Фильтруем признаки: Оставляем только те колонки, которые необходимы для анализа.
  • Распаковываем вложенные структуры: Преобразуем данные из JSON-подобных словарей (словарь в ячейке) в плоский и понятный вид, удобный для манипуляций в Pandas.
In [5]:
# Оставляем только нужные колонки из списка
columns_to_keep = [
    'id', 'name', 'area', 'salary', 'experience', 
    'employment', 'schedule', 'employer', 'snippet', 'published_at', 
    'target_city', 'target_query'
]
# Создаем новый фрейм данных
df = df_full[columns_to_keep].copy()

# Функция для очистки словарей (если данных нет, пишем None)
def get_name(x):
    return x.get('name') if isinstance(x, dict) else None

# Распаковываем простые колонки
df['city'] = df['area'].apply(get_name)
df['company'] = df['employer'].apply(get_name)
df['exp_level'] = df['experience'].apply(get_name)
df['job_type'] = df['employment'].apply(get_name)
df['work_schedule'] = df['schedule'].apply(get_name)

# Распаковываем зарплату (от, до и валюта)
df['salary_from'] = df['salary'].apply(lambda x: x.get('from') if isinstance(x, dict) else None)
df['salary_to'] = df['salary'].apply(lambda x: x.get('to') if isinstance(x, dict) else None)
df['salary_currency'] = df['salary'].apply(lambda x: x.get('currency') if isinstance(x, dict) else None)

# Вытаскиваем требования из snippet
df['requirements'] = df['snippet'].apply(lambda x: x.get('requirement') if isinstance(x, dict) else None)

# Удаляем старые «грязные» колонки, которые мы уже распаковали
df.drop(columns=['area', 'employer', 'experience', 'employment', 'salary', 'snippet', 'schedule'], inplace=True)

display(df.head())
id name published_at target_city target_query city company exp_level job_type work_schedule salary_from salary_to salary_currency requirements
0 131549046 Аналитик данных 2026-04-12T16:22:43+0300 Москва аналитик данных Москва ИКС БИО ТЕХНОЛОДЖИ От 3 до 6 лет Полная занятость Полный день NaN NaN None ...к <highlighttext>аналитике</highlighttext> ...
1 131889061 Аналитик баз данных 2026-04-12T20:11:58+0300 Москва аналитик данных Москва Фортис Технологии От 3 до 6 лет Полная занятость Удаленная работа 80000.0 120000.0 RUR Навыки работы с СУБД: построение SQL-запросов....
2 131978902 Аналитик данных 2026-04-12T10:27:56+0300 Москва аналитик данных Москва Медиабюро Статус презенс От 1 года до 3 лет Полная занятость Полный день 120000.0 150000.0 RUR Высшее образование. Аналитический склад ума, в...
3 131232738 Аналитик данных 2026-04-12T07:11:54+0300 Москва аналитик данных Москва Aviasales.ru От 3 до 6 лет Полная занятость Удаленная работа NaN NaN None Опыт в дата или продуктовой <highlighttext>ана...
4 131868762 Аналитик платформы данных 2026-04-12T12:47:45+0300 Москва аналитик данных Москва Цифровой аудит От 3 до 6 лет Полная занятость Полный день NaN NaN None Опыт работы в хранилищах <highlighttext>данных...

Текстовая очистка и удаление HTML-тегов¶

На текущем этапе мы сформировали датафрейм с целевыми атрибутами. Однако текстовое поле requirement (требования), полученное из API, содержит избыточную разметку: HTML-теги (например, <highlight>, <li> и др.).

Необходимо очистить текстовые данные от тегов для корректного поиска ключевых навыков и проведения дальнейшего контент-анализа. Для этого мы применим библиотеку re (регулярные выражения).

In [6]:
# Функция для удаления HTML-тегов (<highlight> и прочие)
def clean_tags(text):
    if isinstance(text, str):
        # Удаляем теги, заменяя их на пустоту
        return re.sub(r'<.*?>', '', text)
    return text

# Применяем очистку к нашей новой колонке requirements
df['requirements'] = df['requirements'].apply(clean_tags)

# Посмотрим на результат: название вакансии и очищенное требование
display(df[['name', 'requirements']].head())
name requirements
0 Аналитик данных ...к аналитике и исследованию данных. Продвину...
1 Аналитик баз данных Навыки работы с СУБД: построение SQL-запросов....
2 Аналитик данных Высшее образование. Аналитический склад ума, в...
3 Аналитик данных Опыт в дата или продуктовой аналитике от трех ...
4 Аналитик платформы данных Опыт работы в хранилищах данных (DWH/КХД). Уве...

Приведение типов: Формат даты¶

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

In [7]:
# Переводим строку в формат даты
df['published_at'] = pd.to_datetime(df['published_at']).dt.date

# Переименуем колонку для наглядности
df.rename(columns={'published_at': 'published_date'}, inplace=True)

# Проверим, что получилось
display(df.head())
id name published_date target_city target_query city company exp_level job_type work_schedule salary_from salary_to salary_currency requirements
0 131549046 Аналитик данных 2026-04-12 Москва аналитик данных Москва ИКС БИО ТЕХНОЛОДЖИ От 3 до 6 лет Полная занятость Полный день NaN NaN None ...к аналитике и исследованию данных. Продвину...
1 131889061 Аналитик баз данных 2026-04-12 Москва аналитик данных Москва Фортис Технологии От 3 до 6 лет Полная занятость Удаленная работа 80000.0 120000.0 RUR Навыки работы с СУБД: построение SQL-запросов....
2 131978902 Аналитик данных 2026-04-12 Москва аналитик данных Москва Медиабюро Статус презенс От 1 года до 3 лет Полная занятость Полный день 120000.0 150000.0 RUR Высшее образование. Аналитический склад ума, в...
3 131232738 Аналитик данных 2026-04-12 Москва аналитик данных Москва Aviasales.ru От 3 до 6 лет Полная занятость Удаленная работа NaN NaN None Опыт в дата или продуктовой аналитике от трех ...
4 131868762 Аналитик платформы данных 2026-04-12 Москва аналитик данных Москва Цифровой аудит От 3 до 6 лет Полная занятость Полный день NaN NaN None Опыт работы в хранилищах данных (DWH/КХД). Уве...

Инсайт для соискателя: > Перевод данных в формат published_date — это не только техническая очистка, но и база для прикладного вывода. Анализ дня недели публикации поможет определить пиковые периоды активности работодателей, чтобы соискатель мог эффективнее мониторить свежие вакансии.

Анализ полноты данных: Поле Salary¶

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

In [8]:
# Считаем количество пропусков по валюте
total_vacancies = len(df)
no_salary_count = df['salary_currency'].isna().sum()
has_salary_count = total_vacancies - no_salary_count

# Считаем проценты
no_salary_pct = (no_salary_count / total_vacancies) * 100

print(f"Общее количество вакансий: {total_vacancies}")
print(f"Вакансий с указанной ЗП: {has_salary_count}")
print(f"Вакансий БЕЗ указания ЗП: {no_salary_count} ({no_salary_pct:.2f}%)")

# Маленькая проверка: есть ли строки, где указана валюта, но нет цифр?
check_logic = df[df['salary_currency'].notna() & df['salary_from'].isna() & df['salary_to'].isna()]
if len(check_logic) > 0:
    print(f"Внимание: есть {len(check_logic)} вакансий с валютой, но без сумм.")
Общее количество вакансий: 6177
Вакансий с указанной ЗП: 2137
Вакансий БЕЗ указания ЗП: 4040 (65.40%)

Вывод по результатам проверки:¶

Доля вакансий без указания дохода составляет 65,3%. Это существенный объем, который требует осознанного подхода к дальнейшей обработке:

  • Сохранение репрезентативности: Я не удаляю эти строки на текущем этапе. Они содержат критически важную информацию о работодателях и требованиях к кандидатам (стеке технологий), которая необходима для полноты исследования.
  • Стратегия анализа ЗП: Поскольку заполнение такого количества пропусков (импутация) может привести к смещению оценок, расчеты среднего и медианного дохода будут проводиться исключительно по подвыборке, где данные о зарплате указаны явно.
  • Дальнейшие шаги: В блоке анализа доходов мы сфокусируемся на «прозрачной» части рынка, а также проверим, в каких городах работодатели чаще склонны открыто публиковать финансовые условия.

Группировка поисковых запросов¶

Для корректной визуализации необходимо объединить схожие по смыслу запросы в единые категории. Поиск велся на двух языках (RU/EN), что создало избыточность в названиях позиций.

Что мы делаем:

  1. Создаем колонку vacancy_category.
  2. Объединяем запросы («Data Analyst» + «Аналитик данных») и («System Analyst» + «Системный аналитик») в две соответствующие группы.

Это позволит сохранить историю поиска в target_query, но при этом получить чистые и репрезентативные графики без дублирования ролей.

In [9]:
# Создаем словарь для маппинга (замены)
# Ищем ключевые слова в target_query и объединяем их
def categorize_vacancy(query):
    query = str(query).lower()
    if 'system' in query or 'системный' in query:
        return 'Системный аналитик'
    if 'data' in query or 'данных' in query:
        return 'Аналитик данных'
    return 'Другое'

# Применяем функцию
df['vacancy_category'] = df['target_query'].apply(categorize_vacancy)

# Проверим, как распределились новые категории
print(df['vacancy_category'].value_counts())
vacancy_category
Аналитик данных       4183
Системный аналитик    1994
Name: count, dtype: int64
In [10]:
display(df.head())
id name published_date target_city target_query city company exp_level job_type work_schedule salary_from salary_to salary_currency requirements vacancy_category
0 131549046 Аналитик данных 2026-04-12 Москва аналитик данных Москва ИКС БИО ТЕХНОЛОДЖИ От 3 до 6 лет Полная занятость Полный день NaN NaN None ...к аналитике и исследованию данных. Продвину... Аналитик данных
1 131889061 Аналитик баз данных 2026-04-12 Москва аналитик данных Москва Фортис Технологии От 3 до 6 лет Полная занятость Удаленная работа 80000.0 120000.0 RUR Навыки работы с СУБД: построение SQL-запросов.... Аналитик данных
2 131978902 Аналитик данных 2026-04-12 Москва аналитик данных Москва Медиабюро Статус презенс От 1 года до 3 лет Полная занятость Полный день 120000.0 150000.0 RUR Высшее образование. Аналитический склад ума, в... Аналитик данных
3 131232738 Аналитик данных 2026-04-12 Москва аналитик данных Москва Aviasales.ru От 3 до 6 лет Полная занятость Удаленная работа NaN NaN None Опыт в дата или продуктовой аналитике от трех ... Аналитик данных
4 131868762 Аналитик платформы данных 2026-04-12 Москва аналитик данных Москва Цифровой аудит От 3 до 6 лет Полная занятость Полный день NaN NaN None Опыт работы в хранилищах данных (DWH/КХД). Уве... Аналитик данных

Обработка названий компаний (Brand Consolidation)¶

При анализе рынка труда важно учитывать структуру крупных холдингов. Часто дочерние ИТ-подразделения (например, SberTech, OzonTech) фигурируют как отдельные юридические лица, что дробит статистику и искажает реальный вес бренда-работодателя.

План действий:

  1. Выявление топ-15 компаний по объему вакансий.
  2. Проверка выявленных лидеров на наличие дочерних структур в общем списке.
  3. Создание функции для унификации названий (приведение «дочек» к материнскому бренду).

Это позволит получить объективный рейтинг крупнейших нанимателей в направлении Data & System Analytics.

In [11]:
print(df['company'].value_counts().head(15))
company
СБЕР                                                   202
Ozon                                                    89
ИЦ АЙ-ТЕКО                                              61
Т-Банк                                                  50
МТС                                                     48
Банк ВТБ (ПАО)                                          46
RWB (Wildberries & Russ)                                42
Альфа-Банк                                              42
Алабуга, ОЭЗ ППТ                                        36
VK                                                      36
Совкомбанк                                              32
АО «ОТП Банк» (JSC «OTP Bank»)                          32
Центральный банк Российской Федерации (Банк России)     29
Яндекс                                                  28
ГКУ Инфогород                                           28
Name: count, dtype: int64

В лидеры по количеству открытых позиций вошли крупнейшие игроки банковского сектора, ритейла и ИТ-индустрии: СБЕР, Ozon, Wildberries, Альфа-Банк, МТС, Т-Банк и VK и другие.

Для корректного подсчета я реализую функцию поиска по ключевым словам. Это позволит объединить записи дочерних компаний под единым зонтичным брендом там, где это уместно.

In [12]:
def find_companies(df, search_word):
    # Приводим поисковое слово к нижнему регистру
    search_word = search_word.lower()
    
    # Ищем совпадения в столбце 'company'
    matches = df[df['company'].str.lower().str.contains(search_word, na=False)]
    
    if matches.empty:
        print(f"По запросу '{search_word}' ничего не найдено.")
    else:
        # Группируем и считаем
        result = matches['company'].value_counts().reset_index()
        result.columns = ['Название в базе', 'Количество вакансий']
        
        print(f"--- Результаты поиска для '{search_word}' ---")
        display(result)

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

In [13]:
find_companies(df, 'sber')
print()
find_companies(df, 'сбер')
print()
find_companies(df, 'Russ')
print()
find_companies(df, 'альфа')
print()
find_companies(df, 'мтс')
print()
find_companies(df, 'втб')
print()
find_companies(df, 'яндекс')
print()
find_companies(df, 'газпром')
print()
find_companies(df, 'магнит')
print()
find_companies(df, 'ростелеком')
print()
--- Результаты поиска для 'sber' ---
Название в базе Количество вакансий
0 SberTech 3
--- Результаты поиска для 'сбер' ---
Название в базе Количество вакансий
0 СБЕР 202
1 СберЛизинг 15
2 СБЕРКОРУС 7
3 СберЗдоровье 6
4 СберСпасибо 3
5 Страховая компания Сбербанк страхование 3
6 Сбер Бизнес Софт 2
7 АЙСБЕРГ 2
8 СК Сбербанк страхование жизни 1
9 СберСити 1
10 СберМаркетинг 1
--- Результаты поиска для 'russ' ---
Название в базе Количество вакансий
0 RWB (Wildberries & Russ) 42
1 Russ 4
2 Cornerstone Russia 2
3 Cheil Russia 1
4 INVAIDER RUSSIA 1
--- Результаты поиска для 'альфа' ---
Название в базе Количество вакансий
0 Альфа-Банк 42
1 Альфа-Деньги 4
2 Альфа Капитал 2
3 1й Нагатинский (СЗ Альфа) 1
4 Альфа-Форекс 1
5 АльфаСпа Косметик 1
6 АльфаСтрахование 1
--- Результаты поиска для 'мтс' ---
Название в базе Количество вакансий
0 МТС 48
1 МТС Банк 16
--- Результаты поиска для 'втб' ---
Название в базе Количество вакансий
0 Банк ВТБ (ПАО) 46
1 ВТБ Факторинг 2
--- Результаты поиска для 'яндекс' ---
Название в базе Количество вакансий
0 Яндекс 28
1 Яндекс Крауд 3
2 Яндекс Команда для бизнеса 3
3 Яндекс.Еда 2
--- Результаты поиска для 'газпром' ---
Название в базе Количество вакансий
0 Газпромбанк 17
1 Газпром нефть 14
2 Газпромнефть-Региональные продажи 9
3 Газпром ЦПС 8
4 Газпром сеть АГЗС 3
5 Газпром информ 3
6 Газпромбанк Лизинг 2
7 Электронная торговая площадка Газпромбанка 2
8 Газпромнефть-Снабжение 2
9 Газпромбанк Автолизинг 1
10 «Газпром энергохолдинг» 1
11 Газпром бурение 1
12 Газпром ТЕХ 1
13 Газпром питание 1
14 Филиал УСЗ ПАО Газпром 1
--- Результаты поиска для 'магнит' ---
Название в базе Количество вакансий
0 МАГНИТ, Розничная сеть 21
1 Магнит OMNI 3
--- Результаты поиска для 'ростелеком' ---
Название в базе Количество вакансий
0 Ростелеком 18
1 Ростелеком Информационные Технологии 7

Результаты поиска дочерних структур¶

Поиск подтвердил наличие значительного количества аффилированных юрлиц, особенно у таких гигантов, как Сбер, Газпром и Альфа-Банк.

Следующий шаг: Выполнение скрипта для унификации названий. Все найденные дочерние структуры будут приведены к единому наименованию материнского бренда для обеспечения точности при визуализации рыночных долей.

In [14]:
# Словарь компаний и их синонимов для поиска
company_mapping = {
    'СБЕР': [
        'СБЕР', 'СберЛизинг', 'Страховая компания Сбербанк страхование', 
        'СберЗдоровье', 'СБЕРКОРУС', 'СберСпасибо', 'Сбер Бизнес Софт', 
        'СберМобайл', 'СК Сбербанк страхование жизни', 'СберПраво', 
        'Корпоративный университет Сбербанка', 'Сбербанк АСТ', 'SberTech'
    ],
    'RWB (Wildberries & Russ)': [
        'RWB (Wildberries & Russ)', 'Russ'
    ],
    'Альфа Банк': [
        'Альфа-Банк', 'Альфа-Деньги', 'Альфа Капитал', 
        'АльфаСтрахование-Жизнь', 'Альфа-Форекс', 'Альфа-Лизинг'
    ],
    'МТС': [
        'МТС', 'МТС Банк'
    ],
    'ВТБ': [
        'Банк ВТБ (ПАО)', 'ВТБ Лизинг', 
        'ВТБ Факторинг', 'НПФ ВТБ Пенсионный Фонд'
    ],
    'Яндекс': [
        'Яндекс', 'Яндекс Крауд', 'Яндекс.Еда'
    ],
    'ГАЗПРОМ': [
        'Газпромбанк', 'Газпром нефть', 'Газпромнефть-Региональные продажи', 
        'Газпром ЦПС', 'Газпромнефть-Снабжение', 'Газпром сеть АГЗС', 
        'Электронная торговая площадка Газпромбанка', 'Газпромбанк Автолизинг', 
        'Газпромбанк Лизинг', 'Газпром экспертиза', 'Газпром информ', 'Газпром питание', 
        'Филиал УСЗ ПАО Газпром', 'Газпром ТЕХ', '«Газпром энергохолдинг»', 'Газпромтранс', 'Газпром энергосбыт'
    ],
    'МАГНИТ': [
        'МАГНИТ, Розничная сеть', 'Магнит OMNI'
    ],
    'Ростелеком': [
        'Ростелеком', 'Ростелеком Информационные Технологии'
    ]
}

# Функция для объединения названий
def clean_company_names(company_name):
    for final_name, synonyms in company_mapping.items():
        # Проверяем, есть ли текущее название в списке синонимов
        if company_name in synonyms:
            return final_name
    return company_name # Если совпадений нет, оставляем как было

# Применяем очистку к столбцу 'company'
df['company'] = df['company'].apply(clean_company_names)

# Проверяем результат: теперь СБЕР и другие компании должен суммироваться в одну строку
print("Обновленный Топ-15 компаний:")
print(df['company'].value_counts().head(15))
Обновленный Топ-15 компаний:
company
СБЕР                                                   242
Ozon                                                    89
ГАЗПРОМ                                                 65
МТС                                                     64
ИЦ АЙ-ТЕКО                                              61
Т-Банк                                                  50
Альфа Банк                                              49
ВТБ                                                     48
RWB (Wildberries & Russ)                                46
Алабуга, ОЭЗ ППТ                                        36
VK                                                      36
Яндекс                                                  33
АО «ОТП Банк» (JSC «OTP Bank»)                          32
Совкомбанк                                              32
Центральный банк Российской Федерации (Банк России)     29
Name: count, dtype: int64
Результаты унификации брендов¶

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

Ключевые изменения в Топ-15:

  • Лидерство СБЕРа: После консолидации отрыв компании стал еще более значительным (+52 вакансии за счет дочерних подразделений).
  • Прорыв ГАЗПРОМа: Компания переместилась с последних мест на 3-е место. Это подтверждает, что значительная часть найма ведется через дочерние ИТ-структуры.
  • Банковский сектор и Телеком: Альфа-Банк, МТС и ВТБ также упрочили свои позиции в рейтинге, что позволило получить более объективную картину спроса на специалистов.

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

Контент-анализ Hard Skills: Методология «Золотого стандарта»¶

Особенность данных: При работе с API HeadHunter поле requirements содержит усеченный фрагмент описания вакансии. Прямое извлечение всех слов из такой выборки может привести к искажению статистики.

Для получения объективной картины применен метод целевого контент-анализа. Вместо обработки «шумного» текста, реализован поиск по списку из 14 ключевых индустриальных стандартов с использованием регулярных выражений.

Преимущества подхода:

  • Фильтрация шума: Фокус исключительно на Hard Skills, исключая общие формулировки («ответственность», «командный игрок»).
  • Учет вариативности: Использование регулярных выражений (например, python|питон или postgres|sql) позволяет корректно считывать навыки при разном написании.
  • Релевантность 2026: Выбранный список навыков является «золотым стандартом» для позиций Data и System Analyst, что позволяет провести качественное сравнение направлений.

Список целевых навыков:

  • Языки и библиотеки: SQL, Python, Pandas.
  • Инструменты BI: Excel, Power BI, Tableau, Superset.
  • Data Engineering: Spark, Airflow, ETL, ClickHouse.
  • Дополнительно: A/B тесты, Английский язык, Git.

Прежде чем переходить к массовому извлечению, реализуем вспомогательный скрипт для экспресс-проверки: убедимся, что выбранные ключевые слова корректно распознаются в тексте вакансий.

In [15]:
def find_requirements(df, search_word):
    # Приводим поисковое слово к нижнему регистру
    search_word = search_word.lower()
    
    # Ищем совпадения в столбце 'requirements'
    matches = df[df['requirements'].str.lower().str.contains(search_word, na=False)]
    
    if matches.empty:
        print(f"По запросу '{search_word}' ничего не найдено.")
    else:
        # Группируем и считаем
        result = matches['requirements'].value_counts().reset_index()
        result.columns = ['Название в базе', 'Количество вакансий']
        
        print(f"--- Результаты поиска для '{search_word}' ---")
        display(result)
In [16]:
find_requirements(df, 'sql')
find_requirements(df, 'Airflow')
find_requirements(df, 'английский')
find_requirements(df, 'python')
find_requirements(df, 'spark')
find_requirements(df, 'tableau')
find_requirements(df, 'power bi')
find_requirements(df, 'English')
find_requirements(df, 'pandas')
find_requirements(df, 'etl')
find_requirements(df, 'a/b')
find_requirements(df, 'Git')
find_requirements(df, 'ClickHouse')
find_requirements(df, 'superset')
--- Результаты поиска для 'sql' ---
Название в базе Количество вакансий
0 Уверенное владение Python (pandas, numpy) для ... 3
1 SQL на высоком уровне: уверенное владение окон... 3
2 Опыт работы с данными от 2 лет (дата-инженер, ... 3
3 Опыт работы с микросервисной архитектурой. Опы... 3
4 ...моделирования данных: Dimensional Modeling,... 2
... ... ...
658 Свободное владение SQL: уверенное написание сл... 1
659 Высшее образование. Опыт работы на позиции Ана... 1
660 Углублённые знания Power BI (Power Query, M, D... 1
661 Опыт взаимодействия с промышленными СУБД: Orac... 1
662 Работа с базами данных (SQL) — умение оптимизи... 1

663 rows × 2 columns

--- Результаты поиска для 'airflow' ---
Название в базе Количество вакансий
0 Опыт работы аналитиком данных более 3 лет. Опы... 2
1 Опыт работы с AirFlow, Git. Знание pyhon для а... 1
2 ...ключи, индексы и планы запросов. Опыт разра... 1
3 Опыт проектирования масштабируемых решений для... 1
4 Опыт в системном анализе ETL, проектировании п... 1
5 MLOps: работа с TensorFlow в продакшене, DVC, ... 1
6 Имеете опыт работы с инструментами аналитиков ... 1
7 3+ лет опыта в роли Data Engineer / BI Enginee... 1
8 Высшее образование. Продвинутые навыки в разра... 1
9 Высшее техническое или финансово-математическо... 1
10 Опыт работы с orchestration-инструментами (Air... 1
11 Уверенный опыт работы Data Engineer от 2–3 лет... 1
12 ...данных – AirFlow, Nifi, SSIS или другие. Же... 1
13 Опыт работы с BI-системами и визуализацией дан... 1
14 Продвинутые навыки работы с SQL: написание сло... 1
15 Опыт работы с Airflow или аналогичными оркестр... 1
16 ...системами (SuperSet, Reedash и аналогичными... 1
17 Spark, Hadoop, Airflow – плюс. Способность фор... 1
18 ETL/ELT-инструменты, dbt или аналог. Оркестрац... 1
19 Python, pyspark, sql (clickhouse, vertica, pos... 1
20 Умеете осуществлять оркестрацию с помощью airf... 1
21 MS SQL. Apache Airflow. Apache Kafka. Gitlab. ... 1
22 Инженерия данных: понимание архитектуры (Airfl... 1
23 Знание Git (ветвление, merge requests, code re... 1
24 От 2-х лет опыта работы Data Engineer. Хорошее... 1
--- Результаты поиска для 'английский' ---
Название в базе Количество вакансий
0 Опыт работы продуктовым менеджером в B2B (FinT... 2
1 Опыт работы системным/бизнес/full-stack аналит... 2
2 Навыки работы с СУБД: построение SQL-запросов.... 1
3 Уверенное владение SQL, опыт написания bash-ск... 1
4 Умение понимать код на С. Владение языками нот... 1
5 Базовых навыков работы с данными (SQL). Англий... 1
6 Свободный английский язык (устный и письменный... 1
7 Техническое образование. Опыт работы разработч... 1
8 Имеет законченное высшее образование. Имеет ур... 1
9 Хорошо разбираешься в ставках. Любовь и знание... 1
10 ...аналитики от 3 лет. Английский язык (чтение... 1
11 Английский язык - Upper-intermediate +. Опыт в... 1
12 Опыт управления распределенными командами от 7... 1
13 Сильная экспертиза в SEO и performance-маркети... 1
14 Аналитический склад ума: умение работать с бол... 1
15 Сильные аналитические навыки и опыт оптимизаци... 1
16 Высшее образование, в приоритете международный... 1
17 Английский язык Upper-Intermediate B2+ | финал... 1
18 Английский язык на уровне чтения технической д... 1
19 Опыт работы бизнес ассистентом / project-менед... 1
20 Английский язык — письменный и устный. Опыт ра... 1
21 Опыт в поддержке от полугода. Английский язык ... 1
22 Опыт работы с аналитикой и дашбордами (Google ... 1
23 Обязательно – только очная форма обучения. Обя... 1
24 Знает английский язык на уровне С1. Имеет опыт... 1
25 Опыт работы с Airflow или аналогичными оркестр... 1
26 Опыт работы с Python и SQL для анализа данных ... 1
27 Опыт работы системным аналитиком от полутора л... 1
28 Высшее образование (техническое), желательно М... 1
29 Владеет английский языком не ниже Upper-interm... 1
30 Понимание в области разработки концептуальных ... 1
31 Уверенный уровень Microsoft Office, особенно P... 1
32 Понимание принципов работы реляционных (Postgr... 1
33 ...компьютеры, операционные системы) - обязате... 1
34 Понимание бизнес-процессов в сегментах жилой и... 1
35 Умение доводить проекты от идеи до аналитики р... 1
36 Английский не ниже B1 — для глобальных инсайто... 1
37 Опыт 2–4 года в аналитике (Marketplace / Mobil... 1
38 Продвинутый Excel и PowerPoint, умение обрабат... 1
39 Свободный английский язык. Являются плюсом: - ... 1
40 Выпускник с математическим/техническим образов... 1
41 Выпускник с математическим/техническим образов... 1
42 6+ лет коммерческой разработки на Python. Увер... 1
43 Опыт работы с аналитикой и дашбордами (Google ... 1
44 Английский язык: B1+ (переписка, контент, созв... 1
--- Результаты поиска для 'python' ---
Название в базе Количество вакансий
0 Уверенное владение Python (pandas, numpy) для ... 3
1 ...SQL запросов для самостоятельного сбора и а... 2
2 Навыки работы с базами данных и знание основны... 2
3 SQL: уверенные базовые знания, готовность писа... 2
4 Уверенно работаете с Python и SQL, знаете осно... 2
... ... ...
306 Опыт работы бизнес-аналитиком / продуктовым ан... 1
307 Опыт работы с Python (Pandas, NumPy, Jupyter) ... 1
308 Высшее математическое, техническое образование... 1
309 Опыт алгоритмической работы в Python. Навыки р... 1
310 Работа в офисе в Казани с 9 до 18 ч. Уверенное... 1

311 rows × 2 columns

--- Результаты поиска для 'spark' ---
Название в базе Количество вакансий
0 Опыт работы от 3 лет в роли дата аналитика. Ув... 1
1 Свободное владение SQL, Python (Pandas, NumPy)... 1
2 Опыт разработки ETL-процессов. Опыт работы с B... 1
3 Хорошее знание Pytorch. Опыт написания SQL зап... 1
4 Уверенное знание Python. Опыт работы с Big Dat... 1
5 ...backtesting), PnL-анализ, построение индика... 1
6 Высшее техническое или финансово-математическо... 1
7 Опыт работы с Kubernetes от 2 лет. Опыт с Big ... 1
8 Продвинутый уровень SQL и опыт оптимизации зап... 1
9 Знание PySpark, SQL. Опыт модельной аналитики ... 1
10 Опыт работы с Python, включая стандартный data... 1
11 Опыт работы с Big Data инструментами (Hadoop, ... 1
12 Имеете опыт работы с инструментами аналитиков ... 1
13 Опыт работы с современным стеком: ClickHouse, ... 1
14 Опыт с экосистемой Сбера: Platform V (SDP Hado... 1
15 Опыт работы с современным стеком: ClickHouse, ... 1
16 Понимание основ облачных технологий и технолог... 1
17 хорошо владеете Python, pySpark и SQL. – поним... 1
18 Разработка: Уверенное знание Python (asyncio, ... 1
19 Опыт в системном анализе ETL, проектировании п... 1
20 Опыт работы с экосистемой Apache Hadoop (Hive,... 1
21 Понимание архитектуры DWH и принципов построен... 1
22 Опыт работы в системной аналитике, опыт разраб... 1
23 Глубокое знание SQL. Знание HDFS и Apache Spar... 1
24 Имеете уверенный опыт программирования на Pyth... 1
25 SQL, Hadoop, Hive, Spark. Опыт от 2-х лет (Обя... 1
26 ...NumPy, Dbeaver, Spark, BI. Продвинутые знан... 1
27 ...и управления качеством данных. Знание техно... 1
28 Будет плюсом опыт работы со Spark и умение соз... 1
29 ...инструментами: python на уровне аналитика д... 1
30 Опыт работы с базами данных в качестве разрабо... 1
31 Python, pyspark, sql (clickhouse, vertica, pos... 1
32 Имеете опыт работы с: Python; SQL, PySpark. Ув... 1
33 Опыт работы в финансовом секторе от 3-х лет бу... 1
34 Опыт работы с grpc запросами. Опыт тестировани... 1
35 Spark, Hadoop, Airflow – плюс. Способность фор... 1
36 Имеете опыт работы аналитиком данных от 2х лет... 1
37 Опыт работы c Py/Spark. Знание понятий и конце... 1
38 моделирования, концепции статистической провер... 1
39 ...pyspark, Altair; извлечение\загрузка данных... 1
40 Технический стек: Python (библиотеки дата-анал... 1
41 ...запросов, построение и поддержка структур д... 1
42 Базовое знание SQL, Python на уровне, достаточ... 1
43 Имеете глубокие знания SQL, Python, PySpark. И... 1
44 Знание Hadoop, Data Warehouse, Data Lake. Опыт... 1
45 Data & Infra: - Уверенное владение SQL (сложны... 1
46 Data & Infra: - Уверенное владение SQL (сложны... 1
47 Kubernetes: опыт администрирования от 2 лет. B... 1
--- Результаты поиска для 'tableau' ---
Название в базе Количество вакансий
0 ...Power BI, Looker, Tableau). Умение формулир... 3
1 Хорошее знание SQL (join, оконные функции, вло... 2
2 Глубокое знание SQL и умение эффективно работа... 2
3 Продвинутый уровень SQL. Регулярное использова... 2
4 Более 3 лет работал в качестве аналитика. Имее... 1
5 Написание SQL-запросов для выгрузки и проверки... 1
6 Уверенные навыки работы с Excel (обработка дан... 1
7 Уверенное владение Python для задач продакшн-а... 1
8 Опыт построения сложной BI отчетности (в любой... 1
9 Опыт построения продуктовой аналитики. - Умени... 1
10 Уверенное владение SQL, Python и современными ... 1
11 Опыт работы с системами визуализации (Power BI... 1
12 Высокий уровень SQL (в частности, оптимизация ... 1
13 Опыт построения Dashboards в BI системах : Qli... 1
14 Инструменты Business Intelligence: уверенный о... 1
15 В зависимости от продукта и масштабов исследов... 1
16 Опыт работы с системами аналитики (Power BI, T... 1
17 ...визуализации данных (например, Tableau, Pow... 1
18 Опыт построения отчетов и аналитики: Looker St... 1
19 Специалиста, для которого преимуществом будет ... 1
20 Техническая грамотность: базовое понимание код... 1
21 Высшее образование: маркетинг, социология, ста... 1
22 Опыт работы с BI‑инструментами (Power BI/Table... 1
23 Уверенное знание SQL (сложные запросы, оконные... 1
24 Опыт запуска аналитики с нуля. Опыт в маркетин... 1
25 Навыки работы с инструментами анализа данных (... 1
26 Понимание рекламных кабинетов/аналитики. BI-ин... 1
27 Продвинутый уровень SQL: Базовый уровень Pytho... 1
28 Продвинутое владение Excel/Google Sheets (форм... 1
29 Знание Python на уровне обработки и анализа да... 1
30 ...24, AmoCRM) через API. Работа с другими BI-... 1
31 Power BI / Tableau — средний уровень. 1С — баз... 1
32 Опыт работы аналитиком данных от 3 лет. Опыт р... 1
33 SQL: Vertica, ClickHouse. Будет плюсом: Опыт р... 1
34 Навык построения дашбордов и визуализации данн... 1
35 Опыт построения отчетности в Apache Superset н... 1
36 Опыт работы с системами бизнес-аналитики (Powe... 1
37 Уверенный SQL. Уверенный Python для аналитики ... 1
38 Продвинутое владение Excel/Google Sheets (форм... 1
39 Навыки работы с Excel (сводные таблицы, формул... 1
40 Знание инструментов аналитики (Excel, Power BI... 1
41 Умение работать с большими массивами данных, в... 1
42 Желательно, чтобы вы знали инструменты анализа... 1
43 Используете Python для выгрузки и обработки бо... 1
44 Баумана, НИУ ВШЭ, МГУ. Опыт визуализации данны... 1
45 Понимание основных алгоритмов и структур данны... 1
46 Опыт работы BI-аналитиком от 3 лет. Глубокое з... 1
47 Понимание ключевых метрик Ozon: Конверсия. CTR... 1
48 Навыки работы с BI‑инструментами (Power BI, Ta... 1
49 Power BI или Tableau для визуализации данных -... 1
50 Опыт работы с большими данными и сложными стру... 1
51 Опыт работы с Power BI, Tableau, Data Studio д... 1
52 Опыт работы с инструментами визуализации и ана... 1
--- Результаты поиска для 'power bi' ---
Название в базе Количество вакансий
0 ...Power BI, Looker, Tableau). Умение формулир... 3
1 Опыт разработки отчетов и дашбордов в Power BI... 2
2 Владение инструментами аналитики (например, Po... 2
3 ...большими массивами данных. Владение Excel (... 2
4 Продвинутый уровень SQL. Регулярное использова... 2
... ... ...
110 Уверенное построение факторных и финансовых мо... 1
111 Требования: Практический опыт с Power BI / DAX... 1
112 Навыки работы с Excel (сводные таблицы, формул... 1
113 Опыт работы с большими массивами данных. Навык... 1
114 Навыки работы с аналитикой: Я.Метрика, GA4, Po... 1

115 rows × 2 columns

--- Результаты поиска для 'english' ---
Название в базе Количество вакансий
0 English language proficiency level C1+. Quickl... 2
1 Bachelor’s degree required. Bachelor’s degree ... 1
2 Fluent English: Ты свободно питчишь, шутишь и ... 1
3 Опыт от 1 года: в операциях, аналитике, консал... 1
4 AI-Native Development Skills. Communication Sk... 1
--- Результаты поиска для 'pandas' ---
Название в базе Количество вакансий
0 Уверенное владение Python (pandas, numpy) для ... 3
1 ...структур данных для аналитики). Опыт примен... 2
2 SQL: уверенные базовые знания, готовность писа... 2
3 Владение методами анализа данных с использован... 2
4 Образование в профильной или смежной области. ... 2
... ... ...
58 Высшее образование. Владение Python для анализ... 1
59 Опыт работы в сфере аналитики данных от 3 лет.... 1
60 ...запросов, построение и поддержка структур д... 1
61 ...в аналитике от 1 года. Опыт работы на Pytho... 1
62 Обязательно: Python (pandas, numpy, scikit-lea... 1

63 rows × 2 columns

--- Результаты поиска для 'etl' ---
Название в базе Количество вакансий
0 Не менее 2 лет работы в качестве Data Engineer... 2
1 Опыт в доработках хранилищ данных: исследовани... 2
2 Опыт работы от 2х лет в роли Data Engineer, Da... 2
3 Опыт участия в разработке систем обработки дан... 1
4 ...Data modeling, ETL/ELT, lakehouse архитекту... 1
... ... ...
70 Опыт работы с Airflow или аналогичными оркестр... 1
71 Продвинутые навыки автоматизации (ETL), постро... 1
72 Моделирование данных: OLAP-куб, DWH. Python - ... 1
73 Занимаешься data analytics / data engineering ... 1
74 Знание ClickHouse. -Знание Python. -Опыт получ... 1

75 rows × 2 columns

--- Результаты поиска для 'a/b' ---
Название в базе Количество вакансий
0 Мы ищем продуктового/data аналитика с большим ... 3
1 Навыки количественного и качественного исследо... 2
2 Практический опыт работы с аналитикой: SQL-зап... 2
3 Глубокие знание математической статистики и пр... 2
4 Уверенный Excel (сводные таблицы, статистика, ... 2
5 Свободное владение SQL, Python (Pandas, NumPy)... 1
6 Опыт: Успешные кейсы по оптимизации продукта н... 1
7 Готовность развиваться в этом направлении. Dat... 1
8 Опыт работы с мобильной аналитикой (Amplitude,... 1
9 Образование: Высшее техническое или в сфере IT... 1
10 ...аналитике. Понимание продуктовой аналитики:... 1
11 Умение работать с продуктовой аналитикой и при... 1
12 Будет плюсом. Опыт работы в SaaS / стартапах. ... 1
13 Data‑driven подход: умение задавать требования... 1
14 Понимание CI/CD, сборок, автоматизации, пайпла... 1
15 Навыки проведения A/B тестирования. Знание сис... 1
16 Опыт проведения A/B-тестирования (креативы, ау... 1
17 Сильный data-driven подход и опыт A/B-тестиров... 1
18 Data-driven мышление (эксперименты, A/B-тесты,... 1
19 Понимание и использование метрик (CTR, CR, охв... 1
20 Имеете опыт проведения A/B тестов. Понимаете M... 1
21 Понимание маркетинговых воронок и навыки A/B-т... 1
22 +Базовые знания в области AI/ML, LLM и аналити... 1
23 Опыт работы в аналитике (Data/Business Analyst... 1
24 A/B тесты, генерация и проверка гипотез - быть... 1
25 SQL: уверенное владение. Python: статпакеты / ... 1
26 Опыт в продуктовой аналитике от трёх лет. Прик... 1
27 Уверенный SQL (PostgreSQL / ClickHouse / Terad... 1
28 Опыт работы в роли аналитика клиентских данных... 1
29 Наши ожидания: — Уверенное владение Excel. — О... 1
30 Отличные знания математической статистики: Нав... 1
31 Понимание принципов A/B тестирования. Умение р... 1
32 Опыт работы в продуктовой аналитике в одной из... 1
33 Анализировать качество модели и её влияние на ... 1
34 Опыт исследовательской деятельности в роли Dat... 1
--- Результаты поиска для 'git' ---
Название в базе Количество вакансий
0 Опыт в digital marketing от 2 лет. Понимание, ... 2
1 Опыт в телемаркетинге или продажах от 6 месяце... 2
2 Опыт написания SQL запросов для чтения и модиф... 2
3 Умение разобраться в любой lineage-цепочке дан... 2
4 Знание основ digital-маркетинга и умение работ... 2
... ... ...
103 Сильные аналитические навыки и работа с digita... 1
104 Опыт работы с технологиями по направлению Java... 1
105 Релевантный опыт работы на стороне рекламного ... 1
106 Разбираешься в digital и медийных инструментах... 1
107 Высшее профильное образование (маркетинг, рекл... 1

108 rows × 2 columns

--- Результаты поиска для 'clickhouse' ---
Название в базе Количество вакансий
0 SQL на высоком уровне: уверенное владение окон... 3
1 Уверенное владение Python (pandas, numpy) для ... 3
2 Опыт в продуктовой аналитике от 1 года. Сильны... 2
3 ...источников данных. Глубокие знания SQL, опы... 2
4 Опыт ClickHouse и/или PostgreSQL: проектирован... 1
5 Опыт работы с современным стеком: ClickHouse, ... 1
6 Опыт построения Data Lakehouse-архитектуры с н... 1
7 Опыт работы Data Engineer / DWH Engineer / ETL... 1
8 Уверенное знание Python. Опыт работы с Big Dat... 1
9 Коммерческий опыт работы с ClickHouse от 1 год... 1
10 Высшее образование. Опыт в продуктовой/data-ан... 1
11 Опыт работы с Clickhouse или оптимизации запро... 1
12 Опыт работы с Clickhouse. Опыт в интернет-марк... 1
13 Опыт проектирования хранилищ данных. Знание ос... 1
14 Опыт работы с современным стеком: ClickHouse, ... 1
15 Ориентируетесь в предметных областях: управлен... 1
16 ClickHouse. Greenplum. Trino. Python. Data Lak... 1
17 Хорошая математическая база (ТеорВер, МатСтат)... 1
18 Технологии и стек: - Микросервисы, REST, Kafka... 1
19 Опыт работы в роли системного аналитика от 3 л... 1
20 Опыт разработки высоконагруженных web-проектов... 1
21 ...и/или ClickHouse (желательно). Будет плюсом... 1
22 Опыт работы BI-аналитиком, data-аналитиком или... 1
23 Опыт работы в качестве системного аналитика не... 1
24 Глубокое знание SQL, опыт работы с СУБД Clickh... 1
25 Глубокие знания ClickHouse или других OLAP-сис... 1
26 ...Sql базами данных (у нас MySQL и своё key-v... 1
27 От 2-х лет опыта работы Data Engineer. Хорошее... 1
28 Знание ClickHouse. -Знание Python. -Опыт получ... 1
29 Писать бизнес-логику на Golang или Python. Вла... 1
30 Опыт коммерческой работы дата-аналитиком, инже... 1
31 Опыт работы с ClickHouse. Понимание основ DWH ... 1
32 Опыт работы с большими объёмами данных и сложн... 1
33 Уверенные технические знания: профи в SQL и оп... 1
34 ...с большими массивами данных (обязательное з... 1
35 Знание SQL на уровне не ниже мидл (опыт работы... 1
36 Знание SQL (Postrgre, ClickHouse), Python. Опы... 1
37 Имеете опыт работы аналитиком данных от 3 лет.... 1
38 Знание Git (ветвление, merge requests, code re... 1
39 Опыт работы в качестве системного аналитика от... 1
40 Глубокое понимание принципов структуры баз дан... 1
41 Уверенно работаешь с большими объемами данных ... 1
42 Опыт работы с базами данных: PostgreSQL, Click... 1
43 Уверенное владение SQL (желательно ClickHouse)... 1
44 Опыт работы в продуктовой аналитике в одной из... 1
45 SQL: Vertica, ClickHouse. Будет плюсом: Опыт р... 1
46 Python, pyspark, sql (clickhouse, vertica, pos... 1
47 Уверенно пишет на SQL (ClickHouse — наш основн... 1
48 ETL/ELT-инструменты, dbt или аналог. Оркестрац... 1
49 Отличное знание SQL. Опыт работы с Clickhouse.... 1
50 Высшее образование (математическое, экономичес... 1
51 ...схемы стриминга данных Я.Метрики и/или Data... 1
52 Составление ER-диаграмм и схем миграции данных... 1
53 Уверенный SQL (PostgreSQL / ClickHouse / Terad... 1
54 Работа с большими данными: понимаем особенност... 1
55 Баумана, НИУ ВШЭ, МГУ. Опыт визуализации данны... 1
56 Kafka/RabbitMQ/SQS. Опыт с хранилищами и анали... 1
--- Результаты поиска для 'superset' ---
Название в базе Количество вакансий
0 Хорошее знание SQL (join, оконные функции, вло... 2
1 Знание Python на продвинутом уровне (pyodbs, p... 1
2 Высокий уровень SQL (в частности, оптимизация ... 1
3 ...аналитиком от 2-х лет. ️ Отличное знание SQ... 1
4 Опыт визуализации данных с BI инструментами - ... 1
5 Хорошая математическая база (ТеорВер, МатСтат)... 1
6 Уверенное владение Python для задач продакшн-а... 1
7 Опыт построения сложной BI отчетности (в любой... 1
8 Опыт построения продуктовой аналитики. - Умени... 1
9 Опыт работы с системами визуализации (Power BI... 1
10 Опыт построения Dashboards в BI системах : Qli... 1
11 Инструменты Business Intelligence: уверенный о... 1
12 Более 3 лет работал в качестве аналитика. Имее... 1
13 ...Data Strategy & Governance) и процессов по ... 1
14 Навыки визуализации информации в BI-системах (... 1
15 Уверенное знание SQL (сложные запросы, оконные... 1
16 Понимание рекламных кабинетов/аналитики. BI-ин... 1
17 Имеет опыт разработки дашбордов и отчетов в од... 1
18 ...визуализации данных (например, Tableau, Pow... 1
19 Свободное владение SQL (сложные запросы, оконн... 1
20 Опыт работы в клиентской аналитике от 2 лет. S... 1
21 3 года опыта работы в BI-аналитике: умение стр... 1
22 Высокий уровень владения SQL, Python. Опыт раб... 1
23 Имеете опыт работы с Superset. Владеете на выс... 1
24 Высшее образование (математика, ИТ, экономика ... 1
25 Опыт работы аналитиком данных от 3 лет. Опыт р... 1
26 Доступный стек: PL SQL, Greenplum, Python; Fin... 1
27 SQL: Vertica, ClickHouse. Будет плюсом: Опыт р... 1
28 Опыт построения отчетности в Apache Superset н... 1
29 Знание Python на продвинутом уровне (pyodbs, p... 1
30 Навыки SQL, Python, SuperSet. Знание математич... 1
31 ...системами (SuperSet, Reedash и аналогичными... 1
32 Высшее образование (математическое, экономичес... 1
33 Apache Superset (или BI аналоги). Понимание А/... 1
34 Поддержка и развитие базы знаний. Есть опыт ра... 1
35 Высшее образование в области аналитики, матема... 1
36 Понимание и использование ML-алгоритмов для ан... 1
37 Понимание основных алгоритмов и структур данны... 1
38 Навыки по формированию отчётности "под ключ" в... 1

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

Следующий шаг: Создание функции extract_skills для автоматизированного подсчета частоты упоминаний каждого навыка. Результаты этой функции станут основой для сравнительного анализа требований.

In [17]:
# Словарь навыков и их синонимов для поиска. Использую регулярные выражения, т.к. ищу определенные слова в кучке текста
skills_map = {
    'SQL': r'sql|postgres|oracle|msql',
    'Python': r'python|питон',
    'Spark': r'spark',
    'Pandas': r'pandas|пандиас',
    'Excel': r'excel|эксель|vlookup|сводные таблицы',
    'Power BI': r'power\s?bi|pbi|повер би',
    'Tableau': r'tableau|табло',
    'Superset': r'superset|суперсет',
    'Airflow': r'airflow|эйрфлоу',
    'ETL': r'etl|extraction',
    'ClickHouse': r'clickhouse|кликхаус',
    'A/B тесты': r'a/b|ab-test|эксперимент|тестирование',
    'Английский язык': r'english|английский|eng|upper|intermediate',
    'Git': r'git|gitlab|github'
}

# Функция для создания колонок навыков
def extract_skills(df, skills_dict):
    # Копируем датафрейм, чтобы не испортить оригинал
    processed_df = df.copy()
    
    # Заполняем пустые требования пустой строкой, чтобы поиск не сломался
    processed_df['requirements'] = processed_df['requirements'].fillna('').str.lower()
    
    for skill, pattern in skills_dict.items():
        # Создаем новую колонку: 1 если паттерн найден, иначе 0
        column_name = f'skill_{skill}'
        processed_df[column_name] = processed_df['requirements'].str.contains(pattern, na=False).astype(int)
        
    return processed_df

# Применяем функцию
df = extract_skills(df, skills_map)

# Проверим результат: создадим генератор списка по столбцам начинающимся с skill_ и суммы по новым колонкам:
skill_columns = [col for col in df.columns if col.startswith('skill_')]
print("Количество упоминаний каждого навыка:")
print(df[skill_columns].sum().sort_values(ascending=False))
Количество упоминаний каждого навыка:
skill_SQL                727
skill_Excel              606
skill_Python             333
skill_Power BI           132
skill_Git                114
skill_Английский язык    108
skill_ETL                 79
skill_Pandas              69
skill_ClickHouse          63
skill_A/B тесты           60
skill_Tableau             58
skill_Spark               48
skill_Superset            40
skill_Airflow             26
dtype: int64
In [18]:
display(df.head(2))
id name published_date target_city target_query city company exp_level job_type work_schedule salary_from salary_to salary_currency requirements vacancy_category skill_SQL skill_Python skill_Spark skill_Pandas skill_Excel skill_Power BI skill_Tableau skill_Superset skill_Airflow skill_ETL skill_ClickHouse skill_A/B тесты skill_Английский язык skill_Git
0 131549046 Аналитик данных 2026-04-12 Москва аналитик данных Москва ИКС БИО ТЕХНОЛОДЖИ От 3 до 6 лет Полная занятость Полный день NaN NaN None ...к аналитике и исследованию данных. продвину... Аналитик данных 1 1 0 0 1 0 0 0 0 0 0 0 0 0
1 131889061 Аналитик баз данных 2026-04-12 Москва аналитик данных Москва Фортис Технологии От 3 до 6 лет Полная занятость Удаленная работа 80000.0 120000.0 RUR навыки работы с субд: построение sql-запросов.... Аналитик данных 1 0 0 0 1 0 0 0 0 0 0 0 1 0
In [19]:
# Сохранение очищенных данных
df.to_csv('hh_vacancies_data.csv', index=False, encoding='utf-8-sig')

# Сохранение неочищенных данных
df_full.to_csv('hh_vacancies_raw.csv', index=False, encoding='utf-8-sig')
In [20]:
# Посмотрим вес двух файлов
import os
print(f"Размер файла: {os.path.getsize('hh_vacancies_data.csv') / 1024:.2f} КБ")
print(f"Размер файла: {os.path.getsize('hh_vacancies_raw.csv') / 1024:.2f} КБ")
Размер файла: 3520.58 КБ
Размер файла: 17665.22 КБ
Итоги этапа предобработки¶
  • Трансформация структур: Выполнена фильтрация избыточных признаков и распаковка вложенных JSON-структур в плоские столбцы.
  • Текстовый процессинг:
    • Проведена очистка описаний вакансий от HTML-тегов и нерелевантных символов.
    • Реализован контент-анализ для извлечения ключевых Hard Skills.
  • Нормализация данных:
    • Стандартизированы названия компаний и сгруппированы поисковые запросы.
    • Оптимизировано представление дат для корректного временного анализа.
  • Работа с финансовыми показателями: Проведен аудит полноты данных по столбцам заработной платы и подготовлена база для расчета средних значений.

В результате очистки, объем обрабатываемой информации был существенно оптимизирован.

  • Размер исходных данных: ~17.3 МБ
  • Размер очищенного датасета: ~3.4 МБ

Результат: Вес файла уменьшился в 5 раз при сохранении всей ключевой информации для анализа. Это не только ускоряет работу скриптов визуализации, но и подготавливает данные к эффективному хранению.

Данные очищены и готовы к этапу исследовательского анализа.

↑ Вернуться к содержанию

IV. Исследовательский анализ данных (EDA)¶

На данном этапе мы переходим к поиску закономерностей в очищенных данных. Анализ разделен на 4 тематических блока, которые позволят сформировать комплексное представление о рынке аналитики в 2026 году.

Блок 1. Общий обзор рынка¶

  1. Топ работодателей: Какие компании лидируют по количеству вакансий (ТОП-10 после консолидации брендов).
  2. Востребованность опыта: Какие грейды специалистов наиболее востребованы на текущем рынке.
  3. География поиска: Сравнение активности рынка в крупнейших хабах: Москва, Санкт-Петербург и Казань.

Блок 2. Деньги и прозрачность¶

  1. Прозрачность условий: В каких городах работодатели чаще готовы указывать зарплату, а где рынок остается закрытым.
  2. География зарплат: Расчет средней и медианной зарплаты в разрезе городов.
  3. Карьерный рост: Зависимость дохода от опыта.

Блок 3. Условия работы¶

  1. Формат и гибкость: Комплексное исследование типов занятости и графиков работы. Проверка связи между опытом специалиста и доступностью удаленного формата работы.

Блок 4. Навыки и бонус¶

  1. Профиль компетенций: Сравнение частоты упоминания ключевых Hard Skills (SQL, Python, BI и др.) в требованиях компаний.
  2. [Bonus Insight] Тайминг публикаций: Анализ активности работодателей по дням недели для определения лучшего времени для мониторинга вакансий.

↑ Вернуться к содержанию

Блок 1. Общий обзор рынка¶

1. Топ работодателей: Какие компании лидируют по количеству вакансий (ТОП-10 после консолидации брендов).¶

In [21]:
# Считаем топ-10 компаний по общему количеству вакансий
top_10_companies = df['company'].value_counts().head(10).index
plot_data = df[df['company'].isin(top_10_companies)]

# Строим график
plt.figure(figsize=(12, 7))
sns.countplot(
    data=plot_data, 
    y='company', 
    hue='vacancy_category',
    order=top_10_companies,
    palette='viridis'
)

plt.title('Топ-10 компаний по количеству вакансий (Аналитики данных vs Системный аналитик)', fontsize=15)
plt.xlabel('Количество открытых вакансий')
plt.ylabel('Компания')
plt.legend(title='Направление')
plt.tight_layout()
plt.show()

# Создаем сводную таблицу для статистики под графиком
top_10_stats = plot_data.pivot_table(
    index='company', 
    columns='vacancy_category', 
    aggfunc='size', 
    fill_value=0
)

# Добавляем колонку с общим итогом и сортируем
top_10_stats['Всего'] = top_10_stats.sum(axis=1)
top_10_stats = top_10_stats.loc[top_10_companies] # Сохраняем порядок как на графике

# Переименовываем заголовки для красоты
top_10_stats.index.name = 'Компания'
top_10_stats.columns.name = 'Направление'

display(top_10_stats)
No description has been provided for this image
Направление Аналитик данных Системный аналитик Всего
Компания
СБЕР 190 52 242
Ozon 78 11 89
ГАЗПРОМ 51 14 65
МТС 45 19 64
ИЦ АЙ-ТЕКО 26 35 61
Т-Банк 43 7 50
Альфа Банк 40 9 49
ВТБ 43 5 48
RWB (Wildberries & Russ) 34 12 46
Алабуга, ОЭЗ ППТ 29 7 36
Вывод¶

Анализ распределения вакансий по крупнейшим работодателям позволяет сделать несколько ключевых выводов:

  • Абсолютное лидерство: СБЕР занимает первое место с колоссальным отрываом — 251 вакансия. Это почти в 3 раза превышает показатели ближайшего преследователя (Ozon — 90 вакансий) и подтверждает статус компании как главного ИТ-нанимателя.
  • Диспропорция направлений: Во всех компаниях из Топ-10 спрос на Аналитиков данных существенно выше, чем на Системных аналитиков. Наиболее яркий контраст виден у Газпрома, Альфа-Банка и ВТБ, где вакансий для Data-специалистов в 4–7 раз больше.
  • Специфика бизнеса: Единственная компания в списке с относительно равным балансом ролей — ИЦ АЙ-ТЕКО (32 против 25). Это подчеркивает специфику системного интегратора, где проектирование систем (Системный аналитик) так же важно, как и работа с данными (Аналитик данных).

Среди компаний с наибольшим числом вакансий можно выделить СБЕР, Ozon, Газпром. Специалисты по аналитике данных на текущий момент являются более востребованными среди лидеров рынка. Однако системная аналитика сохраняет стабильный спрос в компаниях с выраженной проектной и технической разработкой.

2. Востребованность опыта: Какие грейды специалистов наиболее востребованы на текущем рынке.¶

In [22]:
# Указываем правильный порядок грейдов для логики графика
plt.figure(figsize=(12, 7))
exp_order = ['Нет опыта', 'От 1 года до 3 лет', 'От 3 до 6 лет', 'Более 6 лет']

sns.countplot(
    data=df, 
    x='exp_level', 
    hue='vacancy_category', 
    order=exp_order,
    palette='magma'
)

plt.title('Распределение вакансий по требуемому опыту (Грейды)', fontsize=15)
plt.xlabel('Опыт работы')
plt.ylabel('Количество вакансий')
plt.legend(title='Направление')
plt.show()

# Создаем сводную таблицу для статистики под графиком
# Считаем количество по грейдам
exp_stats = df['exp_level'].value_counts().reindex(exp_order).reset_index()
exp_stats.columns = ['Опыт работы', 'Количество вакансий']

# Добавляем расчет доли в %
total = len(df)
exp_stats['Доля'] = exp_stats['Количество вакансий'].apply(lambda x: f"{(x/total)*100:.1f}%")

print(f"Всего в базе: {total} вакансий")
display(exp_stats.set_index('Опыт работы'))
No description has been provided for this image
Всего в базе: 6177 вакансий
Количество вакансий Доля
Опыт работы
Нет опыта 277 4.5%
От 1 года до 3 лет 2427 39.3%
От 3 до 6 лет 2978 48.2%
Более 6 лет 495 8.0%
Вывод¶

Распределение вакансий наглядно иллюстрирует высокий порог входа в профессию в 2026 году:

  • Доминирование Middle/Senior сегмента: Более половины всех предложений (56.1%) ориентированы на специалистов с опытом работы от 3 лет и выше. Рынок сфокусирован на поиске сотрудников для решения сложных задач, требующих накопленной экспертизы.
  • Дефицит позиций для старта: Новичкам без опыта доступно всего 4.3% вакансий. Это подтверждает жесткий тренд: работодатели стремятся нанимать готовых специалистов, способных выдавать результат с первых дней работы, и минимизируют затраты на обучение «с нуля».
  • Основная точка входа: Категория «От 1 года до 3 лет» (39.7%) является наиболее активной зоной найма. Это показывает, что наличие даже минимального коммерческого опыта радикально (почти в 10 раз) повышает востребованность кандидата.

Рынок труда крайне требователен к практическим навыкам. Для успешного трудоустройства начинающему аналитику необходимо компенсировать отсутствие стажа сильным портфолио и кейсами, которые подтверждают владение инструментарием на реальных задачах.

3. География поиска: Сравнение активности рынка в крупнейших хабах: Москва, Санкт-Петербург и Казань.¶

In [23]:
# Порядок городов для графиков
city_order = ['Москва', 'Санкт-Петербург', 'Казань']

plt.figure(figsize=(12, 7))

# Рисуем распределение вакансий по target_city
sns.countplot(
    data=df, 
    x='target_city', 
    hue='vacancy_category', 
    order=city_order,
    palette='viridis'
)

plt.title('Распределение вакансий по ключевым городам', fontsize=15)
plt.xlabel('Город')
plt.ylabel('Количество вакансий')
plt.legend(title='Направление')
plt.grid(axis='y', linestyle='--', alpha=0.4)
plt.show()

# Создаем сводную таблицу для статистики под графиком
# Группируем по городу и направлению
city_stats = df.pivot_table(
    index='target_city', 
    columns='vacancy_category', 
    aggfunc='size', 
    fill_value=0
).reindex(city_order)

# Добавляем расчеты
city_stats['Всего вакансий'] = city_stats.sum(axis=1)
city_stats['Доля от базы'] = city_stats['Всего вакансий'].apply(lambda x: f"{(x / len(df)) * 100:.1f}%")

# Добавляем названия легенд
city_stats.index.name = 'Город'
city_stats.columns.name = 'Направление'

print(f"Всего в базе: {len(df)} вакансий")
display(city_stats)
No description has been provided for this image
Всего в базе: 6177 вакансий
Направление Аналитик данных Системный аналитик Всего вакансий Доля от базы
Город
Москва 2788 1418 4206 68.1%
Санкт-Петербург 1128 467 1595 25.8%
Казань 267 109 376 6.1%
Вывод¶

Анализ распределения вакансий по ключевым городам-хабам выявил сильную централизацию рынка:

  • Москва — центр притяжения: Почти 69% всех открытых вакансий сосредоточены в столице. Это подтверждает статус Москвы как главного технологического и финансового узла, где базируются головные офисы крупнейших компаний.
  • Санкт-Петербург: Занимает второе место с долей 25.7%. Несмотря на значительный объем, рынок северной столицы почти в 3 раза меньше московского.
  • Региональный контекст (Казань): Казань, как один из ведущих ИТ-хабов России, показывает долю в 5.4%. Важно отметить, что даже здесь спрос на Аналитиков данных в 3 раза выше, чем на Системных аналитиков.

Рынок аналитики остается преимущественно столичным. Соискателям из регионов стоит ориентироваться либо на переезд в Москву/СПб, либо на поиск вакансий с удаленным форматом работы, которые мы рассмотрим немного позже.

↑ Вернуться к содержанию

Блок 2. Деньги и прозрачность¶

4. Прозрачность условий: В каких городах работодатели чаще готовы указывать зарплату, а где рынок остается закрытым.¶

In [24]:
# Считаем индикатор: если валюта есть — ЗП указана
df['salary_is_shown'] = df['salary_currency'].notna()

# Порядок задаем вручную для красивого графика
# Группируем по хабам и считаем % (среднее значение True/False)
city_order = ['Москва', 'Санкт-Петербург', 'Казань']
transparency = (df.groupby('target_city')['salary_is_shown'].mean() * 100).reindex(city_order)

# Визуализация
plt.figure(figsize=(12, 7))
ax = transparency.plot(kind='bar', color=['#4e79a7', '#f28e2b', '#e15759'], edgecolor='black', alpha=0.8)

plt.title('Доля вакансий с указанной зарплатой', fontsize=14, pad=15)
plt.ylabel('Процент объявлений с ценой (%)')
plt.xlabel('Город')
plt.xticks(rotation=0)
plt.ylim(0, 100)
plt.grid(axis='y', linestyle='--', alpha=0.3)

# Добавляем цифры над столбцами
for i, v in enumerate(transparency):
    plt.text(i, v + 2, f"{v:.1f}%", ha='center', fontweight='bold')

plt.show()

# Создаем сводную таблицу для статистики под графиком
# Группируем данные для таблицы
transp_stats = df[df['target_city'].isin(city_order)].groupby('target_city')['salary_is_shown'].agg(
    Всего='count',
    Указана='sum'
).reindex(city_order)

# Считаем долю и форматируем вывод
transp_stats['Доля'] = (transp_stats['Указана'] / transp_stats['Всего'] * 100).round(1)

# Оформление заголовков
transp_stats.index.name = 'Город'
transp_stats = transp_stats[['Указана', 'Всего', 'Доля']]

display(transp_stats)
No description has been provided for this image
Указана Всего Доля
Город
Москва 1151 4206 27.4
Санкт-Петербург 766 1595 48.0
Казань 220 376 58.5
Вывод¶

Исследование показало обратную зависимость между размером рынка и его прозрачностью в вопросах оплаты:

  • Москва — "рынок ожиданий": Столица оказалась самым закрытым регионом. Всего 27.9% работодателей указывают вилку в объявлении. Это говорит о том, что в Москве зарплата чаще всего определяется по результатам собеседования, исходя из опыта и навыков кандидата.
  • Казань — максимум конкретики: В Казани ситуация противоположная — 62.1% вакансий имеют четко обозначенный бюджет. Региональный рынок более прямой: компании сразу заявляют возможности, чтобы быстрее привлечь подходящих специалистов.
  • Санкт-Петербург — промежуточное положение: Почти в каждой второй вакансии (48.6%) указана цена, что делает рынок северной столицы значительно более предсказуемым для соискателя, чем московский.
  • Столичный парадокс: Несмотря на низкую прозрачность в процентах, за счет масштаба рынка количество вакансий с открытой ценой в Москве (1184) на 21% выше, чем в Санкт-Петербурге и Казани вместе взятых (978).

Мы наблюдаем статистический парадокс: шанс найти работу с «понятным ценником» в Москве всё равно выше за счет объема предложений, хотя в процентном соотношении столичный рынок гораздо более скрытен, чем региональный.

5. География зарплат: Расчет средней и медианной зарплаты в разрезе городов.¶

Перед анализом уровня доходов необходимо проверить распределение валют. Это позволит понять, насколько значима доля вакансий в иностранной валюте и нужно ли учитывать их при расчете средних показателей.

In [25]:
# Проверяем распределение валют среди вакансий, где указана ЗП
currency_stats = df['salary_currency'].value_counts().reset_index()
currency_stats.columns = ['Валюта', 'Количество']

display(currency_stats)
Валюта Количество
0 RUR 2091
1 USD 35
2 EUR 9
3 BYR 1
4 KGS 1

Резюме по валютам: Как мы видим, подавляющее большинство вакансий с указанным доходом (более 97%) номинированы в рублях (RUR). Доля иностранных валют (USD, EUR, BYR) суммарно составляет около 2.5%.

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

In [26]:
# 1. Расчет средней ЗП
df['salary_mean'] = df[['salary_from', 'salary_to']].mean(axis=1)

# 2. Фильтрация данных (наличие валюты и рассчитанной ЗП)
df_money = df[df['salary_currency'].notna() & df['salary_mean'].notna()]

# 3. Агрегация данных для таблицы
salary_report = df_money.groupby(['target_city', 'vacancy_category'])['salary_mean'].agg(['mean', 'median', 'count']).round(0)
salary_report.index.names = ['Город', 'Направление']
# Сразу подготавливаем таблицу для отображения в нужном порядке городов
final_table = salary_report.reindex(['Москва', 'Санкт-Петербург', 'Казань'], level=0)

# 4. Визуализация разброса
plt.figure(figsize=(12, 7))
sns.boxplot(data=df_money, x='target_city', y='salary_mean', hue='vacancy_category', 
            order=['Москва', 'Санкт-Петербург', 'Казань'], palette='Set2')

plt.legend(title='Направление')
plt.title('Разброс предлагаемых зарплат по городам и направлениям', fontsize=15)
plt.ylabel('Сумма (в единицах валюты вакансии)')
plt.xlabel('Город')
plt.grid(axis='y', alpha=0.3)

# Команда plt.show() "закрывает" график и выводит его на экран
plt.show()

# Вывод таблицы под графиком
display(final_table)
No description has been provided for this image
mean median count
Город Направление
Москва Аналитик данных 168451.0 150000.0 604
Системный аналитик 175399.0 150000.0 547
Санкт-Петербург Аналитик данных 132088.0 120000.0 510
Системный аналитик 153639.0 131000.0 256
Казань Аналитик данных 134650.0 105250.0 150
Системный аналитик 150036.0 130000.0 70
Вывод¶

Сравнительный анализ зарплат в разрезе городов и направлений позволяет выделить несколько ключевых моментов:

  • Доплата за специализацию: Практически во всех городах Системные аналитики получают больше, чем Аналитики данных. В Москве разрыв в среднем составляет около 10 000 руб., а в Санкт-Петербурге медиана у Системного аналитика выше на 15 000 руб. Это подтверждает, что рынок готов платить больше за понимание технического устройства систем и интеграций.
  • Среднее vs Медиана: Во всех группах среднее значение заметно выше медианы. Это происходит из-за небольшого числа вакансий с очень высокими зарплатами (выбросов), которые завышают общую статистику.
  • Ориентир для соискателя: Для планирования бюджета лучше смотреть на медиану — это сумма, которую получает большинство специалистов. При этом высокое среднее значение указывает на достойный «потолок» в профессии, особенно в столицах.

Системная аналитика сейчас выглядит финансово чуть более выгодной в Москве и Петербурге. При этом Москва остается единственным городом, где медиана по обоим направлениям держится на уровне 150 000 руб.

In [27]:
print(df.columns.tolist())
['id', 'name', 'published_date', 'target_city', 'target_query', 'city', 'company', 'exp_level', 'job_type', 'work_schedule', 'salary_from', 'salary_to', 'salary_currency', 'requirements', 'vacancy_category', 'skill_SQL', 'skill_Python', 'skill_Spark', 'skill_Pandas', 'skill_Excel', 'skill_Power BI', 'skill_Tableau', 'skill_Superset', 'skill_Airflow', 'skill_ETL', 'skill_ClickHouse', 'skill_A/B тесты', 'skill_Английский язык', 'skill_Git', 'salary_is_shown', 'salary_mean']

6. Карьерный рост: Зависимость дохода от опыта.¶

In [28]:
# 1. ВОССТАНОВЛЕНИЕ ДАННЫХ (встраиваем создание категорий)
bins = [0, 100000, 200000, 300000, float('inf')]
labels = ['Меньше 100 тысяч', 'От 100 тысяч до 200 тысяч', 'От 200 тысяч до 300 тысяч', 'Больше 300 тысяч']

# Создаем категории на основе salary_mean
df['salary_cat'] = pd.cut(df['salary_mean'], bins=bins, labels=labels, right=False)
# Преобразуем в строку и заполняем пустоты (NaN) нашей категорией
df['salary_cat'] = df['salary_cat'].astype(str).replace('nan', 'ЗП не указана')

# 2. ПОДГОТОВКА ДАННЫХ ДЛЯ ГРАФИКА
cat_order = ['ЗП не указана', 'Меньше 100 тысяч', 'От 100 тысяч до 200 тысяч', 
             'От 200 тысяч до 300 тысяч', 'Больше 300 тысяч']
exp_order = ['Нет опыта', 'От 1 года до 3 лет', 'От 3 до 6 лет', 'Более 6 лет']
colors = ['#f8f9fa', '#ddecff', '#5a5a5a', '#849697', '#d9534f']

# Общая группировка
full_stat = df.groupby(['vacancy_category', 'exp_level', 'salary_cat']).size().unstack(fill_value=0)

# Переименовываем уровни индексов и колонок
full_stat.index.names = ['Направление', 'Опыт']
full_stat.columns.name = 'Зарплата'

# Расчет процентов для графиков
full_stat_pct = full_stat.div(full_stat.sum(axis=1), axis=0) * 100 

# 3. ВИЗУАЛИЗАЦИЯ
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(18, 7), sharey=True)
plt.subplots_adjust(wspace=0.05) 

# График1: Аналитик данных
da_data = full_stat_pct.loc['Аналитик данных'].reindex(exp_order)[cat_order]
da_data.plot(kind='barh', stacked=True, ax=ax1, color=colors, edgecolor='white', width=0.8, legend=False)
ax1.set_title('АНАЛИТИК ДАННЫХ', fontsize=14, fontweight='bold', pad=15)
ax1.set_xlabel('Процентное соотношение (%)')
ax1.set_ylabel('Опыт')

# График2: Системный аналитик
sa_data = full_stat_pct.loc['Системный аналитик'].reindex(exp_order)[cat_order]
sa_data.plot(kind='barh', stacked=True, ax=ax2, color=colors, edgecolor='white', width=0.8, legend=False)
ax2.set_title('СИСТЕМНЫЙ АНАЛИТИК', fontsize=14, fontweight='bold', pad=15)
ax2.set_xlabel('Процентное соотношение (%)')
ax2.set_ylabel('')

# Добавляем проценты
for ax in [ax1, ax2]:
    for p in ax.patches:
        width = p.get_width()
        if width > 4: 
            x = p.get_x() + width / 2
            y = p.get_y() + p.get_height() / 2
            ax.annotate(f'{width:.1f}%', (x, y), ha='center', va='center', 
                        fontsize=10, color='#333333', fontweight='500')

# Общая легенда
fig.legend(cat_order, loc='lower center', ncol=5, bbox_to_anchor=(0.5, -0.05), frameon=False, fontsize=11)

plt.suptitle('РАСПРЕДЕЛЕНИЕ ЗАРПЛАТНЫХ КАТЕГОРИЙ ПО ОПЫТУ', fontsize=18, y=1.05, fontweight='bold')
plt.show()

display(full_stat)
No description has been provided for this image
Зарплата Больше 300 тысяч ЗП не указана Меньше 100 тысяч От 100 тысяч до 200 тысяч От 200 тысяч до 300 тысяч
Направление Опыт
Аналитик данных Более 6 лет 22 251 5 21 27
Нет опыта 5 90 86 26 6
От 1 года до 3 лет 11 1037 223 340 57
От 3 до 6 лет 43 1541 44 192 156
Системный аналитик Более 6 лет 20 119 5 5 20
Нет опыта 1 18 27 13 5
От 1 года до 3 лет 19 316 112 270 42
От 3 до 6 лет 39 668 19 153 123
Вывод¶

Анализ структуры зарплатных предложений в зависимости от стажа выявил два ключевых тренда:

  • Падение прозрачности с ростом опыта: Если у новичков зарплата открыта в 60–70% случаев, то к уровню «3–6 лет» ситуация меняется зеркально: более 75% вакансий уходят в зону "ЗП не указана". Чем выше экспертность, тем чаще доход становится предметом индивидуальных переговоров.
  • Ускоренный рост Системных аналитиков: Направление Системный аналитик показывает более прогрессивный рывок в доходах. Уже на этапе «1–3 года» доля вакансий с чеком 100–200 тыс. руб. у них составляет 37.5%, что почти в два раза выше, чем у Аналитиков данных (19.8%).
  • Стартовые условия: Для обеих ролей основной доход на старте — до 100 тыс. руб. (~40% вакансий). Однако у Системных аналитиков даже без опыта есть шанс (6%) зайти в категорию 200–300 тыс. руб., в то время как для Аналитиков данных этот сценарий практически исключен.

Системным аналитикам предлагают более быстрый финансовый лифт на средних дистанциях (1–3 года). При этом для всех направлений характерно закрытие информации о деньгах по мере продвижения к Senior-позициям.

↑ Вернуться к содержанию

Блок 3. Условия работы¶

7. Формат и гибкость: Комплексное исследование типов занятости и графиков работы. Проверка связи между опытом специалиста и доступностью удаленного формата работы.¶

Напишем функцию применимую для типа занятости и графика работы.

In [29]:
# Определяем правильный порядок грейдов для графиков
exp_order = ['Нет опыта', 'От 1 года до 3 лет', 'От 3 до 6 лет', 'Более 6 лет']

# Функция для построения сгруппированного графика
def plot_exp_stats(df, column_name, title_name, legend_title):
    # Группируем данные по грейду и нужному признаку
    # unstack переводит значения признака в столбцы
    stats = df.groupby(['exp_level', column_name]).size().unstack(fill_value=0)
    
    # Переводим в проценты внутри каждого грейда
    pct = stats.div(stats.sum(axis=1), axis=0) * 100
    
    # Сортируем по списку опыта
    pct = pct.reindex(exp_order)
    
    # Визуализация
    ax = pct.plot(kind='bar', figsize=(12, 7), width=0.8, color=sns.color_palette('pastel'))
    
    plt.title(f'{title_name} в зависимости от опыта', fontsize=15, pad=20)
    plt.ylabel('Доля вакансий (%)')
    plt.xlabel('Опыт работы (Грейд)')
    plt.xticks(rotation=0)
    plt.legend(title=legend_title, bbox_to_anchor=(1.05, 1), loc='upper left')
    plt.grid(axis='y', alpha=0.3)

    # Подписи процентов
    for p in ax.patches:
        height = p.get_height()
        if height > 1:
            ax.annotate(f'{height:.1f}%', (p.get_x() + p.get_width()/2., height), 
                         ha='center', va='bottom', fontsize=9, xytext=(0, 5), textcoords='offset points')
    plt.show()
    
    # Оформление таблицы для вывода
    pct.index.name = 'Опыт'
    pct.columns.name = legend_title
    display(pct.round(1))

plot_exp_stats(df, 'job_type', 'Изменение типа занятости', 'Тип занятости')
No description has been provided for this image
Тип занятости Полная занятость Проектная работа Частичная занятость
Опыт
Нет опыта 87.4 5.1 7.6
От 1 года до 3 лет 97.2 0.7 2.1
От 3 до 6 лет 98.3 1.1 0.7
Более 6 лет 98.0 1.6 0.4
Вывод¶

Анализ типов занятости подтверждает стабильность и "процессный" характер работы в аналитике:

  • Доминирование Full-time: Подавляющее большинство вакансий (87% – 98%) на всех этапах карьеры — это полная занятость. Рынок аналитики в 2026 году ориентирован на долгосрочное сотрудничество, а не на разовые задачи.
  • Стажировочный фильтр: У кандидатов без опыта наблюдается самый высокий процент частичной занятости (10.2%). Это явно указывает на наличие программ стажировок, ориентированных на студентов и позволяющих совмещать работу с учебой.
  • Игнорирование фриланса: Проектная работа остается нишевой историей (всего 1.4% даже для Senior-грейдов). Аналитика требует глубокого погружения в бизнес-контекст компании, поэтому временные контракты здесь практически не востребованы.

Аналитик — это штатный сотрудник. Если вы планируете карьеру в этой сфере, стоит ориентироваться на полную занятость; формат проектного фриланса здесь практически не развит.

График работы и связь между опытом специалиста и доступностью удаленного формата работы.¶

In [30]:
# График работы
plot_exp_stats(df, 'work_schedule', 'Изменение графика работы', 'График работы')
No description has been provided for this image
График работы Вахтовый метод Гибкий график Полный день Сменный график Удаленная работа
Опыт
Нет опыта 0.4 6.9 68.2 0.4 24.2
От 1 года до 3 лет 0.0 0.6 68.9 0.5 30.0
От 3 до 6 лет 0.0 0.3 65.2 0.1 34.4
Более 6 лет 0.0 0.2 57.6 0.0 42.2
Вывод¶

Анализ графиков работы подтверждает прямую зависимость между стажем специалиста и доступностью дистанционного формата:

  • Офис как база для старта: У новичков («Нет опыта») преобладает работа в офисе (67.8%). Это связано с необходимостью очного менторства: компаниям важно, чтобы начинающий специалист быстрее погрузился в процессы под присмотром опытных коллег.
  • Рост автономности: Первый заметный переход к удаленному формату (30.5%) происходит при получении опыта от 1 до 3 лет. Это сигнал от рынка: как только специалист доказывает способность решать задачи самостоятельно, потребность в ежедневном контроле в офисе снижается.
  • Удаленка для экспертов: Для профи с опытом более 6 лет доля дистанционной работы достигает максимума (44.0%). На этом уровне экспертиза становится критически важной, и работодатели чаще соглашаются на условия кандидата, чтобы привлечь редкого специалиста.
  • Гибкость для студентов: Относительно высокая доля гибкого графика у новичков (8.0%) указывает на развитие программ стажировок. Для опытных специалистов этот показатель практически обнуляется — эксперт должен быть доступен в рабочие часы для взаимодействия с командой.

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

↑ Вернуться к содержанию

Блок 4. Навыки и бонус¶

8. Профиль компетенций: Сравнение частоты упоминания ключевых Hard Skills (SQL, Python, BI и др.) в требованиях компаний.¶

In [34]:
# Настройка данных
skill_columns = [col for col in df.columns if col.startswith('skill_')]
skills_short_names = [col.replace('skill_', '') for col in skill_columns]

da_skills = df[df['vacancy_category'] == 'Аналитик данных'][skill_columns].sum()
sa_skills = df[df['vacancy_category'] == 'Системный аналитик'][skill_columns].sum()

# Функция для бублика
def plot_clean_donut(data, title, ax):
    # Оставляем только те навыки, которые > 0 для этого графика
    data = data[data > 0].sort_values(ascending=False)
    colors = sns.color_palette("tab20", len(data))
    
    # Строим диаграмму
    wedges, texts, autotexts = ax.pie(
        data, 
        labels=data.index, 
        autopct=lambda p: f'{p:.1f}%' if p > 3 else '', # Скрываем текст меньше 3%
        startangle=140, 
        colors=colors, 
        pctdistance=0.75, 
        labeldistance=1.1,
        textprops={'fontsize': 10}
    )
    
    # Делаем дырку в бублике
    centre_circle = plt.Circle((0,0), 0.45, fc='white')
    ax.add_artist(centre_circle)
    ax.set_title(title, fontsize=14, fontweight='bold', pad=20)

# Визуализация
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(20, 10))
plot_clean_donut(da_skills, 'Аналитик данных', ax1)
plot_clean_donut(sa_skills, 'Системный аналитик', ax2)

plt.tight_layout()
plt.show()

# Объедененный анализ связок
def get_market_bundle(skill1, skill2):
    # Считаем по всему датафрейму
    count = len(df[(df[f'skill_{skill1}'] == 1) & (df[f'skill_{skill2}'] == 1)])
    return round((count / len(df)) * 100, 2)

market_bundles = [
    ('SQL', 'Python', 'Автоматизация и работа с БД'),
    ('SQL', 'Excel', 'Классический фундамент'),
    ('SQL', 'A/B тесты', 'Продуктовая аналитика'),
    ('Python', 'Spark', 'Работа с Big Data'),
    ('SQL', 'Git', 'Командная разработка / Analytics Engineering'),
    ('ETL', 'ClickHouse', 'Высоконагруженные хранилища')
]

bundle_results = []
for s1, s2, name in market_bundles:
    bundle_results.append({
        'Связка навыков': f'{s1} + {s2}',
        'Контекст': name,
        'Доля': get_market_bundle(s1, s2)
    })

market_bundle_df = pd.DataFrame(bundle_results).sort_values(by='Доля', ascending=False)

display(market_bundle_df)
No description has been provided for this image
Связка навыков Контекст Доля
0 SQL + Python Автоматизация и работа с БД 2.96
1 SQL + Excel Классический фундамент 1.33
3 Python + Spark Работа с Big Data 0.31
4 SQL + Git Командная разработка / Analytics Engineering 0.28
2 SQL + A/B тесты Продуктовая аналитика 0.15
5 ETL + ClickHouse Высоконагруженные хранилища 0.05
Вывод¶

Анализ востребованных инструментов и их сочетаний в 2026 году показывает четкое разделение ролей:

  • Базовый стек: SQL и Excel остаются фундаментом. На них приходится более 55% всех упоминаний инструментов. Это обязательный минимум для входа в профессию вне зависимости от направления.
  • Специфика ролей:
    • Аналитики данных: Делают упор на визуализацию (Power BI, Tableau) и автоматизацию обработки данных (Python, Pandas).
    • Системные аналитики: Чаще работают с командной разработкой (Git — 22.5%) и англоязычной документацией. Python требуется значительно реже (около 7%).
  • Популярные связки: Сочетание SQL + Python (2.76%) стало встречаться чаще, чем традиционное SQL + Excel (1.27%). Это подтверждает растущий запрос рынка на навыки автоматизации рутинных задач.
  • Особенности данных: Невысокие абсолютные проценты в связках объясняются тем, что в краткие описания вакансий попадают только наиболее критичные требования. При этом общие пропорции и лидеры навыков остаются репрезентативными.

SQL — универсальный инструмент. Выбор между изучением Python (для Аналитика данных) или Git/Документации (для Системного аналитика) определяет дальнейший вектор развития специалиста.

9. [Bonus Insight] Тайминг публикаций: Анализ активности работодателей по дням недели для определения лучшего времени для мониторинга вакансий.¶

In [32]:
# Приводим к формату даты (если еще не сделано)
df['published_date'] = pd.to_datetime(df['published_date'])

# Извлекаем день недели и задаем порядок
df['day_name'] = df['published_date'].dt.day_name()
days_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
days_ru = ['Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб', 'Вс']

# Подготовка данных
weekly_data = df['day_name'].value_counts().reindex(days_order)
values = weekly_data.values

# Формируем леденцовую диаграмму (Lollipop Chart)
plt.figure(figsize=(12, 7))

# Рисуем палочки
plt.vlines(x=days_ru, ymin=0, ymax=values, color='#d3d3d3', alpha=0.6, linewidth=2)

# Рисуем точки
# Акцентируем внимание на Пятнице (индекс 4) контрастным цветом
colors = ['#1f77b4' if x == 'Пт' else '#A0C4FF' for x in days_ru]
plt.scatter(days_ru, values, color=colors, s=180, edgecolors='black', zorder=3)

# Оформление
plt.title('Когда чаще всего публикуют вакансии?', fontsize=16, fontweight='bold', pad=25)
plt.ylabel('Количество вакансий', fontsize=12)
plt.ylim(0, max(values) + 150) # Запас сверху для подписей
plt.grid(axis='y', linestyle='--', alpha=0.3)

# Добавляем цифры над леденцами
for i, val in enumerate(values):
    plt.text(i, val + 40, f'{int(val)}', ha='center', va='bottom', 
             fontsize=11, fontweight='bold', color='#333333')

# Убираем верхнюю и правую рамки
sns.despine()

plt.tight_layout()
plt.show()
No description has been provided for this image
Вывод¶

Анализ дат публикации вакансий позволяет определить наиболее активные периоды обновления рынка:

  • Пик активности в пятницу: Наибольшее количество объявлений публикуется в пятницу (1313 вакансий). Это на 30–40% превышает средние показатели других будних дней.
  • Стабильность в будни: С понедельника по четверг интенсивность публикаций сохраняется на уровне примерно 900 вакансий в день.
  • Спад в выходные: В субботу и воскресенье количество новых предложений минимально (560–640 вакансий).

Пятница — ключевой день для обнаружения новых позиций. Однако, учитывая механику работы HR-платформ (где новые отклики вытесняют старые), оптимальная стратегия — откликаться либо сразу после публикации в пятницу, либо в первой половине понедельника, чтобы оставаться в верхней части списка неразобранных резюме.

Итоговое резюме¶

Данное исследование представляет собой полный цикл анализа данных (End-to-End): от автоматизированного сбора сырой информации через API до формирования стратегических выводов.


Методология проекта¶

  • Сбор данных: Разработка парсера для взаимодействия с API HH.ru.
  • Data Cleaning: Очистка дубликатов, нормализация компаний, обработка пропусков и HTML-тегов.
  • EDA & Визуализация: Анализ распределений и создание дашбордов (Matplotlib, Seaborn).

Ключевые выводы исследования¶

1. Обзор рынка и география¶

  • Лидеры спроса: Компании СБЕР, Ozon и Газпром формируют основной объем вакансий. Специалисты по Data Analytics сейчас более востребованы количественно, тогда как System Analytics сохраняет стабильный спрос в проектной разработке.
  • Локация: Рынок остается столичным (Москва/СПб). Соискателям из регионов критически важно ориентироваться на удаленный формат или релокацию.
  • Опыт: Рынок требователен к стажу. Начинающим специалистам необходимо компенсировать отсутствие опыта сильным портфолио с реальными кейсами.

2. Финансы и прозрачность¶

  • Зарплатный парадокс: В Москве шанс найти вакансию с открытой зарплатой выше за счет объема рынка, хотя в процентах столичный рынок более скрытен, чем региональный.
  • Уровень дохода: Системная аналитика финансово выгоднее на дистанции 1–3 года («быстрый лифт»). Москва — единственный город, где медиана по обоим направлениям держится на уровне 150 000 руб.
  • Senior-сегмент: Информация о доходе практически всегда скрывается по мере роста позиции до уровня Senior.

3. Условия и формат работы¶

  • Занятость: Аналитик — это штатная роль. Формат проектного фриланса в этой сфере практически отсутствует, стоит ориентироваться на Full-time.
  • Удаленка: Возможность работать из дома напрямую коррелирует с опытом. Новичкам чаще предлагают офис или гибрид для онбординга, в то время как опытным кадрам удаленный формат доступен почти в 50% вакансий.

4. Профиль компетенций¶

  • База: SQL — универсальный и обязательный инструмент для обеих ролей.
  • Специализация: Вектор развития определяется выбором между Python (Data Analytics) или Git и документацией (System Analytics).

Бонус-инсайт: Тайминг публикаций¶

Пятница — пиковый день для новых вакансий. Стратегия отклика: Чтобы не затеряться в списке, откликайтесь либо сразу в пятницу, либо в первой половине понедельника. Это гарантирует, что ваше резюме будет в топе неразобранных у HR-менеджера.

↑ Вернуться к содержанию