Всем привет! Долго собирался выложить данный пост и вот настал момент = )

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

Язык программирования: Python

Весь код указан с пояснениями, но если у Вас возникли вопросы - отвечу в комментариях.

Начнем!!!

Тема: "Демографическая ситуация по субъектам РФ"

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

Цель проекта:

1.
Определить на какой территории минимальный и максимальный процент безработных за весь период времени.
2. Проверить гипотезу: в каждой из возрастных категорий среднее значение занятых идентично средниму значению безработных
3. Проверить гипотезу: среднее значение безработных (в возрасте от 40 до 49 лет) в Брянской области одинаково со средним значением безработных возрасте до 20 лет
4. Проверить гипотеза: средний показатель безработицы в 2009 и 2019 году одинаков

План анализа данных:

1.
Изучение общей информации
2.
Предобработка данных
2.1. Проверка корректности наименований колонок;
2.2. Проверка и обработка пропущенных значений;
2.3. Проверка и обработка дубликатов;
2.4. Проверка и обработка типов данных;
3. Исследовательский анализ данных
3.1.
Построение гистограммы с количеством безработных и занятых (общему количеству и по конкретной территории);
3.2. Определение на какой территории минимальный и максимальный процент безработных;
3.3. Вычислить среднее число безработных и занятых (по каждой территории)
3.4. Выделить ТОП-5 территорий по безработным и занятым
3.5. Построение столбчатой диаграммы с количеством занятых (по возрастным категориям)
3.6. Построение столбчатой диаграммы с количеством безработных (по возрастным категориям)
4. Проверка гипотез
4.1. Гипотеза: в каждой из возрастных категорий среднее значение занятых идентично среднему значению безработных
4.3. Гипотеза: среднее значение безработных (в возрасте от 40 до 49 лет) в Брянской области одинаково со средним значением безработных возрасте до 20 лет
4.4. Гипотеза: средний показатель безработицы в 2009 и 2019 одинаков
5. Дополнительная информация
5.1. Подготовка документации по библиотекам
6. Общий вывод

Описание данных:

territory - наименование территории по ОКАТО 
num_economactivepopulation_all - численность экономически активного населения - всего 
employed_num_all - занятые в экономике 
unemployed_num_all - безработные 
eactivity_lvl - уровень экономической активности 
employment_lvl - уровень занятости 
unemployment_lvl - уровень безработицы 
dis_unagegroup_to20 - распределение безработных в возрасте до 20 лет по регионам РФ 
dis_unagegroup_20-29 - распределение безработных в возрасте от 20 до 29 лет по регионам РФ 
dis_unagegroup_30-39 - распределение безработных в возрасте от 30 до 39 лет по регионам РФ 
dis_unagegroup_40-49 - распределение безработных в возрасте от 40 до 49 лет по регионам РФ 
dis_unagegroup_50-59 - распределение безработных в возрасте от 50 до 59 лет по регионам РФ 
dis_unagegroup_60older - распределение безработных в возрасте 60 и более лет по регионам РФ 
dis_emagegroup_to20 - распределение занятых в экономике в возрасте до 20 лет по регионам РФ 
dis_emagegroup_20-29 - распределение занятых в экономике в возрасте от 20 до 29 лет по регионам РФ 
dis_emagegroup_30-39 - распределение занятых в экономике в возрасте от 30 до 39 лет по регионам РФ 
dis_emagegroup_40-49 - распределение занятых в экономике в возрасте от 40 до 49 лет по регионам РФ 
dis_emagegroup_50-59 - распределение занятых в экономике в возрасте от 50 до 59 лет по регионам РФ 
dis_emagegroup_60older - распределение занятых в экономике в возрасте 60 и более лет по регионам РФ 
num_unagegroup_to20 - численность безработных в возрасте до 20 лет по регионам РФ 
num_unagegroup_20-29 - численность безработных в возрасте от 20 до 29 лет по регионам РФ 
num_unagegroup_30-39 - численность безработных в возрасте от 30 до 39 лет по регионам РФ 
num_unagegroup_40-49 - численность безработных в возрасте от 40 до 49 лет по регионам РФ 
num_unagegroup_50-59 - численность безработных в возрасте от 50 до 59 лет по регионам РФ 
num_unagegroup_60older - численность безработных в возрасте 60 и более лет по регионам РФ 
num_emagegroup_to20 - численность занятых в экономике регионов РФ в возрасте до 20 лет 
num_emagegroup_20-29 - численность занятых в экономике регионов РФ в возрасте от 20 до 29 лет 
num_emagegroup_30-39 - численность занятых в экономике регионов РФ в возрасте от 30 до 39 лет 
num_emagegroup_40-49 - численность занятых в экономике регионов РФ в возрасте от 40 до 49 лет 
num_emagegroup_50-59 - численность занятых в экономике регионов РФ в возрасте от 50 до 59 лет 
num_emagegroup_60older - численность занятых в экономике регионов РФ в возрасте 60 и более лет 
year - отчетный год

Изучение общей информации

Откроем файл с данными и изучим общую информацию

# импортируем необходимые библиотеки
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
from scipy import stats as st

import warnings
warnings.filterwarnings('ignore')

