Данный отчет состоит из 5 секций:

  1. Обработка данных
  2. Анализ динамики спроса на труд в течение года
  3. Общий анализ плотности распределения уровня заработных плат
  4. Детальный анализ плотности распределения уровня заработных плат по регионам
  5. Выводы

Код может быть раскрыт при нажатии кнопки “Show”

1. Обработка данных

  1. Данные о вакансиях и их описании были собраны с сервиса баз данных hh.ru “Люди в цифрах” поэтапно для каждого ФО (в силу ограничения объёмов выгрузки)

  2. После приведения всех таблиц к общему виду и объединения, суммарное количество наблюдений составило 313 865

  3. Для очистки данных от нерелевантных вакансий были отобраны только наблюдения в названия вакансий которых входили следующие структуры: “Диспетчер”|“ДИСПЕТЧЕР”|“диспетчер”|“Оператор”|“оператор”|“ОПЕРАТОР”|“Обращен”|“ОБРАЩЕН”|“обращен”|“Клиент”|“КЛИЕНТ”|“клиент”|“Потребител”|“ПОТРЕБИТЕЛ”|“потребител”|“абонент”|“АБОНЕНТ”|“Абонент”|“call”|“CALL”|“Call”|“Колл”|“КОЛЛ”|“колл”|“контакт”|“КОНТАКТ”|“Контакт”|“Contact”|“CONTACT”|“contact”|“OPERATOR”|“operator”|“Operator”|“customer inquiries”|“Customer inquiries”|“CUSTOMER INQUIRIES”. Также далее были отфильтрованы оставшиеся не относящиеся к функции вакансии. В результате данного действия количество наблюдений в датасете составило 276 182

  4. В изначальных данных представлен разброс заработных плат “от” и “до” для каждой из вакансий

Итоговая ЗП по вакансии была получена по следующему принципу:

"Зарплата от" указана и "Зарплата до" не указана ~ Итоговая зарплата = "Зарплата от"
"Зарплата от" не указана и "Зарплата до" указана ~ Итоговая зарплата = "Зарплата до"
"Зарплата от" указана и "Зарплата до" указана ~ Итоговая зарплата = среднее значение между зарплатой "от" и "до"
"Зарплата от" не указана и "Зарплата до" не указана ~ Данные не учтены в исследовании

В результате данного действия количество наблюдений в датасете составило 153 077

  1. Все представленные в данных валюты были переведены в рубли в соответствии с курсом на момент 28.02.2025:

Курс на момент 28.02.2025:

1 BYR - выведен из обращения, наблюдения удалены из данных 
1 EUR = 92.04 RUR
1 KZT = 0.17 RUR
1 USD = 87.70 RUR
1 UZS = 0.01 RUR
1 UAH = 2.11 RUR
1 GEL = 31.16 RUR

В результате данного действия количество наблюдений в датасете составило 153 077

  1. Итоговая зарплата была переведена в Гросс, в зависсимости от указания о том была ли ЗП выдана “на руки” или “до вычета НДФЛ”

  2. Оставим только “живые” вакансии 2024 или 2025 года последней публикации. В результате данного действия количество наблюдений в датасете составило 148 202

  3. Отфильтруем внутрирегиональные выбросы с помошью Метода межквартильного размаха (IQR). В результате данного действия количество наблюдений в датасете составило 140 017

  4. Отфильтруем оставшиеся значения, которые ниже МРОТ для каждого из регионов. В результате данного действия количество наблюдений в датасете составило 138 291

  5. Для рассмотрения в разрезе Регионов (смотр. пункт 4) данные были исследованы на предмет распределения вакансий по Регионам. Из Рис.1 следует, что в некоторых регионах было опубликовано недостаточное количество вакансий, что делает невозможным ссылаться на них при формулировке выводов о каждом конкретном Регионе. В связи с этим для исследования по 4 пункту данные были обрезаны по условию: Не менее 50 вакансий на Регион. В результате данного действия были отфильтрованы данные по 1 региону и итоговое количество наблюдений в датасете составило 138 278

