Начало тут

Руководящие документы по организации первичной медико-социальной помощи населению предписывают проводить сравнительный анализ численности населения по территориальным участкам (норматив численности населения на терапевтическом участке - 1700 взрослых, на педиатрическом участке - 800 детей, на акушерско-гинекологическом участке - 3300 женщин в возрасте 15 лет и старше и т.д.) .

Оценку численности населения по субъектам РФ Росстат публикует ежегодно на 1 января текущего года. Для крупных городов территории обслуживания населения медицинскими организациями часто не совпадают с адресно административным делением и распределение населения по зонам ответственности медицинской организации становится скорее творчеством нежели технологической процедурой. Вопрос как декомпозировать данные из бюллетеня Росстата до медицинского участка для меня остается нерешенным.

Мы пойдем другим путем. Данные о населении мы можем получить из медицинской информационной системы (МИС). База МИС обогащается на регулярной основе данными страховых компаний о застрахованных лицах по программе обязательного медицинского страхования (ОМС).

Для работы нам понадобится обезличенная выгрузка из МИС, содержащая данные по пациентам: пол, дату рождения, адрес регистрации, адрес фактического места жительства, данные медицинской организации и номера участка по терапевтическому или педиатрическому профилю. Я загрузил ее в pandas.dataframe.

Рассчитаем возраст пациентов на текущую дату и отсортируем по возрастанию. В нашем случае, в МИС используется несколько форматов хранения даты рождения, учитываем это при написании функции.

def calculate_age(birth_date):
    format_str=["%d.%m.%Y", "%d.%m.%Y %H:%M:%S", "%Y-%m-%d %H:%M:%S"]
    today = date.today()
    i = 0

    while (i < len(format_str)):
        try:
            birth_date = datetime.datetime.strptime(birth_date, format_str[i]) 
        except:
            i +=  1
    try:
        age = today.year - birth_date.year
        full_year_passed = (today.month, today.day) < (birth_date.month, birth_date.day)
        if not full_year_passed:
            age -= 1
        #в каждой БД есть свой ветеран Куликовской битвы
        #if age > 100:
        #   print(birth_date)

    except:
        age = -1
    return age


df['age'] = df['birthdate'].apply(calculate_age)
df = df.sort_values(by='age', ascending=True)

Методы pandas позволяют агрегировать и группировать данные. Сделаем группировку населения по возрасту для часто использующих запросов информации и построим график

#Группировка дети/взрослые и т.д методами Pandas

df['age_lvls_child_adults'] = pd.cut(df['age'],bins=[-999,0,18,199],
                                     labels=['Неуказан','0-17','18+'],right=False)

df['age_lvls_foms'] = pd.cut(df['age'],bins=[-999,0,5,18,60,199],
                             labels=['Неуказан','0-4','5-17','18-59', '60+'],right=False)
df['age_lvls_stat']=pd.cut(df['age'],
                             bins=[-999,0,
                                  5,10,15,20,25,
                                  30,35,40,45,50,
                                  55,60,65,70,199],
                             labels=['Неуказан',
                                     '0-4','5-9','10-14','15-19','20-24',
                                     '25-29','30-34','35-39','40-44','45-49',
                                     '50-54','55-59','60-64','65-69','70+'],right=False)
df['age_lvls_death']=pd.cut(df['age'],
                             bins=[-999,0,1,2,3,4,
                                  5,10,15,20,25,
                                  30,35,40,45,50,
                                  55,60,65,70,75,
                                  80,85,199],
                             labels=['Неуказан','0..','1','2','3',
                                     '4','5-9','10-14','15-19','20-24',
                                     '25-29','30-34','35-39','40-44','45-49',
                                     '50-54','55-59','60-64','65-69','70-74',
                                     '75-79','80-84','85+'],right=False)

df_age_lvls_stat = df.groupby(by=['age_lvls_death'],
                              as_index=False)[['ID','age']].count()
fig_stat = px.bar(df_age_lvls_stat, x='age_lvls_death', y='ID',
                  labels={'age_lvls_death':'Возрастная группа','ID':'Количество человек'}
                 )
fig_stat.show()