# для полноценной работы со строками мы уберем ограничение строк и столбцов в отображении
pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns', None)
# присвоим переменной data нашу таблицу
data = pd.read_csv('Desktop/Датасеты/Статистические данные о занятости и безработице среди населения/data.csv')
# выведем первые пять строки нашей таблицы
data.head()

Посмотрим на информацию по нашим столбцам. 

С помощью метода info() мы можем увидеть типы каждого из столбцов.

# проверяем типы столбцов
data.info()

Мы ознакомились с данными, с которыми в дальнейшем будем работать. Теперь перейдем к самой обработке.

Предобработка данных

Проверка корректности наименований колонок

# отобразим названия колонок
list(data.columns)

Теперь мы переименуем наши колонки, чтобы они стали более понятными.

data.rename(columns = {
    'territory' : 'Территория',
    'num_economactivepopulation_all' : 'Численность населения',
    'employed_num_all' : 'Занятые в экономике',
    'unemployed_num_all' : 'Безработные',
    'eactivity_lvl' : 'Уровень экономической активности',
    'employment_lvl' : 'Уровень занятости',
    'unemployment_lvl' : 'Уровень безработицы',
    'dis_unagegroup_to20' : 'Распределение безработных (до 20 лет)',
    'dis_unagegroup_20-29' : 'Распределение безработных (от 20 до 29 лет)',
    'dis_unagegroup_30-39' : 'Распределение безработных (от 30 до 39 лет)',
    'dis_unagegroup_40-49' : 'Распределение безработных (от 40 до 49 лет)',
    'dis_unagegroup_50-59' : 'Распределение безработных (от 50 до 59 лет)',
    'dis_unagegroup_60older' : 'Распределение безработных (60 и более лет)',
    'dis_emagegroup_to20' : 'Распределение занятых (до 20 лет)',
    'dis_emagegroup_20-29' : 'Распределение занятых (от 20 до 29 лет)',
    'dis_emagegroup_30-39' : 'Распределение занятых (от 30 до 39 лет)',
    'dis_emagegroup_40-49' : 'Распределение занятых (от 40 до 49 лет)',
    'dis_emagegroup_50-59' : 'Распределение занятых (от 50 до 59 лет)',
    'dis_emagegroup_60older' : 'Распределение занятых (60 и более лет)',
    'num_unagegroup_to20' : 'Численность безработных (до 20 лет)',
    'num_unagegroup_20-29' : 'Численность безработных (от 20 до 29 лет)',
    'num_unagegroup_30-39' : 'Численность безработных (от 30 до 39 лет)',
    'num_unagegroup_40-49' : 'Численность безработных (от 40 до 49 лет)',
    'num_unagegroup_50-59' : 'Численность безработных (от 50 до 59 лет)',
    'num_unagegroup_60older' : 'Численность безработных (60 и более лет)',
    'num_emagegroup_to20' : 'Численность занятых (до 20 лет)',
    'num_emagegroup_20-29' : 'Численность занятых (от 20 до 29 лет)',
    'num_emagegroup_30-39' : 'Численность занятых (от 30 до 39 лет)',
    'num_emagegroup_40-49' : 'Численность занятых (от 40 до 49 лет)',
    'num_emagegroup_50-59' : 'Численность занятых (от 50 до 59 лет)',
    'num_emagegroup_60older' : 'Численность занятых (60 и более лет)',
    'year' : 'Год',
}, inplace = True)

Далее проверим как изменились наши столбцы.

list(data.columns)

Теперь колонки имеют более читабельный вид. Перейдем к обработке пропусков.

Проверка и обработка пропущенных значений

Перед тем, как мы будем делать обработку - нам необходимо проверить количество пропусков в каждом столбце.

# проверим количество пропусков (метод isnull()) и посчитаем сумму (метод sum())
data.isnull().sum()

Мы видим в каждой ячейке пропущенные значения, кроме "Территории" и "Год". В большинстве столбцов пропущенных значений (2) и мы можем предположить, что две строки полностью пустые. Но для того, чтобы точно определиться мы это проверим.

# проверяем пропуска, во всех ли столбцах они есть? и предпримем необходимые действия
data[pd.isnull(data).any(axis=1)]

Действительно строка 40 и строка 131 с пустыми значениями. Предположительно по этим территориям не фиксировались данные, поэтому мы исключим строки.

# удаляем строки, в которых отсутствуют данные по всем ячейкам. Это строки 40 и 131
data.drop(labels = [40,131],axis = 0, inplace = True)
# проверяем, как удалились строки 40 и 131
data[pd.isnull(data).any(axis=1)]

Проверим сколько пропусков у нас осталось сейчас.

data.isnull().sum()

Осталось еще несколько столбцов с пропусками - поработаем и с ними.
Значения в этих ячейках указаны в процентах. Посчитаем сколько процентов сейчас, учитывая, что у нас есть незаполненные ячейки. В указанных столбцах распределение безработных и занятых.