library(dplyr)
library(stringr)
library(ggplot2)
library(readr)
library(scales)
library(plotly)
library(lubridate)
#install.packages("WriteXLS")
# Загрузка пакета
library(WriteXLS)

#Скачиваем данные по заработным платам по функциональному направлению "Оператор". Данные разделены по ФО и регионам
centr_acc = read_csv("~/Desktop/SSC/Исследование_ЗП_2025/Operator/csv/centralny_FO_oper_27_01_2025.csv")
dalnev_juzhny_acc = read_csv("~/Desktop/SSC/Исследование_ЗП_2025/Operator/csv/dalnevostochny_yuzhny_FO_oper_27_01_2025.csv")
privolzhsk_acc_1 = read_csv("~/Desktop/SSC/Исследование_ЗП_2025/Operator/csv/privolzhsk_oper_01_07_2024.csv")
privolzhsk_acc_2 = read_csv("~/Desktop/SSC/Исследование_ЗП_2025/Operator/csv/privolzhsk_oper_27_01_2025.csv")
uralsk_acc = read_csv("~/Desktop/SSC/Исследование_ЗП_2025/Operator/csv/uralsky_FO_oper_27_01_2025.csv")
spb_sibir_acc = read_csv("~/Desktop/SSC/Исследование_ЗП_2025/Operator/csv/spb_sibirsky_FO_oper_27_01_2025.csv")
msc_obl_acc = read_csv("~/Desktop/SSC/Исследование_ЗП_2025/Operator/csv/moscow_obl_oper_27_01_2025.csv")

#Переименуем регионы г.Санкт-Петербург и г. Москва
spb_sibir_acc$Регион = if_else(spb_sibir_acc$Город == "Санкт-Петербург","Санкт-Петербург", spb_sibir_acc$Регион)
msc_obl_acc$Регион = if_else(msc_obl_acc$Город == "Москва","Москва", msc_obl_acc$Регион)

oper = rbind(centr_acc, dalnev_juzhny_acc, msc_obl_acc, privolzhsk_acc_1, privolzhsk_acc_2, spb_sibir_acc, uralsk_acc)

mrot = read_csv("~/Desktop/SSC/Исследование_ЗП_2025/МРОТ.csv")
mrot = na.omit(mrot)
mrot$Регион = str_squish(mrot$Регион)


#Избавимся от возможных дупликатов одних и тех же данных
#oper$`Страница вакансии на HeadHunter` %>% unique() %>% length()

#Проверим количество регионов
#oper$Регион %>% unique() %>% length()
oper = oper %>% distinct()


rm(centr_acc, dalnev_juzhny_acc, msc_obl_acc, privolzhsk_acc_1, privolzhsk_acc_2, spb_sibir_acc, uralsk_acc)

#Отбираем ценные для нас колонки
oper = oper[c("Регион","Город","Название вакансии","Страница вакансии на HeadHunter", "Специализации вакансии","Дата создания","Дата последней публикации","Дата архивации","Зарплата от","Зарплата до","Валюта зарплаты","Зарплата указана")]

#Посмотрим не попали ли к нам не относящиеся к исследованию вакансии
#oper$`Специализации вакансии` %>% unique()
#oper %>% filter(oper$`Специализации вакансии` == "Автослесарь, автомеханик") 
#oper %>% filter(oper$`Специализации вакансии` == "Арт-директор, креативный директор")
#oper %>% filter(oper$`Специализации вакансии` == "Хостес")
#oper %>% filter(oper$`Специализации вакансии` == "Машинист")
#oper %>% filter(oper$`Специализации вакансии` == "Механик")
#oper %>% filter(oper$`Специализации вакансии` == "Врач)




