Исследовательский анализ данных по медицинскому страхованию

Healthcare.gov предоставляет открытый доступ к данным плана квалифицированного медицинского обслуживания (QHP). Наборы данных включают индивидуальные и семейные планы медицинского страхования, доступные в штатах, где федеральное правительство управляет рынком. Количественный анализ имеющихся данных о планах медицинского страхования, а также о других медицинских услугах в целом может помочь сделать нашу систему здравоохранения более прозрачной в том, что касается ценообразования и обслуживания, основанного на стоимости.

В этом посте мы проведем исследовательский анализ данных о ландшафте QHP. Данные доступны здесь. Он также доступен в виде файла .csv на моем GitHub.

Давайте начнем!

Во-первых, давайте сохраним данные во фрейме данных pandas и распечатаем список столбцов:

import pandas as pd
df = pd.read_csv("QHP_landscape_2020.csv")
print(df.columns)

Мы также можем распечатать полный список столбцов:

print(list(df.columns))

Как видите, здесь 128 столбцов. Мы ограничим наш анализ следующими столбцами:

  1. Название эмитента
  2. Название округа
  3. Маркетинговое название плана
  4. Медицинский максимум из кармана - Индивидуальный - Стандартный
  5. Медицинская франшиза - Индивидуальная - Стандартная
  6. Врач первичной медико-санитарной помощи - Стандарт
  7. Приемная неотложная помощь - Стандарт
  8. Специалист - Стандарт
df = df[['Issuer Name', 'County Name', 'Plan Marketing Name',   
         'Medical Maximum Out Of Pocket - Individual - Standard',
         'Medical Deductible - Individual - Standard', 'Primary Care Physician - Standard', 'Specialist - Standard', 'Emergency Room - Standard']]
         
print(df.head())

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

def return_counter(data_frame, column_name, limit):
   from collections import Counter    print(dict(Counter(data_frame[column_name].values).most_common(limit)))

Давайте применим нашу функцию к столбцу «Название эмитента» и ограничим наши результаты пятью наиболее распространенными значениями:

return_counter(df, 'Issuer Name', 5)

Мы видим, что у эмитента «Medica» больше всего записей.

Давайте применим нашу функцию к столбцу «Название округа»:

return_counter(df, 'County Name', 5)

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

Затем было бы полезно сгенерировать сводную статистику из числовых столбцов, таких как «Медицинский максимум из кармана - Индивидуальный - Стандартный». Во-первых, нам нужно преобразовать суммы в долларах со строковым значением в типы с плавающей запятой.

OOP_INDV = []
for i in list(df['Medical Maximum Out Of Pocket - Individual - Standard'].values):
    try:
        OOP_INDV.append(float((str(i)[1] + str(i)[3:])))
    except(ValueError):
        OOP_INDV.append(np.nan)
        
df['OOP_INDV'] = OOP_INDV

И убедимся, что конверсия сработала:

print(df[['Medical Maximum Out Of Pocket - Individual - Standard', 'OOP_INDV']].head())

Выглядит неплохо!

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

def return_statistics(data_frame, categorical_column, numerical_column):
    mean = []
    std = []
    field = []
    for i in set(list(data_frame[categorical_column].values)):
        new_data = data_frame[data_frame[categorical_column] == i]
        field.append(i)
        mean.append(new_data[numerical_column].mean())
        std.append(new_data[numerical_column].std())
    df = pd.DataFrame({'{}'.format(categorical_column): field, 'mean {}'.format(numerical_column): mean, 'std in {}'.format(numerical_column): std})
    df.sort_values('mean {}'.format(numerical_column), inplace = True, ascending = False)
    df.dropna(inplace = True)
    return df

Мы можем просмотреть сводную статистику для «Название эмитента» и «Медицинский максимум из кармана - физическое лицо»:

stats = return_statistics(df, 'Issuer Name', 'OOP_INDV')
print(stats.head(15))

«Blue Cross and Blue Shield of Texas» имеет наивысшую максимальную доступность для физических лиц и нулевое стандартное отклонение для всех типов планов.

Затем мы будем использовать коробчатые диаграммы для визуализации распределения в числовых значениях на основе минимума, максимума, медианы, первого квартиля и третьего квартиля. Если вы с ними не знакомы, прочтите статью Понятие коробчатых графиков.

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

def get_boxplot_of_categories(data_frame, categorical_column, numerical_column, limit):
    import seaborn as sns
    import matplotlib.pyplot as plt
    keys = []
    for i in dict(Counter(df[categorical_column].values).most_common(limit)):
        keys.append(i)
    print(keys)
    
    df_new = df[df[categorical_column].isin(keys)]
    sns.boxplot(x = df_new[categorical_column], y = df_new[numerical_column])

Давайте сгенерируем коробчатые диаграммы для OOP_INDV в 5 наиболее часто встречающихся именах эмитентов:

get_boxplot_of_categories(df, 'Issuer Name', 'OOP_INDV', 5)

Мы можем провести аналогичный анализ по «Медицинской франшизе - Индивидуальный - Стандарт». Давайте преобразуем суммы в долларах со строковым значением в типы с плавающей запятой:

DEDUCT_INDV = []
for i in list(df['Medical Deductible — Individual — Standard'].values):
    try:
        DEDUCT_INDV.append(float((str(i)[1] + str(i)[3:])))
    except(ValueError):
        DEDUCT_INDV.append(np.nan)
        
df['DEDUCT_INDV'] = DEDUCT_IND

И убедимся, что конверсия сработала:

print(df[['Medical Deductible - Individual - Standard', 'DEDUCT_INDV']].head())

Мы можем просмотреть сводную статистику для «Название эмитента» и «Медицинская франшиза - Физическое лицо - Стандарт»:

stats = return_statistics(df, 'Issuer Name', 'DEDUCT_INDV')
print(stats.head(15))

«Wellmark Value Health Plan, Inc.» и «Wellmark Health Plan of Iowa, Inc.» имеют наивысшее значение «Медицинская франшиза - индивидуальный - стандартный».

Давайте сгенерируем коробчатые диаграммы для DEDUCT_INDV в 5 наиболее часто встречающихся именах эмитентов:

get_boxplot_of_categories(df, 'Issuer Name', 'DEDUCT_INDV', 5)

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

def get_scatter_plot_category(data_frame, categorical_column, categorical_value, numerical_column_one, numerical_column_two):
    import matplotlib.pyplot as plt
    import seaborn as sns
    df_new = data_frame[data_frame[categorical_column] == categorical_value]
    sns.set()
    plt.scatter(x= df_new[numerical_column_one], y = df_new[numerical_column_two])
    plt.xlabel(numerical_column_one)
    plt.ylabel(numerical_column_two)

Давайте сгенерируем диаграмму рассеяния OOP_INDV против DEDUCT_INDV для Medica:

get_scatter_plot_category(df, ‘Issuer Name’, ‘Medica’, ‘DEDUCT_INDV’, ‘OOP_INDV’)

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

Подводя итог, я рассмотрел несколько методов анализа данных рынка медицинского страхования. Это включало визуализацию данных с помощью коробчатых диаграмм и диаграмм рассеяния. Мы также определили функции для генерации сводной статистики, такой как среднее значение, стандартное отклонение и количество категориальных значений. Надеюсь, этот пост был интересным. Код из этого поста доступен на GitHub. Спасибо за чтение!