# выведем индексы, они нам понадобятся в дальнейшей работе
data_index = data[pd.isnull(data).any(axis=1)]
print(data_index.index)
# создадим функцию для расчета значений по нескольким столбцам
def data_dis_unage_emage (index):
    data_dis_unage = data.loc[index, ['Распределение безработных (до 20 лет)', 'Распределение безработных (от 20 до 29 лет)', 
                                      'Распределение безработных (от 30 до 39 лет)', 'Распределение безработных (от 40 до 49 лет)', 
                                      'Распределение безработных (от 50 до 59 лет)', 'Распределение безработных (60 и более лет)']].sum()
    data_dis_emage = data.loc[index, ['Распределение занятых (до 20 лет)', 'Распределение занятых (от 20 до 29 лет)', 
                                      'Распределение занятых (от 30 до 39 лет)', 'Распределение занятых (от 40 до 49 лет)', 
                                      'Распределение занятых (от 50 до 59 лет)', 'Распределение занятых (60 и более лет)']].sum()
    
    # выведем значение столбца с наименованием территорий
    name = data.loc[index, 'Территория']
    
    print(name)
    print('Распределение безработных -', data_dis_unage.round(), '%')
    print('Распределение занятых -', data_dis_emage.round(), '%')
    print()

# пройдемся циклом по всем нашим строкам со значением NaN в рассматриваемых столбцах
for i in data_index.index:
    data_dis_unage_emage(i)

Калининградская область
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
Псковская область
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
Республика Ингушетия
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
Карачаево-Черкесская Республика
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
Республика Мордовия
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
Республика Ингушетия
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
Республика Алтай
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
Орловская область
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
Тамбовская область
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
Ямало-Ненецкий автономный округ
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
Республика Тыва
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
Чукотский автономный округ
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
Орловская область
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
Республика Адыгея
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
г. Севастополь
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
Кабардино-Балкарская Республика
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
Тамбовская область
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
Ямало-Ненецкий автономный округ
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
Амурская область
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %
Чукотский автономный округ
Распределение безработных - 100.0 %
Распределение занятых - 100.0 %

Мы проверили распределение занятых и безработных людей, и учитывая что некоторые ячейки у нас имели значение NaN, то все равно общий процент 100%. Тем самым мы можем заменить NaN на значение 0 в этих ячейках.

# заменяем с помощью метода fillna()
data[['Распределение безработных (до 20 лет)', 'Распределение безработных (от 20 до 29 лет)',  'Распределение безработных (от 30 до 39 лет)', 'Распределение безработных (от 40 до 49 лет)', 'Распределение безработных (от 50 до 59 лет)', 'Распределение безработных (60 и более лет)']] = data[['Распределение безработных (до 20 лет)', 'Распределение безработных (от 20 до 29 лет)', 'Распределение безработных (от 30 до 39 лет)', 'Распределение безработных (от 40 до 49 лет)', 'Распределение безработных (от 50 до 59 лет)', 'Распределение безработных (60 и более лет)']].fillna(0)
data[['Распределение занятых (до 20 лет)', 'Распределение занятых (от 20 до 29 лет)', 'Распределение занятых (от 30 до 39 лет)', 'Распределение занятых (от 40 до 49 лет)',  'Распределение занятых (от 50 до 59 лет)', 'Распределение занятых (60 и более лет)']] = data[['Распределение занятых (до 20 лет)', 'Распределение занятых (от 20 до 29 лет)','Распределение занятых (от 30 до 39 лет)', 'Распределение занятых (от 40 до 49 лет)',  'Распределение занятых (от 50 до 59 лет)', 'Распределение занятых (60 и более лет)']].fillna(0)

# делаем проверку по индексу 26
data.loc[26]

В столбце "Распределение безработных (60 и более лет)" значение с "NaN" поменялось на 0.0, значения изменились. Продолжим работать с остальными пропусками.

# проверяем какие пропуска остались
data[pd.isnull(data).any(axis=1)]
# выведем индексы, они нам понадобятся в дальнейшей работе
data_index_1 = data[pd.isnull(data).any(axis=1)]
# заменяем с помощью метода fillna()
data[['Численность безработных (до 20 лет)',
      'Численность безработных (от 20 до 29 лет)',
      'Численность безработных (от 30 до 39 лет)',
      'Численность безработных (от 40 до 49 лет)',
      'Численность безработных (от 50 до 59 лет)',
      'Численность безработных (60 и более лет)']] = data[['Численность безработных (до 20 лет)',
                                                             'Численность безработных (от 20 до 29 лет)',
                                                             'Численность безработных (от 30 до 39 лет)',
                                                             'Численность безработных (от 40 до 49 лет)',
                                                             'Численность безработных (от 50 до 59 лет)',
                                                             'Численность безработных (60 и более лет)']].fillna(0)
data[['Численность занятых (до 20 лет)', 
      'Распределение занятых (от 20 до 29 лет)',
      'Распределение занятых (от 30 до 39 лет)',
      'Распределение занятых (от 40 до 49 лет)',
      'Распределение занятых (от 50 до 59 лет)',
      'Распределение занятых (60 и более лет)']] = data[['Распределение занятых (до 20 лет)',
                                                         'Распределение занятых (от 20 до 29 лет)',
                                                         'Распределение занятых (от 30 до 39 лет)',
                                                         'Распределение занятых (от 40 до 49 лет)', 
                                                         'Распределение занятых (от 50 до 59 лет)',
                                                         'Распределение занятых (60 и более лет)']].fillna(0)