#Очистим данные от лишних строк
oper = oper %>% filter(str_detect(oper$`Название вакансии`, "Диспетчер|ДИСПЕТЧЕР|диспетчер|Оператор|оператор|ОПЕРАТОР|Обращен|ОБРАЩЕН|обращен|Клиент|КЛИЕНТ|клиент|Потребител|ПОТРЕБИТЕЛ|потребител|абонент|АБОНЕНТ|Абонент|call|CALL|Call|Колл|КОЛЛ|колл|контакт|КОНТАКТ|Контакт|Contact|CONTACT|contact|OPERATOR|operator|Operator|customer inquiries|Customer inquiries|CUSTOMER INQUIRIES"))

oper = oper %>% filter(!str_detect(oper$`Название вакансии`, "Инженер|инженер|ИНЖЕНЕР|Engineer|engineer|ENGINEER|Data|DATA|data|разраб|Разраб|РАЗРАБ|Аналитик|аналитик|АНАЛИТИК|TRANSACTION|Transaction|transaction|Ключ|КЛЮЧ|ключ|MANAGER|Manager|manager|МЕНЕДЖЕР|Менеджер|менеджер|АВАРИ|Авари|авари|Кредит|КРЕДИТ|кредит|ФИНАНС|Финанс|финанс|БАНК|Банк|банк|ВРАЧ|Врач|врач|Юрист|ЮРИСТ|юрист|ЭНЕРГЕТИК|Энергетик|энергетик|Фельдшер|фельдшер|ФЕЛЬДШЕР|НЕДВИЖ|Недвиж|недвиж|БУХГАЛТЕР|Бухгалтер|бухгалтер|ГОРНЫЙ|Горный|горный"))

regions_count = oper %>%
  group_by(Регион) %>%
  summarise(count = n())

regions_count_min = regions_count %>% filter(regions_count$count < 50)
regions_count_min = regions_count_min %>%  dplyr::select(Регион)
g1 = ggplot(regions_count, aes(x = reorder(Регион, count), y = count)) +
  geom_col(fill = "steelblue") +
  coord_flip() +
  labs(
    title = "Рис.1 Количество вакансий функции Оператор по регионам",
    x = "Регион",
    y = "Количество вакансий"
  ) +
  theme_minimal() +
  theme(
    axis.text.y = element_text(size = 12),      
    axis.text.x = element_text(size = 12),       
    axis.title = element_text(size = 14),        
    plot.title = element_text(size = 16,  hjust = 0),
    plot.title.position = "plot"    
  )
g1

rm(g1)
#Посчитаем среднюю заработную плату по вилке "От" и "До"
oper = oper %>% mutate(`Итоговая зарплата` = case_when((oper$`Зарплата от` == "не указана" & oper$`Зарплата до` == "не указана" ) ~ "0", (oper$`Зарплата от` == "не указана" & oper$`Зарплата до` != "не указана") ~ oper$`Зарплата до`, (oper$`Зарплата до` == "не указана" & oper$`Зарплата от` != "не указана") ~ oper$`Зарплата от`, (oper$`Зарплата до` != "не указана" & oper$`Зарплата от` != "не указана") ~ "1.01"))

#oper %>% filter(oper$`Итоговая зарплата` == 0) %>% count()

oper$`Итоговая зарплата` = as.numeric(oper$`Итоговая зарплата`)
oper$`Зарплата от` = as.numeric(oper$`Зарплата от`)
oper$`Зарплата до` = as.numeric(oper$`Зарплата до`)

oper$`Итоговая зарплата` = if_else(oper$`Итоговая зарплата` == 1.01, rowMeans(oper[9:10]), oper$`Итоговая зарплата`)

#oper %>% filter(oper$`Итоговая зарплата` == 0) %>% count()

#Количество нулей не изменилось с предыдущего шага, можем избавиться от всех нулевых значений
oper = oper %>% filter(`Итоговая зарплата` != 0)
#
#oper$`Валюта зарплаты` %>% unique()
#oper %>% filter(oper$`Валюта зарплаты` == "KZT")