Разделим данные по полу и построим график:

#Полово-возрастной состав, расчетная база для группировки “Смертность населения”
df_gb = df.groupby(by=['age_lvls_death','gender'],
                   as_index=False)[['id']].agg(['count']) .reset_index()

fig_gender = px.bar(df_gb, x='age_lvls_death', y='id', 
                    color='gender',
                    color_discrete_map={
                        'женский': 'red',
                        'мужской': 'blue'
                    },
                    labels={'age_lvls_death':'Возрастная группа',
                            'gender':'Пол','id':'Количество человек'},
                    barmode="group", 
                    height=400 
                   )
fig_gender.show()

Для более сложных запросов, учитывающих пол, разный возраст для выхода на пенсию, я использовал метод select пакета numpy:

male = 'мужской'
female = 'женский'
#Примечание: мужчины 16 61 года, женщины 16 56 лет. 
#Трудоспособный возраст в соответствии с приказом Росстата: 
#2021 год 16 60 лет для мужчин и 16 55 лет для женщин; 
#2022 года 16 61 года для мужчин и 16 56 лет для женщин. 

conditions_mzrf = [
    df['age'] < 0,
    df['age'].between(0, 15),
    (df['age'].between(16, 60)) & (df['SEX'] == male),
    (df['age'].between(16, 55)) & (df['SEX'] == female),
    (df['age'] >= 61) & (df['SEX'] == male),
    (df['age'] >= 56) & (df['SEX'] == female)
 ]

choices_mzrf = [' Неуказан','0-15','16-55/16-60','16-55/16-60','56+/61+','56+/61+']

df['age_lvls_trud'] = np.select(conditions_mzrf, choices_mzrf)

fig_trud = px.bar(df_trud.sort_values('age_lvls_trud', ascending=True),
                    x='age_lvls_trud', y='ID', 
                    color='SEX', #включает легенду из столбца ДФ
                    color_discrete_map={'женский': 'red',
                        'мужской': 'blue'},
                    labels={'age_lvls_trud':'Возрастная группа','SEX':'Пол',
                            'ID':'Количество человек'},
                    barmode="group", 
                    text_auto=True, 
                    height=400,                   
                    color_discrete_sequence=px.colors.diverging.Spectral
                   )
fig_trud.update_traces(textfont_size=14, textangle=-90, textposition="inside",
                       cliponaxis=True)
fig_trud.update_layout(uniformtext_minsize=8, uniformtext_mode='hide')
fig_trud.show()

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

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


  1. Darth_Anjan
    22.04.2023 06:35
    +7

    А простой смертный может получить обезличенную выгрузку из МИС? Было бы интересно взглянуть...


    1. cohr Автор
      22.04.2023 06:35

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


      1. Darth_Anjan
        22.04.2023 06:35

        Просто, если смотреть примеры из прошлой статьи, складывается впечатление, что обезличенная выгрузка — не такая уж и обезличенная. Ибо связки места жительства, пола и даты рождения вполне должно хватать для однозначной идентификации субьекта персональных данных (с точки зрения 152-ФЗ).
        Поэтому и интересно: как эту выгрузку сделали, кто может получить к ней доступ, насколько получение такого доступа доступно...


  1. vassabi
    22.04.2023 06:35

    хм, (глядя на разрыв графика между "4" и "5-9")

    а как сильно заметно что в 5-9 лет государство наконец-то узнает сколько в нем человек на самом деле (дети идут в школу)


    1. velon
      22.04.2023 06:35
      +2

      Сначала в графике выводят по 1 году, а потом резко за 5 в одну колонку


      1. cohr Автор
        22.04.2023 06:35

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


        1. velon
          22.04.2023 06:35

          можно, но, как видите, это может вводить в заблуждение. Из-за чего делают неправильные поспешные выводы.


          1. cohr Автор
            22.04.2023 06:35
            +1

            Принято, добавил графики с шагом по возрасту 5 лет


    1. timoxa_dev
      22.04.2023 06:35

      Мне кажется тут все проще, в первом графике дети за один возраст, а во втором графике диапазон возрастов.

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


      1. cohr Автор
        22.04.2023 06:35

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