# делаем проверку по индексу 26
data.loc[683]

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

# проверим сколько пропусков сейчас осталось
data.isnull().sum()

Все пропуска обработаны. Теперь проверим дубликаты.

Проверка и обработка дубликатов

# проверим количество дубликатов (метод duplicated())
sum_duplicated = data.duplicated().sum()
print('Количество дубликатов: ', sum_duplicated)

Количество дубликатов: 0

Проверка и обработка типов данных

Перед тем, как приступить к обработке типов данных столбцов - рассмотрим какиетипы данных в данный момент.

data.info()
# изменением типы на int
data['Численность населения'] = data['Численность населения'].astype(int)
data['Занятые в экономике'] = data['Занятые в экономике'].astype(int)
data['Безработные'] = data['Безработные'].astype(int)

Посмотрим прошли ли изменения типов

data.dtypes

Мы изменили только "Численность населения", "Занятые в экономике", "Безработные" на целочисленное значение. По остальным столбцам оставили дробное, т.к. указаны значения в процентном соотношении.

Вывод

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

Исследовательский анализ данных

# изучаем зависимость занятых и безработных от года
#sns.pairplot(data, x_vars=['Занятые в экономике', 'Безработные'], y_vars=['Год'], size=6)
#plt.show()

Построение гистограммы с количеством безработных и занятых (общему количеству и по конкретной территории)

Посчитаем общее количество безработных по территориям сначала в табличном варианте и выведем первые 10 строк.

sum_unemployed = data.groupby('Территория')['Безработные'].sum().sort_values(ascending=False)
print(sum_unemployed.head(10))

Теперь построим гистограмму на основе полученной таблицы.

# Количество безработных на разных территориях

# построим гистограмму
sum_unemployed.plot(x = 'Территория', y = 'Безработные', kind = 'bar', figsize=(20,8), grid=True, title = 'Количество безработных на разных территориях')

# меняем наименование горизонтальной линии ('X')
plt.xlabel('Территория')
# меняем наименование вертикальной линии ('Y')
plt.ylabel('Безработные')
plt.show()
sum_unemployed.head(2)

Большая часть безработных, а именно на территориях Республики Северной Осетии - Алания и Российская Федерация.
Предположительно Республики Северной Осетии - Алания нехватка рабочих мест, а в РФ это может быть обусловлено большим количеством субьектов.
Так же давайте не забывать, что у нас подсчет по всем безработным по всем возрастам. И может быть такое, что некоторые люди не подходят к той или иной работе в силу возраста, а та работа, которая им необходима уже переполнена персоналом.

sum_unemployed.tail(2)

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

Посчитаем общее количество занятых по территориям.

sum_employed = data.groupby('Территория')['Занятые в экономике'].sum().sort_values(ascending=False)
print(sum_employed.head(10))
# Количество занятых на разных территориях

# построим гистограмму
sum_employed.plot(x = 'Территория', y = 'Занятые в экономике', kind = 'bar', figsize=(20,8), grid=True, title = 'Количество безработных на разных территориях')

# меняем наименование горизонтальной линии ('X')
plt.xlabel('Территория')
# меняем наименование вертикальной линии ('Y')
plt.ylabel('Занятые в экономике')
plt.show()
sum_employed.head(2)
sum_employed.tail(2)

По данной таблице РФ опять показывает значительные результаты, но давайте не будем забыть о ее площади. Если мы посмотрим внимательнее, то можем обратить внимание, что количество занятых в РФ значительно больше чем совокупность регионов РФ, которые указаны в нашей выборке, это может быть обусловлено тем, что не по всем субьектам указаны данные.

Определение на какой территории минимальный и максимальный процент безработных

# считаем долю безработицы по сравнению с общий количеством на территории

data_ter_count_full = data.groupby('Территория')['Численность населения'].sum()

# вычисляем процентное соотношение
percent_data_count_ter_unemployed = sum_unemployed / data_ter_count_full * 100
percent_data_count_ter_unemployed = percent_data_count_ter_unemployed.round(2)
percent_data_count_ter_unemployed = percent_data_count_ter_unemployed.sort_values(ascending=False)
percent_data_count_ter_filter_unemployed = percent_data_count_ter_unemployed.sort_values().head(10)

print('Доля безработицы:')
display(percent_data_count_ter_unemployed)

Большое соотношение безработных приходится на Республику Северная Осетия - Алания, возможно это связано с переодическими военными действиями, которые там проходят.

Прошу обратить внимание на РФ, у которой были значительно высокие показатели как безработных. По соотношению безработных по всем территориям у РФ 6.38%.

percent_data_count_ter_unemployed.plot(x = 'Территория', y = 'Безработные', kind = 'bar', figsize=(20,8), grid=True, title = 'Процентное соотношение безработных на разных территориях')
plt.show()

Теперь сделаем такой же график, только без Руспублики Северная Осетия.

percent_data_count_ter_unemployed_new = percent_data_count_ter_unemployed[1:]

percent_data_count_ter_unemployed_new.plot(x = 'Территория', y = 'Безработные', kind = 'bar', figsize=(20,8), grid=True, title = 'Процентное соотшошение безработных на разных территориях')
plt.show()
print(percent_data_count_ter_unemployed.sort_values(ascending=False).head())