#1)
oper$`Итоговая зарплата` = if_else(oper$`Валюта зарплаты` == "EUR",oper$`Итоговая зарплата`*92.04 ,oper$`Итоговая зарплата`)
oper$`Итоговая зарплата` = if_else(oper$`Валюта зарплаты` == "KZT",oper$`Итоговая зарплата`*0.17 ,oper$`Итоговая зарплата`)
oper$`Итоговая зарплата` = if_else(oper$`Валюта зарплаты` == "BYR",oper$`Итоговая зарплата`*0 ,oper$`Итоговая зарплата`)
oper$`Итоговая зарплата` = if_else(oper$`Валюта зарплаты` == "USD",oper$`Итоговая зарплата`*87.70 ,oper$`Итоговая зарплата`)
oper$`Итоговая зарплата` = if_else(oper$`Валюта зарплаты` == "UZS",oper$`Итоговая зарплата`*0.01 ,oper$`Итоговая зарплата`)
oper$`Итоговая зарплата` = if_else(oper$`Валюта зарплаты` == "UAH",oper$`Итоговая зарплата`*2.11 ,oper$`Итоговая зарплата`)
oper$`Итоговая зарплата` = if_else(oper$`Валюта зарплаты` == "GEL",oper$`Итоговая зарплата`*31.16 ,oper$`Итоговая зарплата`)

oper = oper %>% filter(`Итоговая зарплата` != 0)

#2)
oper$`Итоговая зарплата` = if_else(oper$`Зарплата указана` == "на руки", oper$`Итоговая зарплата`/87*100, oper$`Итоговая зарплата`)

g2 = ggplot(data = oper) + 
  geom_point(aes(x = reorder(`Название вакансии`, `Итоговая зарплата`), y = `Итоговая зарплата`), alpha = 0.9, size = 1.5, color = "steelblue") +
  labs(title = "Распределение зп по функции: Оператор", x = "", y = "ЗП") +
  scale_y_continuous(labels = label_number(scale = 1e0, suffix = "")) +
  scale_x_discrete(labels = " ") + 
  theme_minimal()

#g2
rm(g2)

2. Общая динамика спроса на труд

Посмотрим на динамику спроса на труд (по дате последней публикации) в течение года. На Рис.2 представлен график изображающий изменение количества вакансий по месяцам 2024 года. На графике также подписана средняя заработная плата, соответствующая каждому месяцу. Данный грaфик показывает отношение спроса на труд и его стоимости

oper$`Дата создания` = dmy(oper$`Дата создания`)
oper$`Дата последней публикации` = dmy(oper$`Дата последней публикации`)
oper$`Дата архивации` = dmy(oper$`Дата архивации`)

#sum(if_else(year(oper$`Дата последней публикации`) == 2024|year(oper$`Дата последней публикации`) == 2025, FALSE, TRUE))

oper$`Год последней публикации` = year(oper$`Дата последней публикации`)
oper = oper %>% filter(oper$`Год последней публикации`== 2024 | oper$`Год последней публикации`== 2025)

days_count = oper %>% group_by(`Дата последней публикации`) %>% summarise(n = n()) 

g7 = ggplot(days_count) + geom_line(aes(x = `Дата последней публикации`, y = n), color = "steelblue") +
  xlab("") +
  ylab("Количество вакансий") +
  scale_y_continuous(labels = scales::comma) +
  theme_minimal()


#g7
rm(g7)



#amr = oper %>%  filter(Регион == "Амурская область")
#amr$`Итоговая зарплата` = round(amr$`Итоговая зарплата`)
#amr = amr %>%
#  group_by(Регион) %>%  mutate(
#    Q1 = quantile(`Итоговая зарплата`, 0.25, na.rm = TRUE),
#    Q3 = quantile(`Итоговая зарплата`, 0.75, na.rm = TRUE),
#    IQR_value = Q3 - Q1,
#    lower_bound = Q1 - 1.5 * IQR_value,
#    upper_bound = Q3 + 1.5 * IQR_value
#  ) %>%
  # Фильтруем строки, оставляя только значения, находящиеся в пределах установленных границ
#  filter(`Итоговая зарплата` >= lower_bound, `Итоговая зарплата` <= upper_bound) %>%
#  ungroup() # Убираем группировку, возвращаясь к исходной структуре данных
  # Удаляем вспомогательные столбцы, если они больше не нужны



