Исследовательский анализ данных по медицинскому страхованию
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 столбцов. Мы ограничим наш анализ следующими столбцами:
- Название эмитента
- Название округа
- Маркетинговое название плана
- Медицинский максимум из кармана - Индивидуальный - Стандартный
- Медицинская франшиза - Индивидуальная - Стандартная
- Врач первичной медико-санитарной помощи - Стандарт
- Приемная неотложная помощь - Стандарт
- Специалист - Стандарт
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. Спасибо за чтение!