Обратим внимание на Республика Северная Осетия - Алания. Доля безработных составляет 2590,43%
Давайте проверим почему такая большая доля.

# выведем суммарное количество за все года по Республика Северная Осетия - Алания
data_alaniya = data[data['Территория'] == 'Республика Северная Осетия - Алания']
data_alaniya_1 = data_alaniya.groupby('Территория').sum()
data_alaniya_1

Количество безработных по годам в Республика Северная Осетия - Алания

data_alaniya

Мы видим, что общее количество безработных составляет 44141, хотя общее количество составляет 1704. Давайте посмотрим данные за каждый год.

В строке 221 мы видим, что за 2007 год у нас значение безработных "огромное" и в связи с этим у нас некорректно формируется доля безработных. Предлагаю удалить строку с этими показателями.

data = data.drop(index = [221])
data_alaniya = data[data['Территория'] == 'Республика Северная Осетия - Алания']
data_alaniya_1 = data_alaniya.groupby('Территория').sum()
data_alaniya_1

Еще рассчитаем доли безработных

# считаем долю безработицы по сравнению с общий количеством на этой территории (после корректировки количества)

data_count_ter_1 = data.groupby('Территория')['Безработные'].sum()
data_ter_count_full_1 = data.groupby('Территория')['Численность населения'].sum()

# вычисляем процентное соотношение
percent_data_count_ter_1 = data_count_ter_1 / data_ter_count_full_1 * 100

percent_data_count_ter_2 = percent_data_count_ter_1.sort_values(ascending=False).round(2)

print('Доля безработицы:')
display(percent_data_count_ter_2.head())
print('Максимальная доля безработицы')
print(percent_data_count_ter_2.index[0], '-', percent_data_count_ter_2.max(), '%')
print()
print('Минимальная доля безработицы')
print(percent_data_count_ter_2.index[-1], '-', percent_data_count_ter_2.min(), '%')

Небольшой вывод: после чистки некорректных данных в записях мы рассчитали максимальную и минимальную долю безработицы. 
Максимальная доля безработицы в Республике Ингушетия - 38.64 % 
Минимальная доля безработицы в г.Москве - 1.55 %

Вычислить среднее число безработных и занятых (по каждой территории)

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

data_mean_people = pd.DataFrame()

data_mean_people['Среднее значение занятых'] = data.groupby('Территория')['Занятые в экономике'].mean().round(2)
data_mean_people['Среднее значение безработных'] = data.groupby('Территория')['Безработные'].mean().round(2)
display(data_mean_people.head())

axs = data_mean_people.plot.area(figsize=(24, 10), subplots=True)
plt.xticks(rotation=90)
plt.xticks(np.arange(len(data_mean_people)))
plt.show()

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

people_1 = data.groupby('Территория')['Занятые в экономике'].mean().round(2).sort_values(ascending=False)
people_2 = data.groupby('Территория')['Безработные'].mean().round(2).sort_values(ascending=False)

print('Среднее число занятые')
print(data.loc[0, 'Территория'], '-', people_1[0])
print('Среднее число безработные')
print(data.loc[0, 'Территория'], '-', people_2[0])

Мы видим, что у РФ большое количество как занятых, так и безработных, и как писалось ранее это можно аргументировать тем, что достаточно много субъектов входит в ее состав. Поэтому и на графике мы видим значительный рост показателей в отличии от других.

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

data_mean_people_years = pd.DataFrame()

data_mean_people_years['Среднее значение занятых'] = data.groupby('Год')['Занятые в экономике'].mean().round(2)
data_mean_people_years['Среднее значение безработных'] = data.groupby('Год')['Безработные'].mean().round(2)
display(data_mean_people_years)

axs = data_mean_people_years.plot.area(figsize=(24, 10), subplots=True)
plt.xticks(rotation=90)
plt.show()

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

Выделить ТОП-5 территорий по безработным и занятым

# выведем ТОП 5 территорий с занятыми

top_5_data_mean_people_employed = data.groupby('Территория')['Занятые в экономике'].mean().sort_values(ascending=False).head(5).round(2)

print(top_5_data_mean_people_employed)
# выведем ТОП 5 территорий с безработными

top_5_data_mean_people_unemployed = data.groupby('Территория')['Безработные'].mean().sort_values(ascending=False).head(5).round(2)

print(top_5_data_mean_people_unemployed)

Построение столбчатой диаграммы с количеством занятых (по возрастным категориям)

data_2 = data[['Численность занятых (до 20 лет)', 'Численность занятых (от 20 до 29 лет)',
               'Численность занятых (от 30 до 39 лет)', 'Численность занятых (от 40 до 49 лет)',
               'Численность занятых (от 50 до 59 лет)', 'Численность занятых (60 и более лет)']].sum()
display(data_2)
data_2 = data_2.plot()
plt.xticks(rotation=90)
plt.show()

Построение столбчатой диаграммы с количеством безработных (по возрастным категориям)