oper_outliers = oper
oper_outliers = oper_outliers %>% filter(`Итоговая зарплата` >= quantile(oper_outliers$`Итоговая зарплата`, 0.1), `Итоговая зарплата` <= quantile(oper_outliers$`Итоговая зарплата`, 0.9))

oper_outliers = oper_outliers %>% left_join(mrot)
#is.na(oper_outliers$МРОТ) %>% sum()
oper_outliers = oper_outliers %>% filter(`Итоговая зарплата` >= МРОТ)



oper$`Итоговая зарплата` = round(oper$`Итоговая зарплата`)
oper = oper %>%
  group_by(Регион) %>%  mutate(
    Q1 = quantile(`Итоговая зарплата`, 0.25, na.rm = TRUE),
    Q3 = quantile(`Итоговая зарплата`, 0.75, na.rm = TRUE),
    IQR_value = Q3 - Q1,
    lower_bound = Q1 - 1.5 * IQR_value,
    upper_bound = Q3 + 1.5 * IQR_value
  ) %>%
  # Фильтруем строки, оставляя только значения, находящиеся в пределах установленных границ
  filter(`Итоговая зарплата` >= lower_bound, `Итоговая зарплата` <= upper_bound) %>%
  ungroup() %>%  # Убираем группировку, возвращаясь к исходной структуре данных
  # Удаляем вспомогательные столбцы, если они больше не нужны
  dplyr::select(-Q1, -Q3, -IQR_value, -lower_bound, -upper_bound)


oper = oper %>% left_join(mrot)
#is.na(oper$МРОТ) %>% sum()
oper = oper %>% filter(`Итоговая зарплата` >= МРОТ)



weeks_count = days_count %>% group_by(year = year(`Дата последней публикации`),week = week(`Дата последней публикации`)) %>% summarise(cnt = sum(n))
weeks_count = weeks_count[1:53,]

g8 = ggplot(weeks_count) + geom_line(aes(x = week, y = cnt, color = factor(year)),show.legend = FALSE,) +
  labs(title = "Распределение количества вакансий по неделям, 2024", x = "", y = "Количество вакансий") +
  scale_color_discrete(name = "Год") +
  scale_y_continuous(labels = scales::comma) +
  scale_x_continuous(breaks = c(1, 10, 20, 30, 40, 50), 
                   labels = c("1 нед\n Янв","10 нед\n Мар","20 нед\n Май","30 нед\n Июл","40 нед\n Окт","50 нед\n Дек")) +
  theme_minimal()


#g8

rm(g8)


months_count = days_count %>% group_by(year = year(`Дата последней публикации`),month_1 = month(`Дата последней публикации`)) %>% summarise(cnt = sum(n))

months_wage = oper %>% group_by(year = year(`Дата последней публикации`),month_1 = month(`Дата последней публикации`)) %>% summarise(wage = median(`Итоговая зарплата`))

months_wage = months_wage[,3]

months_count = months_count %>% cbind(months_wage)

months_count = months_count %>% filter(year == 2024)
g9 = ggplot(months_count) + 
  geom_line(aes(x = month_1, y = cnt), show.legend = FALSE, color = "steelblue") + 
  labs(title = " Рис.2 Распределение количества вакансий по месяцам и соответствующая месяцу медианная ЗП, 2024", x = "", y = "Количество вакансий") +
  scale_y_continuous(labels = scales::comma) +
  scale_x_continuous(breaks = 1:12, labels = c("Янв", "Фев", "Мар","Апр", "Май", "Июн","Июл", "Авг", "Сен","Окт", "Ноя", "Дек")) +  
  geom_label(aes(x = month_1, y = cnt, label = paste0(round(wage/1000,3), "K RUB")), 
             size = 3, vjust = -0.5, fill = "white", color = "black") +
  theme_minimal() +
  theme(
    axis.text.y = element_text(size = 12),      
    axis.text.x = element_text(size = 12),       
    axis.title = element_text(size = 14),        
    plot.title = element_text(size = 16,  hjust = 0),
    plot.title.position = "plot"    
  )

g9

rm(days_count, months_count, weeks_count, g9)

Промежуточный вывод: Из Рис.2 видно, что график имеет волно-образную форму, количество поэтапно растёт и снижается, с верхними пиками в Феврале, Июле, Августе, Октябре и нижними пиками в Марте, Сентябре и Декабре. Малое число вакансий публикующихся в Декабре вероятно связано с окончанием года. Стоимость труда неравномерно возрастает, визуально не зависмимо от спроса.

3. Плотность распределения заработных плат

Посмотрим на общий уровень заработной платы по функции “Оператор”

average_regions_oper = oper %>% group_by(Регион) %>% summarise(`Средняя зарплата в регионе` = mean(`Итоговая зарплата`))
average_regions_oper = average_regions_oper %>% arrange(average_regions_oper$`Средняя зарплата в регионе`)
#average_regions_oper %>% slice_min(average_regions_oper$`Средняя зарплата в регионе`, prop = 0.1)


#Точечно проверим верность результатов
acc_altay = oper %>% filter(oper$Регион == "Алтайский край")
#median(acc_altay$`Итоговая зарплата`)

acc_ivanov = oper %>% filter(oper$Регион == "Ивановская область")
#median(acc_ivanov$`Итоговая зарплата`)

acc_kostrom = oper %>% filter(oper$Регион == "Костромская область")
#median(acc_kostrom$`Итоговая зарплата`)

rm(acc_altay, acc_ivanov, acc_kostrom)

g3 = ggplot(oper, aes(x=`Итоговая зарплата`)) + 
    geom_histogram(aes(y=..density..),  
                   binwidth=.5,
                   colour="black", fill="white") +
    geom_density(alpha=.2, fill="#FF6666")
#g3

rm(g3)



# Вычисляем показатели
mean_val   = mean(oper$`Итоговая зарплата`, na.rm = TRUE)
median_val = median(oper$`Итоговая зарплата`, na.rm = TRUE)
q1         = quantile(oper$`Итоговая зарплата`, 0.25, na.rm = TRUE)
q3         = quantile(oper$`Итоговая зарплата`, 0.75, na.rm = TRUE)

# Вычисляем плотность для определения максимального значения по y
dens = density(oper$`Итоговая зарплата`, na.rm = TRUE)
ymax = max(dens$y)

g4 = ggplot(oper, aes(x = `Итоговая зарплата`)) +
  geom_density() +
  geom_vline(xintercept = mean_val, color = "red4", linetype = "dashed", size = 1) +
  geom_vline(xintercept = median_val, color = "navy", linetype = "dashed", size = 1) +
  geom_vline(xintercept = q1, color = "forestgreen", linetype = "dashed", size = 1) +
  geom_vline(xintercept = q3, color = "forestgreen", linetype = "dashed", size = 1) +
  annotate("text", x = mean_val, y = ymax * 0.2, 
           label = paste("Среднее:", round(mean_val)), 
           color = "red4", angle = 90, vjust = -0.5, size = 4.5) +
  annotate("text", x = median_val, y = ymax * 0.2, 
           label = paste("Медиана:", round(median_val)), 
           color = "navy", angle = 90, vjust = -0.5, size = 4.5) +
  annotate("text", x = q1, y = ymax * 0.2, 
           label = paste("1-й квартиль:", round(q1)), 
           color = "forestgreen", angle = 90, vjust = -0.5, size = 4.5) +
  annotate("text", x = q3, y = ymax * 0.2, 
           label = paste("3-й квартиль:", round(q3)), 
           color = "forestgreen", angle = 90, vjust = -0.5, size = 4.5) +
  labs(title = "Рис.3 Плотность распределения ЗП по функции: Оператор", 
       x = "Итоговая зарплата", y = "Плотность") +
  scale_y_continuous(labels = NULL, breaks = NULL) +
  theme_minimal() +
  theme(
    axis.text.y = element_text(size = 12),      
    axis.text.x = element_text(size = 12),       
    axis.title = element_text(size = 14),        
    plot.title = element_text(size = 16,  hjust = 0),
    plot.title.position = "plot"    
  )