data_3 = data[['Численность безработных (до 20 лет)', 'Численность безработных (от 20 до 29 лет)',
               'Численность безработных (от 30 до 39 лет)', 'Численность безработных (от 40 до 49 лет)',
               'Численность безработных (от 50 до 59 лет)', 'Численность безработных (60 и более лет)']].sum()
display(data_3)
data_3 = data_3.plot()
plt.xticks(rotation=90)
plt.show()

В нашей ТОП-5 выборке привалирует количество занятых людей во всех возрастных категориях, кроме людей до 20 лет и людей старше 60 лет. Объяснить это можно тем, что не все молодое поколение хочет работать, а более приклонный возраст или не может работать в связи с ограниченными возможностями или могут просто не доживать до этого возраста.

Проверка гипотез

В финальной части нам необходимо проверить три гипотезы:

  • в каждой из возрастных категорий среднее значение занятых идентично со средним значением безработных

  • среднее значение безработных (в возрасте от 40 до 49 лет) в Брянской области одинаково со средним значением безработных возрасте до 20 лет

  • средний показатель безработицы в 2009 и 2019 году одинаков

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

Подготовим выгрузку для проверки гипотезы

# сумма занятых до 20
employed_aged_20 = data['Численность занятых (до 20 лет)'].sum().round()
#print("Сумма занятых до 20 лет -", employed_aged_20)
# сумма занятых от 20 до 29 лет
employed_aged_20_to_29 = data['Численность занятых (от 20 до 29 лет)'].sum().round()
#print("Сумма занятых от 20 до 29 лет -", employed_aged_20_to_29)
# сумма занятых от 30 до 39 лет
employed_aged_30_to_39 = data['Численность занятых (от 30 до 39 лет)'].sum().round()
#print("Сумма занятых от 30 до 39 лет -", employed_aged_30_to_39)
# сумма занятых от 40 до 49 лет
employed_aged_40_to_49 = data['Численность занятых (от 40 до 49 лет)'].sum().round()
#print("Сумма занятых от 40 до 49 лет -", employed_aged_40_to_49)
# сумма занятых от 50 до 59 лет
employed_aged_50_to_59 = data['Численность занятых (от 50 до 59 лет)'].sum().round()
#print("Сумма занятых от 50 до 59 лет -", employed_aged_50_to_59)
# сумма занятых с возрастом 60 и более лет
employed_aged_60_or_more = data['Численность занятых (60 и более лет)'].sum().round()
#print("Сумма занятых в возрасте от 60 и более лет -", employed_aged_60_or_more)
#print()

# сумма безработных до 20
unemployed_aged_20 = data['Численность безработных (до 20 лет)'].sum().round()
#print("Сумма безработных до 20 лет -", unemployed_aged_20)
# сумма безработных от 20 до 29 лет
unemployed_aged_20_to_29 = data['Численность безработных (от 20 до 29 лет)'].sum().round()
#print("Сумма безработных от 20 до 29 лет -", unemployed_aged_20_to_29)
# сумма безработных от 30 до 39 лет
unemployed_aged_30_to_39 = data['Численность безработных (от 30 до 39 лет)'].sum().round()
#print("Сумма безработных от 30 до 39 лет -", unemployed_aged_30_to_39)
# сумма безработных от 40 до 49 лет
unemployed_aged_40_to_49 = data['Численность безработных (от 40 до 49 лет)'].sum().round()
#print("Сумма безработных от 40 до 49 лет -", unemployed_aged_40_to_49)
# сумма безработных от 50 до 59 лет
unemployed_aged_50_to_59 = data['Численность безработных (от 50 до 59 лет)'].sum().round()
#print("Сумма безработных от 50 до 59 лет -", unemployed_aged_50_to_59)
# сумма безработных с возрастом 60 и более лет
unemployed_aged_60_or_more = data['Численность безработных (60 и более лет)'].sum().round()
#print("Сумма безработных в возрасте от 60 и более лет -", unemployed_aged_60_or_more)

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

employed_under_20 = data['Численность занятых (до 20 лет)']
unemployed_under_20 = data['Численность безработных (до 20 лет)']

print('Среднее количество занятых (до 20 лет) -', data['Численность занятых (до 20 лет)'].mean())
print('Среднее количество безработных (до 20 лет) -', data['Численность безработных (до 20 лет)'].mean())
print()

alpha = .05 # критический уровень статистической значимости
# если p-value окажется меньше него - отвегнем гипотезу

results = st.ttest_ind(
    employed_under_20, 
    unemployed_under_20)

print('p-значение: ', results.pvalue)

if results.pvalue < alpha:
    print("Отвергаем нулевую гипотезу")
else:
    print("Не получилось отвергнуть нулевую гипотезу") 

Результат говорит о том, что нулевая гипотеза не отвергается.

employed_aged_20_to_29 = data['Численность занятых (от 20 до 29 лет)']
unemployed_aged_20_to_29 = data['Численность безработных (от 20 до 29 лет)']

print('Среднее количество занятых (от 20 до 29 лет) -', data['Численность занятых (от 20 до 29 лет)'].mean())
print('Среднее количество безработных (от 20 до 29 лет) -', data['Численность безработных (от 20 до 29 лет)'].mean())
print()

alpha = .05 # критический уровень статистической значимости
# если p-value окажется меньше него - отвегнем гипотезу