g4

rm(g4)

Промежуточный вывод: Данный график показывает, что первые 50% зарплат по функции “Оператор” распределены от уровня МРОТ до 50 000 рублей, последующие 25% распределены ~ от 50 000 до 62 000 рублей, а оставшиеся 25% от 62 000 до 200 000 рублей. После пика в 1ом квартиле плотность распределения убывает с ростом уровня зарплат. Средняя заработная плата для функции “Оператор” по всем регионам = 53 615 Руб. Регулярные пики могут быть обусловлены склонностью людей к размещению вакансий с целочисленными, “круглыми” зарплатами. Также на графике хорошо видны свойства медианы, значение которой равно 50 000 Руб., она не восприимчива к выбросам и в отличии от среднего не завысит/занизит нам результаты. Поэтому для рссмотрения зарплат в рамках регионов будем использовать Медиану.

4. Детальный анализ плотности распределения уровня заработных плат по регионам

Взглянем на распределение заработных плат по регионам

#Отфильтруем регионы с недостатком данных:
oper = oper %>% filter(!oper$Регион %in% regions_count_min$Регион) 

set.seed(88) 
palette = sample( c("thistle2", "peachpuff2", "lightblue2", "darkseagreen2"), size = 83, replace = TRUE )

df_medians = oper %>%
  group_by(Регион) %>%
  summarize(med = median(`Итоговая зарплата`, na.rm = TRUE)/1000)

df_medians$med = round(df_medians$med)
df_medians_new = df_medians %>% left_join(regions_count)



#WriteXLS("df_medians", ExcelFileName = "df_medians_operator.xls")

g5 = ggplot(oper, aes(x = `Итоговая зарплата` , y = reorder(Регион,`Итоговая зарплата`, FUN = median), fill = Регион)) +
  labs(title = "Рис.4 Распределение зп по функции: Оператор", x = "Итоговая зарплата", y = "Плотность") +
  geom_boxplot(show.legend = FALSE) + theme_minimal() + scale_fill_manual(values = palette) +
  geom_text(
    data = df_medians,
    aes(x = med, y = Регион, label = paste(round(med),"т. RUB")),
    vjust = 0.5,
    hjust = 0.8,
    color = "black",
    size = 3,
    fontface = "bold"
  ) +
  theme(
    axis.text.y = element_text(size = 12),      
    axis.text.x = element_text(size = 12),       
    axis.title = element_text(size = 14),        
    plot.title = element_text(size = 16,  hjust = 0),
    plot.title.position = "plot"    
  )
g5

rm(g5)

Промежуточный вывод: Из графика видно, что в рамках региона разброс зарплат в целом относительно небольшой, но с ростом медианной зарплаты разброс увеличивается. Наибольшие зарплаты принадлежат в основном Москве, Санкт-Петербургу и регионам Дальневосточного ФО. Равный уровень медианных зарплат обуславливается склонностью компаний часто указывать одни и те же значения заработных плат, которые после всех видоизменений (приведению к Гросс, переводу в рубли) перестают быть круглыми.

#rm(palette, df_medians)
#rm(palette, df_medians)
median_regions_oper = oper %>% group_by(Регион) %>% summarise(`Медианная зарплата в регионе` = median(`Итоговая зарплата`))
median_regions_oper = median_regions_oper %>% arrange(median_regions_oper$`Медианная зарплата в регионе`)
#median_regions_oper %>% slice_min(median_regions_oper$`Медианная зарплата в регионе`, prop = 0.2)
medians = median_regions_oper %>% slice_min(median_regions_oper$`Медианная зарплата в регионе`, prop = 0.1)
medians = medians[,1]

acc_boxplot = oper %>% filter(oper$Регион %in% medians$Регион | (oper$Регион == "Санкт-Петербург"| oper$Регион == "Москва"))

df_medians = acc_boxplot %>%
  group_by(Регион) %>%
  summarize(med = median(`Итоговая зарплата`, na.rm = TRUE)/1000)

Рассмотрим подробней регионы с наименьшей медианной зарплатой (нижние 10%) и сравним их с Москвой и Санкт-Петербургом:

# Определяем порядок уровней (если требуется)
acc_boxplot$Регион = factor(acc_boxplot$Регион, levels = levels(reorder(acc_boxplot$Регион, acc_boxplot$`Итоговая зарплата`, FUN = median)))

# Создаем именованный вектор цветов
region_colors = c(
  "Республика Ингушетия" = "peachpuff2",
  "Чеченская республика" = "lightblue2",
  "Ненецкий АО" = "thistle2",
  "Кабардино-Балкарская республика" = "peachpuff2",
  "Республика Северная Осетия-Алания" = "thistle2",
  "Республика Дагестан" = "darkseagreen2",
  "Республика Тыва" = "peachpuff2",
  "Республика Адыгея" = "lightblue2",
  "Республика Карелия" = "darkseagreen2",
  "Республика Коми" = "thistle2",
  "Еврейская АО" = "peachpuff2",
  "Санкт-Петербург" = "lightblue2", 
  "Москва" = "lightblue2"   
)

g6 = ggplot(acc_boxplot, aes(x = `Итоговая зарплата`, y = Регион, fill = Регион)) +
  labs(title = "Рис.5 Распределение зп по функции: Оператор", 
       x = "Итоговая зарплата", y = "Регион") +
  geom_boxplot(show.legend = FALSE) +
  theme_minimal() +
  scale_fill_manual(values = region_colors) +
  geom_text(
    data = df_medians,
    aes(x = med, y = Регион, label = paste(round(med),"т. RUB")),
    vjust = 0.5,
    hjust = 0.85,
    color = "black",
    size = 3.5,
    fontface = "bold"
  )  +
  scale_x_continuous(labels = function(x) format(x, scientific = FALSE)) +
  theme(
    axis.text.y = element_text(size = 12),      
    axis.text.x = element_text(size = 12),       
    axis.title = element_text(size = 14),        
    plot.title = element_text(size = 16,  hjust = 0),
    plot.title.position = "plot"    
  )
g6

Промежуточный вывод: Наименьшие зарплаты принадлежат регионам: Кабардино-Балкарская республика, Ненецкий АО, Республика Ингушетия, Республика Северная Осетия-Алания, Чеченская республика, Республика Дагестан, Республика Коми, Республика Адыгея, Республика Карелия. Разброс зарплат по функции “Оператор” в большинстве из них не превышает 30 000 руб, тогда как в Санкт-Петербурге и Москве разброс достигает 50 000 руб.

#rm(df_medians, acc_boxplot)
rm(g6, medians)

5. Выводы

По итогу данного исследования можно сделать следующие выводы:

  1. Вакансии по функции “Оператор” публикуются неравномерно в течение года. Большего всего вакансий публикуется в Июле и в Августе. Меньше всего публикуется в Марте. Стоимость труда неравномерно возрастает, визуально практически не зависмимо от спроса.

  2. Первые 50% зарплат по функции “Оператор” распределены от уровня МРОТ до 50 000 рублей. После пика в 1ом квартиле плотность распределения убывает с ростом уровня зарплат. Средняя заработная плата для функции “Оператор” по всем регионам = 53 615 Руб. Медианная заработная плата = 50 000 Руб.

  3. В рамках региона разброс зарплат в целом не большой, но с ростом медианной зарплаты разброс увеличивается. Наибольшие зарплаты принадлежат в основном Москве, Санкт-Петербургу и регионам Дальневосточного ФО. По этой причине рекомендуется исключить данные регионы из рассмотрения

  4. Наименьшие зарплаты принадлежат регионам: Кабардино-Балкарская республика, Ненецкий АО, Республика Ингушетия, Республика Северная Осетия-Алания, Чеченская республика, Республика Дагестан, Республика Коми, Республика Адыгея, Республика Карелия