results = st.ttest_ind(
    employed_aged_20_to_29, 
    unemployed_aged_20_to_29)

print('p-значение: ', results.pvalue)

if results.pvalue < alpha:
    print("Отвергаем нулевую гипотезу")
else:
    print("Не получилось отвергнуть нулевую гипотезу") 
employed_aged_30_to_39 = data['Численность занятых (от 30 до 39 лет)']
unemployed_aged_30_to_39 = data['Численность безработных (от 30 до 39 лет)']

print('Среднее количество занятых (от 30 до 39 лет) -', data['Численность занятых (от 30 до 39 лет)'].mean())
print('Среднее количество безработных (от 30 до 39 лет) -', data['Численность безработных (от 30 до 39 лет)'].mean())
print()

alpha = .05 # критический уровень статистической значимости
# если p-value окажется меньше него - отвегнем гипотезу

results = st.ttest_ind(
    employed_aged_30_to_39, 
    unemployed_aged_30_to_39)

print('p-значение: ', results.pvalue)

if results.pvalue < alpha:
    print("Отвергаем нулевую гипотезу")
else:
    print("Не получилось отвергнуть нулевую гипотезу") 
employed_aged_40_to_49 = data['Численность занятых (от 40 до 49 лет)']
unemployed_aged_40_to_49 = data['Численность безработных (от 40 до 49 лет)']

print('Среднее количество занятых (от 40 до 49 лет) -', data['Численность занятых (от 40 до 49 лет)'].mean())
print('Среднее количество безработных (от 40 до 49 лет) -', data['Численность безработных (от 40 до 49 лет)'].mean())
print()

alpha = .05 # критический уровень статистической значимости
# если p-value окажется меньше него - отвегнем гипотезу

results = st.ttest_ind(
    employed_aged_40_to_49, 
    unemployed_aged_40_to_49)

print('p-значение: ', results.pvalue)

if results.pvalue < alpha:
    print("Отвергаем нулевую гипотезу")
else:
    print("Не получилось отвергнуть нулевую гипотезу") 
employed_aged_50_to_59 = data['Численность занятых (от 50 до 59 лет)']
unemployed_aged_50_to_59 = data['Численность безработных (от 50 до 59 лет)']

print('Среднее количество занятых (от 50 до 59 лет) -', data['Численность занятых (от 50 до 59 лет)'].mean())
print('Среднее количество безработных (от 50 до 59 лет) -', data['Численность безработных (от 50 до 59 лет)'].mean())
print()

alpha = .05 # критический уровень статистической значимости
# если p-value окажется меньше него - отвегнем гипотезу

results = st.ttest_ind(
    employed_aged_50_to_59, 
    unemployed_aged_50_to_59)

print('p-значение: ', results.pvalue)

if results.pvalue < alpha:
    print("Отвергаем нулевую гипотезу")
else:
    print("Не получилось отвергнуть нулевую гипотезу") 
employed_aged_60_or_more = data['Численность занятых (60 и более лет)']
unemployed_aged_60_or_more = data['Численность безработных (от 50 до 59 лет)']

print('Среднее количество занятых (60 и более лет) -', data['Численность занятых (60 и более лет)'].mean())
print('Среднее количество безработных (от 50 до 59 лет) -', data['Численность безработных (от 50 до 59 лет)'].mean())
print()

alpha = .05 # критический уровень статистической значимости
# если p-value окажется меньше него - отвегнем гипотезу

results = st.ttest_ind(
    employed_aged_60_or_more, 
    unemployed_aged_60_or_more)

print('p-значение: ', results.pvalue)

if results.pvalue < alpha:
    print("Отвергаем нулевую гипотезу")
else:
    print("Не получилось отвергнуть нулевую гипотезу") 

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

Гипотеза: среднее значение безработных (в возрасте от 40 до 49 лет) в Брянской области одинаково со средним значением безработных возрасте до 20 лет

data_bryansk = data.query('Территория == "Брянская область"')
#display(data_bryansk.head())
# сумма безработных Брянская область от 40 до 49 лет
unemployed_aged_40_to_49_bryansk = data_bryansk['Численность безработных (от 40 до 49 лет)'].sum().round(2)
#display(unemployed_aged_40_to_49_bryansk)
#print()
# сумма безработных до 20 лет
unemployed_aged_20_bryansk = data_bryansk['Численность безработных (до 20 лет)'].sum().round(2)
#display(unemployed_aged_20_bryansk)
unemployed_under_40_bryansk = data['Численность безработных (от 40 до 49 лет)']
unemployed_under_20_bryansk = data['Численность безработных (до 20 лет)']

print('Среднее количество безработных (от 40 до 49 лет) -', data['Численность безработных (от 40 до 49 лет)'].mean())
print('Среднее количество безработных (до 20 лет) -', data['Численность безработных (до 20 лет)'].mean())
print()

alpha = .05 # критический уровень статистической значимости
# если p-value окажется меньше него - отвегнем гипотезу

results = st.ttest_ind(
    employed_aged_60_or_more, 
    unemployed_aged_60_or_more)

print('p-значение: ', results.pvalue)

if results.pvalue < alpha:
    print("Отвергаем нулевую гипотезу")
else:
    print("Не получилось отвергнуть нулевую гипотезу") 

Количество занятых в разы больше безработных - гипотеза отвергнута.

Гипотеза: средний показатель безработицы в 2009 и 2019 году одинаков

data_unemployed_2009 = data.query('Год == 2009')
#display(data_unemployed_2009.head())
#print()
data_unemployed_2019 = data.query('Год == 2019')
#display(data_unemployed_2019.head())
# сумма безработных 2009
unemployed_year_2009 = data_unemployed_2009['Безработные'].count()
#display(unemployed_year_2009)
#print()
# сумма безработных 2019
unemployed_year_2019 = data_unemployed_2019['Безработные'].count()
#display(unemployed_year_2019)

Средний показатель безработицы за 2009 и 2019 года примерно одинаковый.

unemployed_2009 = unemployed_year_2009
unemployed_2019 = unemployed_year_2019

print('Среднее количество безработных в 2009 -', unemployed_2009)
print('Среднее количество безработных в 2019 -', unemployed_2019)
print()

alpha = .05 # критический уровень статистической значимости
# если p-value окажется меньше него - отвегнем гипотезу

results = st.ttest_ind(
    unemployed_2009, 
    unemployed_2019)

print('p-значение: ', results.pvalue)

if results.pvalue < alpha:
    print("Отвергаем нулевую гипотезу")
else:
    print("Не получилось отвергнуть нулевую гипотезу") 

Средний показатель безработицы за 2019 года преобладает.

Дополнительная информация

Подготовка документации по библиотекам

pandas - это библиотека Python для обработки и анализа структурированных данных, её название происходит от «panel data» («панельные данные»). Панельными данными называют информацию, полученную в результате исследований и структурированную в виде таблиц. Для работы с такими массивами данных и создан Pandas. 
matplotlib - это библиотека на языке Python для визуализации данных. В ней можно построить двумерные (плоские) и трехмерные графики. 
matplotlib.pyplot - самый высокоуровневый интерфейс с набором команд и функций. В высокоуровневом интерфейсе все автоматизировано, поэтому его проще всего осваивать новичкам. 
seaborn - библиотека для создания статистических графиков на Python. Она построена на основе matplotlib и тесно интегрируется со структурами данных pandas. Seaborn помогает вам изучить и понять данные. Его функции построения графиков работают с датасетами и выполняют все необходимы преобразования для создания информативных графиков. 
numpy - библиотека с открытым исходным кодом для языка программирования Python. Возможности: поддержка многомерных массивов (включая матрицы); поддержка высокоуровневых математических функций, предназначенных для работы с многомерными массивами. 
scipy - это еще одна научная библиотека, на базе которой реализована SciPy. Ее особенность — использование специальных структур данных — многомерных массивов, в которых хранится информация. С этим типом данных также работает SciPy. NumPy содержит данные массивов и такие операции, как индексация, сортировка и т. д., а SciPy состоит из числового кода. 
warnings - полезен, когда необходимо предупредить пользователя о каком-либо условии в программе и это условие не требует создания исключения и завершения программы. Например, может возникнуть необходимость выдать предупреждение, когда программа использует устаревший модуль.

Общий вывод

Сделаем вывод по проделанной работе. После тщательной подготовки файла с данными мы провели диагностику. 

В финальной части нам необходимо проверить три гипотезы: 

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

• среднее значение безработных (в возрасте от 40 до 49 лет) в Брянской области одинаково со средним значением безработных возрасте до 20 лет 
Так же нулевая гипотеза опровергнута: 
Среднее количество безработных (от 40 до 49 лет) - 598.7747584541063 
Среднее количество безработных (до 20 лет) - 10.09806763285024 

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

Комментарии (9)


  1. pewpew
    01.09.2022 01:12
    +1

    Статья похожа на чью-то курсовую работу. Интересно конечно, но…
    А где сами данные?
    Где ссылка на исходники / github?
    Или я что-то пропустил?
    Без возможности повторить опыты ценность статьи сильно падает.


    1. Siciliez88 Автор
      01.09.2022 01:19
      +1

      Статью выкладываю впервые! Это личная кропотливая работа в свободное от работы время:) на GitHub аккаунта пока нет, но если на хабре разрешено, могу скинуть ссылку на сайт с датасетом! Они в общем бесплатном доступе с открытой лицензией


      1. pewpew
        01.09.2022 11:45

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


        1. Siciliez88 Автор
          01.09.2022 12:41

          Ссылка на исходный файл https://www.data-in.ru/data-catalog/datasets/122/
          Как и говорил ранее, файл в свободном доступе.


    1. Siciliez88 Автор
      01.09.2022 01:21

      Так же в последующих статьях буду выкладывать код и файлы на GitHub


  1. nnnXion
    01.09.2022 12:32
    +1

    Спасибо, было полезно.


  1. julicq
    01.09.2022 15:29

    В разделе проверки гипотезы с Брянском накосячили)


    1. Siciliez88 Автор
      01.09.2022 15:30

      В чем именно это выражается?


  1. Laifology
    01.09.2022 19:45

    Спасибо, было очень полезно. Побольше бы таких материалов )