Analyzing Healthcare Provider Shortage - Part 3/4

  • 👟 Ready To Run!
  • 🔬 Data Science
  • 🚀 Data Exploration and Cleaning
  • 📈 Statistics & Graphing
  • ⚕️ Healthcare

Requirements

  • 🗺️ Living Atlas configured

In this part of the study, we will explore shortage of Mental Healthcare Providers and OB-GYN Providers. We will study:

  • The distribution of providers across all states to identify which states have highest shortage and
  • For states with most shortage, study shortage at the county level to understand which counties are suffering the most.

In this section, we will:

  • Explore Mental Healthcare Providers
    • Study how Mental Healthcare Providers vary by Population Density accross all states
    • Identify state with highest people to provider ratio
    • Explore the state with highest ratio to understand how providers vary across different counties for that state
  • Explore OBGYN Healthcare Providers
    • Study how OBGYN Healthcare Providers vary by population of mothers accross all states
    • Identify state with highest mothers to provider ratio
    • Explore the state with highest ratio to understand how providers vary across different counties for that state

Part 3: Provider Shortage - Mental Health and OB-GYN Providers

In [1]:
# Import Libraries
from IPython.display import display

# Import arcgis
import arcgis
from arcgis.gis import GIS
from arcgis.features import FeatureLayer
from arcgis.mapping import WebMap

# Import libraries for data exploration
import pandas as pd
pd.set_option('display.max_columns', 500)
import numpy as np

# Import plotting libraries
%matplotlib inline
import matplotlib.pyplot as plt
import seaborn as sns

# Import library for time
import time
In [21]:
# Create a GIS connection
gis = GIS("home")

Get Data Layers

Provider data was geocoded using the GeoAnalytics server. We will get the geocoded provider data feature layer. We will also get County level demographic data from the 2018 USA Population Density layer.

Get Provider Data

In [22]:
# Search the feature layer
data_groups = gis.groups.search('"ArcGIS Sample Notebooks Data" owner:esri_notebook',
                                outside_org=True)
group_query = f"group: {data_groups[0].id}" if data_groups else ""
provider_data_item = gis.content.search(f'provider_data_geocoded_7_30 {group_query}',
                                      item_type = "Feature Service",
                                      outside_org=True)[0]
provider_data_item
Out[22]:
provider_data_geocoded_7_30
Geocoded healthcare provider point data of 5.8M providers in US.Feature Layer Collection by portaladmin
Last Modified: June 04, 2020
0 comments, 1 views
In [23]:
# Get the layer needed for analysis
provider_data_layer = provider_data_item.layers[0]
provider_data_layer
Out[23]:
<FeatureLayer url:"https://datascienceqa.esri.com/server/rest/services/Hosted/provider_data_geocoded_7_30/FeatureServer/0">
In [24]:
# Look at the first 5 fields and their data types
for f in provider_data_layer.properties.fields[:5]:
    print(f['name'], "{0:>35}".format(f['type']))
objectid                    esriFieldTypeOID
user_npi                 esriFieldTypeDouble
user_entity_type                 esriFieldTypeString
user_address                 esriFieldTypeString
user_address2                 esriFieldTypeString

Get Demographics Data

Let's get the 2018 USA Population Density layer

In [111]:
# Search for and get Population data layer
popdensity = gis.content.get('b9095ebdf5e8442588ab3f269dc7ee5e') #2020 layer
popdensity
Out[111]:
2020 USA Population Density
This layer shows the population density in the United States in 2020 in persons per square mile in a multiscale map by country, state, county, ZIP Code, tract, and block group. ArcGIS Online subscription required.Map Image Layer by esri
Last Modified: June 29, 2020
0 comments, 7,118 views
In [112]:
# Check first 5 layers in population Density
popdensity.layers[:5]
Out[112]:
[<FeatureLayer url:"https://demographics5.arcgis.com/arcgis/rest/services/USA_Demographics_and_Boundaries_2020/MapServer/0">,
 <FeatureLayer url:"https://demographics5.arcgis.com/arcgis/rest/services/USA_Demographics_and_Boundaries_2020/MapServer/1">,
 <FeatureLayer url:"https://demographics5.arcgis.com/arcgis/rest/services/USA_Demographics_and_Boundaries_2020/MapServer/2">,
 <FeatureLayer url:"https://demographics5.arcgis.com/arcgis/rest/services/USA_Demographics_and_Boundaries_2020/MapServer/3">,
 <FeatureLayer url:"https://demographics5.arcgis.com/arcgis/rest/services/USA_Demographics_and_Boundaries_2020/MapServer/4">]
In [14]:
# Look at first few field names for county layer
county_layer = popdensity.layers[46]
print('FIELD NAME', "{0:>50}".format('FIELD ALIAS'))
for field in county_layer.properties.fields[:10]:
    print(field['name'], "{0:>50}".format(field['alias']))
FIELD NAME                                        FIELD ALIAS
OBJECTID                                           OBJECTID
Shape                                              Shape
ID                                                 ID
NAME                                               NAME
STATE_NAME                                         STATE_NAME
ST_ABBREV                                          ST_ABBREV
TOTPOP_CY                       2020 Total Population (Esri)
HHPOP_CY                   2020 Household Population (Esri)
FAMPOP_CY                      2020 Family Population (Esri)
GQPOP_CY              2020 Group Quarters Population (Esri)
In [13]:
%%time

# Get specific attributes for Counties

county_layer = popdensity.layers[46]
county_df = pd.DataFrame()
out_fields = ['Shape','ST_ABBREV','NAME','ASIAN_CY','AMERIND_CY','AVGHHSZ_CY','AVGHINC_CY','BLACK_CY',
              'EDUCBASECY','HISPPOP_CY','MEDAGE_CY','MINORITYCY','OTHRACE_CY','PCI_CY','POPDENS_CY',
              'UNEMPRT_CY','WHITE_CY','SMCOLL_CY','ASSCDEG_CY','BACHDEG_CY','GRADDEG_CY','TOTPOP_CY']

offset = 0
while offset <= 3000:
    chunk_df = county_layer.query(out_fields=out_fields,return_all_records=False,
                                  result_offset=offset,result_record_count=750,as_df=True)
    county_df = pd.concat([chunk_df, county_df], ignore_index=True)
    
    offset += 750
Wall time: 27.3 s
In [15]:
county_df.shape
Out[15]:
(3142, 23)

Mental Health

  • Nearly one in five people in the U.S. have some sort of mental health condition [1].
  • Adults living with serious mental illness die on average 25 years earlier than others [2].
  • Suicide is the 2nd leading cause of death for people aged 10–34 [3].

The table below shows a list of providers shortlisted as Mental healthcare providers. Provider Taxonomy codes were filtered using this reference.

Taxonomy Code Description Taxonomy Code Description
207QG0300X Family Medicine Geriatric Medicine 273R00000X Hospital Units Psychiatric Unit
103T00000X Psychologist 103TA0400X Psychologist Addiction (Substance Abuse Disorder)
103TA0700X Psychologist Adult Development & Aging 103TC0700X Psychologist Clinical
103TC2200X Psychologist Clinical Child & Adolescent 103TB0200X Psychologist Cognitive & Behavioral
103TC1900X Psychologist Counseling 103TE1000X Psychologist Educational
103TE1100X Psychologist Exercise & Sports 103TF0000X Psychologist Family
103TF0200X Psychologist Forensic 103TP2701X Psychologist Group Psychotherapy
103TH0004X Psychologist Health 103TH0100X Psychologist Health Service
103TM1700X Psychologist Men & Masculinity 103TM1800X Psychologist Mental Retardation
103TP0016X Psychologist Prescribing (Medical) 103TP0814X Psychologist Psychoanalysis
103TP2700X Psychologist Psychotherapy 103TR0400X Psychologist Rehabilitation
103TS0200X Psychologist School 103TW0100X Psychologist Women
106E00000X Assistant Behavior Analyst 106S00000X Technician
103TC0700X Psychologist Clinical 2084A0401X Physicians Psychiatry & Neurology
2084P0802X Physicians Addiction Psychiatry 2084B0002X Physicians Bariatric Medicine
2084P0804X Physicians Child & Adolescent Psychiatry 2084N0600X Physicians Clinical Neurophysiology
2084D0003X Physicians Diagnostic Neuroimaging 2084F0202X Physicians Forensic Psychiatry
2084P0805X Physicians Geriatric Psychiatry 2084H0002X Physicians Hospice & Palliative Medicine
2084P0005X Physicians Neurodevelopmental Disabilities 2084N0400X Physicians Neurology
2084N0402X Physicians Child Neurology 2084N0008X Physicians Neuromuscular Medicine
2084P2900X Physicians Pain Medicine 2084P0800X Physicians Psychiatry
2084P0015X Physicians Psychosomatic Medicine 2084S0012X Physicians Sleep Medicine
2084S0010X Physicians Sports Medicine 2084V0102X Physicians Vascular Neurology
364SA2200X Nurse/Assiatant Adult Health 364SP0808X Nurse/Assiatant Psychiatric/Mental Health
364SP0809X Nurse/Assiatant Mental Health Adult 364SP0807X Nurse/Assiatant Mental Health Child & Adolescent
364SP0810X Nurse/Assiatant Mental Health Child & Family 364SP0811X Nurse/Assiatant Mental Health Chronically Ill
364SP0812X Nurse/Assiatant Mental Health Community 364SP0813X Nurse/Assiatant Mental Health Geropsychiatric
283Q00000X Hospitals Psychiatric Hospital 261QM0801X Ambulatory Health Care Facilities/Clinic/Center Mental Health
2084P0800X Physician Psychiatry

Mental Healthcare Providers - Heat Map

Let's explore the distribution of mental health providers in the US using a heatmap.

In [29]:
# Create a map
mental_map = gis.map('USA', 4)
mental_map
Out[29]:

This map paints a grim picture of the availability of mental healthcare providers. There are states in every region with vast areas of no mental health providers or very few providers. To list, some of these states include:

  • Midwest: The Dakotas, Montana, Wyoming, Colorado, Nebraska, Kansas
  • South: Texas, New Mexico, Kansas, Louisiana, Mississippi, Alabama
  • West: Nevada, Arizona, Oregon, Idaho, Alaska
  • East: Maine, Vermont, Michigan
In [60]:
# Add provider data to map
renderer = {"renderer": "autocast", #This tells python to use JS autocasting
            "type": "heatmap",
            "blurRadius":1,  # changes the size of the clusters
            "maxPixelIntensity":2,
            "minPixelIntensity":0,
            "field":None}
renderer["colorStops"] = [{"ratio":0,"color":[63, 40, 102, 0]},
                          {"ratio":0.25,"color":[167,97,170,179]},
                          {"ratio":0.50,"color":"#7b3ce9"},
                          {"ratio":0.75,"color":[222,102,0,179]},
                          {"ratio":1,"color":[244,204,0,179]}]
mental_map.add_layer(provider_data_layer,
               { "type": "FeatureLayer",
                 "renderer": renderer,
                "definition_expression" : "user_taxonomy_code_1 in ('2084P0800X','207QG0300X','273R00000X','103T00000X','103TA0400X','103TA0700X','103TC0700X','103TC2200X','103TB0200X','103TC1900X','103TE1000X','103TE1100X','103TF0000X','103TF0200X','103TP2701X','103TH0004X','103TH0100X','103TM1700X','103TM1800X','103TP0016X','103TP0814X','103TP2700X','103TR0400X','103TS0200X','103TW0100X','106E00000X','106S00000X','2084A0401X','2084P0802X','2084B0002X','2084P0804X','2084N0600X','2084D0003X','2084F0202X','2084P0805X','2084H0002X','2084P0005X','2084N0400X','2084N0402X','2084N0008X','2084P2900X','2084P0015X','2084S0012X','2084S0010X','2084V0102X','364SP0808X','364SP0809X','364SP0807X','364SP0810X','364SP0811X','364SP0812X','364SP0813X','283Q00000X','261QM0801X')"
               })
In [61]:
# Add Legend
mental_map.legend = True
In [62]:
# Save as a web map item
mental_map_item = mental_map.save({'title':'mental_heatmap',
                                    'snippet':'Heat map',
                                    'tags':'Mental Health',
                                    'typeKeywords':'WebMap'})
mental_map_item
Out[62]:
mental_heatmap
Heat mapWeb Map by portaladmin
Last Modified: August 11, 2020
0 comments, 0 views

Population to Providers Ratio by State

Let's find out the ratio of population to mental healthcare providers to understand which states have the least number of providers.

DataFrame for Mental Healthcare providers by state

We will use provider_data_layer to subset mental healthcare providers.

In [25]:
%%time

# Get provider data for mental healthcare providers only
taxonomy_codes = ['2084P0800X','207QG0300X','273R00000X','103T00000X','103TA0400X','103TA0700X',
                  '103TC0700X','103TC2200X','103TB0200X','103TC1900X','103TE1000X','103TE1100X',
                  '103TF0000X','103TF0200X','103TP2701X','103TH0004X','103TH0100X','103TM1700X',
                  '103TM1800X','103TP0016X','103TP0814X','103TP2700X','103TR0400X','103TS0200X',
                  '103TW0100X','106E00000X','106S00000X','2084A0401X','2084P0802X','2084B0002X',
                  '2084P0804X','2084N0600X','2084D0003X','2084F0202X','2084P0805X','2084H0002X',
                  '2084P0005X','2084N0400X','2084N0402X','2084N0008X','2084P2900X','2084P0015X',
                  '2084S0012X','2084S0010X','2084V0102X','364SP0808X','364SP0809X','364SP0807X',
                  '364SP0810X','364SP0811X','364SP0812X','364SP0813X','283Q00000X','261QM0801X']

out_fields = ['user_npi','user_entity_type','user_provider_gender','user_taxonomy_code_1',
              'user_full_address','postal','city','subregion','region','regionabbr']

where_str = "user_taxonomy_code_1 in ({})".format(",".join(f"'{x}'" for x in taxonomy_codes))
out_str = "{}".format(",".join(f"{x}" for x in out_fields))

mental_df = provider_data_layer.query(where=where_str, 
                                      out_fields=out_str, as_df=True)
Wall time: 20min 35s
In [26]:
# Create dataframe of mental healthcare provider counts by state
mental_count_df = pd.DataFrame(mental_df['regionabbr'].value_counts().reset_index().values, 
                               columns=['regionabbr','Provider_Count'])
In [27]:
# Plot mental healthcare Providers by State

plt.figure(figsize=(25,12))
sns.barplot(mental_count_df['regionabbr'].iloc[:-19], 
            mental_count_df['Provider_Count'].iloc[:-19],
            color='lightcoral')
plt.title('Mental Healthcare Providers by State', fontsize=22)
plt.xlabel('States', fontsize=18)
plt.ylabel('Provider Count', fontsize=18);

DataFrame for Population by State

We will use the population density layer at state level to create this dataframe

In [28]:
# State population dataframe
state_layer = popdensity.layers[43]
state_df = state_layer.query(out_fields='STATE_NAME,ST_ABBREV,TOTPOP_CY', as_df=True)

state_df.head()
Out[28]:
OBJECTID STATE_NAME ST_ABBREV TOTPOP_CY SHAPE
0 1 Alabama AL 5028316 {"rings": [[[-9804885.6072097, 3535086.3690447...
1 2 Alaska AK 760206 {"rings": [[[-19937055.7493677, 6661392.287859...
2 3 Arizona AZ 7332436 {"rings": [[[-12138858.697798947, 4438979.1094...
3 4 Arkansas AR 3107082 {"rings": [[[-9989042.442704434, 4300705.03222...
4 5 California CA 39648525 {"rings": [[[-13198542.771785328, 3897777.9747...

Merge Provider Count and Population Dataframes

In [29]:
# Merge provider count and population at state level
# Merge with State data on left to preserve 'polygon' geometry
state_mental_df = pd.merge(state_df,mental_count_df,right_on='regionabbr',
                           left_on='ST_ABBREV',how='inner')
In [30]:
state_mental_df.head()
Out[30]:
OBJECTID STATE_NAME ST_ABBREV TOTPOP_CY SHAPE regionabbr Provider_Count
0 1 Alabama AL 5028316 {'rings': [[[-9804885.6072097, 3535086.3690447... AL 2030
1 2 Alaska AK 760206 {'rings': [[[-19937055.7493677, 6661392.287859... AK 678
2 3 Arizona AZ 7332436 {'rings': [[[-12138858.697798947, 4438979.1094... AZ 5237
3 4 Arkansas AR 3107082 {'rings': [[[-9989042.442704434, 4300705.03222... AR 1507
4 5 California CA 39648525 {'rings': [[[-13198542.771785328, 3897777.9747... CA 51773
In [31]:
# Create new columns that shows people per provider
state_mental_df['people_per_prov'] = state_mental_df['TOTPOP_CY']/state_mental_df['Provider_Count']
In [32]:
# Arrange dataframe by people_per_prov descending
state_mental_df = state_mental_df.sort_values(by=['people_per_prov'], ascending=False)
state_mental_df.head()
Out[32]:
OBJECTID STATE_NAME ST_ABBREV TOTPOP_CY SHAPE regionabbr Provider_Count people_per_prov
24 25 Mississippi MS 3056560 {'rings': [[[-9847927.844922382, 3530344.94964... MS 1108 2758.63
0 1 Alabama AL 5028316 {'rings': [[[-9804885.6072097, 3535086.3690447... AL 2030 2477
12 13 Idaho ID 1856821 {'rings': [[[-12918525.160544802, 6275004.9437... ID 790 2350.41
15 16 Iowa IA 3238386 {'rings': [[[-10154308.583250534, 5388473.6788... IA 1396 2319.76
43 44 Texas TX 29806340 {'rings': [[[-10518119.492156893, 3980279.4459... TX 13621 2188.26
In [33]:
# Check geometry type
state_mental_df.spatial.geometry_type
Out[33]:
['polygon']

Plot Shortage

Let's plot the number of people per mental healthcare provider accross all states on a map.

In [39]:
mental_shortage_map = gis.map('USA', zoomlevel=4)
mental_shortage_map
Out[39]:

From this map we can see that, Mississippi is the worst with highest no. of people per mental healthcare provider followed by Alabama, Idaho, Texas, Arkansas and Iowa.

In [63]:
mental_shortage_map = gis.map('USA', zoomlevel=4)
mental_shortage_map
Define Renderer
In [64]:
# Define Renderer
esriTest = {"renderer": { #This tells python to use JS autocasting
                 "type": "classBreaks",  
                 "field":"people_per_prov",
                 "transparency":.5,
                 "minValue":1}}
In [65]:
# Define Manual Class breaks
esriTest['renderer']["classBreakInfos"] = [{
  "classMaxValue": 1000.00,
  "label": "0 - 1000.00",
  "description": "0 - 1000.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [255,247,236,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}, {
  "classMaxValue": 1500.00,
  "label": "1000.001 - 1500.00",
  "description": "1000.001 - 1500.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [253,220,174,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}, {
  "classMaxValue": 2000.00,
  "label": "1500.001 - 2000.00",
  "description": "1500.001 - 2000.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [252,177,123,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}, {
  "classMaxValue": 2500.00,
  "label": "2000.001 - 2500.00",
  "description": "2000.001 - 2500.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [241,109,75,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}, {
  "classMaxValue": 3000.00,
  "label": "2500.001 - 3000.00",
  "description": "2500.001 - 3000.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [200,28,18,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}]
Plot Map
In [66]:
# Plot Map using defined Renderer
state_mental_df.spatial.plot(map_widget=mental_shortage_map, renderer=esriTest['renderer'])
Out[66]:
True
In [67]:
# Add Legend
mental_shortage_map.legend=True
In [68]:
# Save as a Web Map Item
mental_shortage_item = mental_shortage_map.save({'title':'mental_shortage_map',
                                                'snippet':'shortage map',
                                                'tags':'Mental Health',
                                                'typeKeywords':'WebMap'})
mental_shortage_item
Out[68]:
mental_shortage_map
shortage mapWeb Map by portaladmin
Last Modified: August 11, 2020
0 comments, 0 views

Plot Population per Mental Healthcare Provider

In [40]:
# Plot No. of People per Mental Heathcare Provider by State

plt.figure(figsize=(25,12))
sns.barplot(state_mental_df['regionabbr'], state_mental_df['people_per_prov'], color='lightcoral')
plt.title('No. of People per Mental Heathcare Provider by State', fontsize=22)
plt.xlabel('States', fontsize=18)
plt.ylabel('No. of People', fontsize=18)
plt.xticks(fontsize=15)
plt.yticks(fontsize=15);

On average, there is 1 mental healthcare provider for ~2754 people in Mississippi compared to 1 mental healthcare provider for ~364 people per provider in DC. The difference is drastic.

Exploring Mississippi

Mississippi has the highest number of people per mental healthcare provider. Let's explore Mississippi to find out which counties have the lowest number of providers.

DataFrame for Population in Mississippi

In [41]:
# County population df
county_layer = popdensity.layers[46]
MS_pop_df = county_layer.query(where="ST_ABBREV='MS'",
                               out_fields='ST_ABBREV,NAME,TOTPOP_CY', as_df=True)

MS_pop_df.head()
Out[41]:
OBJECTID ST_ABBREV NAME TOTPOP_CY SHAPE
0 1402 MS Adams County 29894 {"rings": [[[-10165458.1208, 3730508.161499999...
1 1403 MS Alcorn County 37214 {"rings": [[[-9838473.1467, 4163284.5766000003...
2 1404 MS Amite County 12759 {"rings": [[[-10079714.5057, 3632712.780100003...
3 1405 MS Attala County 19625 {"rings": [[[-9957952.356, 3933319.7764], [-99...
4 1406 MS Benton County 8961 {"rings": [[[-9917285.1197, 4109417.663400002]...

DataFrame for Mental Healthcare Providers in Mississippi by County

In [42]:
# Get provider data for obgyn providers only
mental_MS_df = mental_df[mental_df['regionabbr']=='MS']
mental_MS_df.head()
mental_MS_df.shape
Out[42]:
(1108, 12)
In [43]:
# Create dataframe of provider counts by county
mental_MScounty_df = pd.DataFrame(mental_MS_df['subregion'].value_counts().reset_index().values,
                                  columns=['County','Provider_Count'])
mental_MScounty_df.head()
Out[43]:
County Provider_Count
0 Hinds County 274
1 Harrison County 143
2 Rankin County 81
3 Lamar County 76
4 Lauderdale County 52

Merge Provider Count and Population Dataframes for MS

In [44]:
# Merge provider count and women data at county level for ND
county_mental_df = pd.merge(MS_pop_df,mental_MScounty_df,
                            left_on='NAME', right_on='County',how='left')
In [45]:
county_mental_df.head()
Out[45]:
OBJECTID ST_ABBREV NAME TOTPOP_CY SHAPE County Provider_Count
0 1402 MS Adams County 29894 {'rings': [[[-10165458.1208, 3730508.161499999... Adams County 7
1 1403 MS Alcorn County 37214 {'rings': [[[-9838473.1467, 4163284.5766000003... Alcorn County 17
2 1404 MS Amite County 12759 {'rings': [[[-10079714.5057, 3632712.780100003... Amite County 1
3 1405 MS Attala County 19625 {'rings': [[[-9957952.356, 3933319.7764], [-99... Attala County 1
4 1406 MS Benton County 8961 {'rings': [[[-9917285.1197, 4109417.663400002]... Benton County 1
In [46]:
# Look at null values
county_mental_df[county_mental_df['Provider_Count'].isnull()]
Out[46]:
OBJECTID ST_ABBREV NAME TOTPOP_CY SHAPE County Provider_Count
6 1408 MS Calhoun County 14877 {'rings': [[[-9934761.9179, 4050465.1162], [-9... NaN NaN
7 1409 MS Carroll County 10936 {'rings': [[[-9995030.8748, 3985476.520499997]... NaN NaN
20 1422 MS Greene County 13482 {'rings': [[[-9844205.9891, 3647601.8276000023... NaN NaN
27 1429 MS Issaquena County 1363 {'rings': [[[-10120537.7007, 3896325.656000003... NaN NaN
28 1430 MS Itawamba County 23874 {'rings': [[[-9832760.787, 4091219.4390999973]... NaN NaN
34 1436 MS Kemper County 11229 {'rings': [[[-9834841.7935, 3885894.5637999997... NaN NaN
39 1441 MS Leake County 22319 {'rings': [[[-9942963.7433, 3886235.684799999]... NaN NaN
48 1450 MS Montgomery County 10553 {'rings': [[[-9963859.4135, 3985593.969899997]... NaN NaN
51 1453 MS Noxubee County 11462 {'rings': [[[-9830004.6277, 3933636.4505999982... NaN NaN
62 1464 MS Sharkey County 4806 {'rings': [[[-10096883.1994, 3908092.367399998... NaN NaN
67 1469 MS Tallahatchie County 14241 {'rings': [[[-10011126.2815, 4050885.2654], [-... NaN NaN
70 1472 MS Tishomingo County 19823 {'rings': [[[-9818385.2662, 4163288.041699998]... NaN NaN
71 1473 MS Tunica County 11035 {'rings': [[[-10064171.5218, 4141205.537699997... NaN NaN
73 1475 MS Walthall County 15166 {'rings': [[[-10023258.8259, 3676464.274899997... NaN NaN
76 1478 MS Wayne County 20559 {'rings': [[[-9847815.2452, 3723726.261], [-98... NaN NaN
77 1479 MS Webster County 10099 {'rings': [[[-9909506.6702, 3970155.0966000035... NaN NaN
78 1480 MS Wilkinson County 9583 {'rings': [[[-10147678.8396, 3677856.414300002... NaN NaN
80 1482 MS Yalobusha County 12806 {'rings': [[[-9964179.5684, 4050586.4976999983... NaN NaN

We can see that,

  • 18 counties in Mississippi do not have any mental healthcare provider.

We will replace NaN values for Provider Count in these counties with 1 to plot them on the map to see which counties have highest number of people per provider.

In [47]:
county_mental_df['Provider_Count'].replace(np.nan,1,inplace=True)
In [48]:
# Create new columns that shows Mental Healthcare provider by population
county_mental_df['people_per_prov'] = county_mental_df['TOTPOP_CY']/county_mental_df['Provider_Count']

Plot Shortage

Let's plot the number of people per provider for all counties in Mississippi on a map.

In [56]:
mental_countyshortage_map = gis.map('Mississippi, USA', zoomlevel=7)
mental_countyshortage_map.layout.height="650px"
mental_countyshortage_map
Out[56]:

We can see that,

  • Marshall County seems to be the worst with 1 provider for 37137 people. Some other counties with high population per provider are Pontotoc, Neshoba, Scott and Union.
Define Renderer
In [70]:
# Define Renderer
mentalCountyTest = {"renderer": { #This tells python to use JS autocasting
                 "type": "classBreaks",  
                 "field":"people_per_prov",
                 "transparency":.5,
                 "minValue":1}}
In [71]:
# Define Manual Class breaks
mentalCountyTest['renderer']["classBreakInfos"] = [{
  "classMaxValue": 7500.00,
  "label": "0 - 7500.00",
  "description": "0 - 7500.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [255,247,236,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}, {
  "classMaxValue": 15000.00,
  "label": "7500.001 - 15000.00",
  "description": "7500.001 - 15000.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [253,220,174,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}, {
  "classMaxValue": 22500.00,
  "label": "15000.001 - 22500.00",
  "description": "15000.001 - 22500.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [252,177,123,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}, {
  "classMaxValue": 30000.00,
  "label": "22500.001 - 30000.00",
  "description": "22500.001 - 30000.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [241,109,75,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}, {
  "classMaxValue": 37500.00,
  "label": "30000.001 - 37500.00",
  "description": "30000.001 - 37500.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [200,28,18,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}]
Plot Map
In [72]:
# Plot Map using defined Renderer
county_mental_df.spatial.plot(map_widget=mental_countyshortage_map, renderer=mentalCountyTest['renderer'])
Out[72]:
True
In [73]:
# Plot legend
mental_countyshortage_map.legend = True
In [74]:
# Save as a Web Map Item
mental_cs_item = mental_countyshortage_map.save({'title':'mental_countyshortage_map',
                                                'snippet':'shortage map',
                                                'tags':'Mental Health',
                                                'typeKeywords':'WebMap'})
mental_cs_item
Out[74]:
mental_countyshortage_map
shortage mapWeb Map by portaladmin
Last Modified: August 11, 2020
0 comments, 0 views

The table below shows top 10 counties with highest number of people per mental healthcare provider

In [57]:
# Counties with highest number of people per mental healthcare provider
county_ppp = county_mental_df.sort_values(by='people_per_prov', ascending=False)[['NAME','TOTPOP_CY','Provider_Count','people_per_prov']]
county_ppp.head(10)
Out[57]:
NAME TOTPOP_CY Provider_Count people_per_prov
46 Marshall County 36641 1 36641.0
57 Pontotoc County 32568 1 32568.0
49 Neshoba County 29284 1 29284.0
72 Union County 28332 1 28332.0
61 Scott County 28263 1 28263.0
28 Itawamba County 23874 1 23874.0
39 Leake County 22319 1 22319.0
76 Wayne County 20559 1 20559.0
70 Tishomingo County 19823 1 19823.0
3 Attala County 19625 1 19625.0
Marshall County Infographic

To visualize high level key facts about a geography and gain greater insights, various infographics can be created using ArcGIS Business Analyst. Learn more about ArcGIS Business Analyst.

The image below shows an infographic of key facts for Marshall County, MS.

To summarize, Mississippi has the highest number of people per Mental Healthcare Provider. Also:

  1. There were ~2754 people per mental healthcare provider in Mississippi compared to ~364 people per provider in DC.
  2. 18 counties in Mississippi do not have any mental healthcare provider.
  3. Marshall County seems to be the worst with 1 provider for 37137 people. Some other counties with high population per provider are Pontotoc, Neshoba, Scott and Union.

Women's Health

  • Half of U.S. counties lack a single OB-GYN and women’s lives are endangered by long treks for much-needed care [4].
  • With a severe shortage of obstetrician/gynecologists forecasted, some metropolitan areas are prone to crisis-level conditions [5].
  • By 2020, there will be a shortage of up to 8,800 OB-GYNs, and the shortage may grow upto 22,000 by 2050 [6].

The table below shows a list of providers shortlisted as OB-GYN providers. Provider Taxonomy codes were filtered for OB-GYN providers using this reference.

Taxonomy Code Description
207V00000X Obstetrics & Gynecology
207VC0200X Critical Care Medicine
207VF0040X Female Pelvic Medicine and Reconstructive Surgery
207VX0201X Gynecologic Oncology
207VG0400X Gynecology
207VH0002X Hospice and Palliative Medicine
207VM0101X Maternal & Fetal Medicine
207VB0002X Obesity Medicine
207VX0000X Obstetrics
207VE0102X Reproductive Endocrinology
363LX0001X Nurse Practitioner-Obstetrics & Gynecology
163WR1000X Registered Nurse-Reproductive Endocrinology/Infertility
163WW0101X Registered Nurse-Women's Health Care, Ambulatory
282NW0100X General Acute Care Hospital - Women

OBGYN Providers - Heat Map

Let's explore the distribution of OB-GYN providers in US using a heatmap.

In [79]:
# Create Map
women_map = gis.map('USA', 4)
women_map
Out[79]:

This map paints a grim picture of the availability of OBGYN healthcare providers. We can see vast areas in Midwest and West with NO or very few OBGYN providers.

In [76]:
# Define renderer and add provider data for OBGYN providers
renderer = {"renderer": "autocast", #This tells python to use JS autocasting
            "type": "heatmap",
            "blurRadius":1,  # changes the size of the clusters
            "maxPixelIntensity":2,
            "minPixelIntensity":0,
            "field":None}
renderer["colorStops"] = [{"ratio":0,"color":[63, 40, 102, 0]},
                          {"ratio":0.25,"color":[167,97,170,179]},
                          {"ratio":0.50,"color":"#7b3ce9"},
                          {"ratio":0.75,"color":[222,102,0,179]},
                          {"ratio":1,"color":[244,204,0,179]}]
women_map.add_layer(provider_data_layer,
               { "type": "FeatureLayer",
                 "renderer": renderer,
                "definition_expression" : "user_taxonomy_code_1 in ('207V00000X','207VC0200X','207VF0040X','207VX0201X','207VG0400X','207VH0002X','207VM0101X','207VB0002X','207VX0000X','207VE0102X','363LX0001X','163WR1000X','163WW0101X','282NW0100X')"
#                 "definition_expression" : "user_taxonomy_code_1 = '207VC0200X' or user_taxonomy_code_1 = '207V00000X'"
                })
In [77]:
# Add Legend
women_map.legend = True
In [78]:
# Save as a web map item
women_map_item = women_map.save({'title':'women_heatmap',
                                    'snippet':'Heat map',
                                    'tags':'Women Health',
                                    'typeKeywords':'WebMap'})
women_map_item
Out[78]:
women_heatmap
Heat mapWeb Map by portaladmin
Last Modified: August 11, 2020
0 comments, 0 views

Mothers to Providers Ratio by State

In this section our goal is to identify which states have the highest mother's to providers ratio. Let's get data from the American Community Survey (ACS) about fertility in past 12 months by age of mother using ACS_Fertility_by_Age_Boundaries layer at State, County and Tract level. We will merge OBGYN providers data with Fertility data to achieve this.

Get the layers

In [80]:
fertility_item = gis.content.get('7995cd11748249fb8e23c4a5ad901dde')
fertility_item
Out[80]:
ACS Fertility in Past 12 Months by Age Variables - Boundaries
This layer contains the most current release of data from the American Community Survey (ACS) about fertility in past 12 months by age of mother. These are 5-year estimates shown by tract, county, and state boundaries. Feature Layer Collection by esri_livingatlas
Last Modified: May 13, 2020
0 comments, 0 views
In [81]:
fertility_item.layers
Out[81]:
[<FeatureLayer url:"https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/ACS_Fertility_by_Age_Boundaries/FeatureServer/0">,
 <FeatureLayer url:"https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/ACS_Fertility_by_Age_Boundaries/FeatureServer/1">,
 <FeatureLayer url:"https://services.arcgis.com/P3ePLMYs2RVChkJx/arcgis/rest/services/ACS_Fertility_by_Age_Boundaries/FeatureServer/2">]
In [82]:
# Define layers for State, County and Tract (Percent of women 15 to 50 who had a birth in the past 12 months)
fertility_state = fertility_item.layers[0]
fertility_county = fertility_item.layers[1]
fertility_tract = fertility_item.layers[2]

DataFrame for women population by State

We will use the fertility layer at state level to create this dataframe

In [84]:
# State population dataframe
fertility_df = fertility_state.query(where="B13016_001E>1", as_df=True)
fertility_df.head()
Out[84]:
OBJECTID STATENS GEOID STUSPS NAME ALAND AWATER B13016_001E B13016_001M B13016_002E B13016_002M B13016_003E B13016_003M B13016_004E B13016_004M B13016_005E B13016_005M B13016_006E B13016_006M B13016_007E B13016_007M B13016_008E B13016_008M B13016_009E B13016_009M B13016_010E B13016_010M B13016_011E B13016_011M B13016_012E B13016_012M B13016_013E B13016_013M B13016_014E B13016_014M B13016_015E B13016_015M B13016_016E B13016_016M B13016_017E B13016_017M B13016_calc_pctBirthsE B13016_calc_pctBirthsM B13016_calc_num15to19E B13016_calc_num15to19M B13016_calc_pct15to19E B13016_calc_pct15to19M B13016_calc_num20to24E B13016_calc_num20to24M B13016_calc_pct20to24E B13016_calc_pct20to24M B13016_calc_num25to29E B13016_calc_num25to29M B13016_calc_pct25to29E B13016_calc_pct25to29M B13016_calc_num30to34E B13016_calc_num30to34M B13016_calc_pct30to34E B13016_calc_pct30to34M B13016_calc_num35to39E B13016_calc_num35to39M B13016_calc_pct35to39E B13016_calc_pct35to39M B13016_calc_num40to44E B13016_calc_num40to44M B13016_calc_pct40to44E B13016_calc_pct40to44M B13016_calc_num45to50E B13016_calc_num45to50M B13016_calc_pct45to50E B13016_calc_pct45to50M Shape__Area Shape__Length SHAPE
0 1 01779775 01 AL Alabama 131174048583 4593327154 1148528 1509 60165 1702 2589 441 14746 793 19098 1127 14125 810 6813 626 1958 470 836 220 1088363 2107 157525 1206 150435 1182 146461 1338 141576 1091 146885 2355 152368 2244 193113 1484 5.2 0.147986 160114 1284 1.6 0.275123 165181 1423 8.9 0.473879 165559 1749 11.5 0.669727 155701 1359 9.1 0.514167 153698 2437 4.4 0.401182 154326 2293 1.3 0.303966 193949 1500 0.4 0.113383 1.901226e+11 2.312416e+06 {"rings": [[[-9805779.6231, 3536997.5778], [-9...
1 2 01785533 02 AK Alaska 1478839695958 245481577452 173058 528 10777 673 505 165 2225 253 3105 378 2921 345 1512 295 396 124 113 58 162281 896 21785 316 21903 344 25874 490 23998 394 21932 588 20641 618 26148 438 6.2 0.388132 22290 356 2.3 0.739357 24128 427 9.2 1.035796 28979 619 10.7 1.284157 26919 524 10.9 1.264097 23444 658 6.4 1.245230 21037 630 1.9 0.586736 26261 442 0.4 0.220741 8.208934e+12 6.399688e+07 {"rings": [[[-19937057.4487, 6661392.0514], [-...
2 3 01779777 04 AZ Arizona 294198551143 1027337603 1598426 1260 87762 2110 3435 460 17844 1040 24962 1126 22224 1223 13423 836 3793 412 2081 349 1510664 2549 225915 906 215683 1230 209585 1152 199483 1217 202196 2727 204064 2534 253738 1365 5.5 0.131806 229350 1016 1.5 0.200457 233527 1611 7.6 0.442214 234547 1611 10.6 0.474476 221707 1725 10.0 0.546088 215619 2852 6.2 0.378876 207857 2567 1.8 0.196928 255819 1409 0.8 0.136351 4.340580e+11 2.904950e+06 {"rings": [[[-12138854.2127, 4438965.0002], [-...
3 4 00068085 05 AR Arkansas 134768872727 2962859592 690046 1218 40212 1649 2509 364 10514 749 12755 850 8907 646 4078 511 1032 229 417 124 649834 2039 95475 960 89255 1069 87751 976 85585 881 91184 1830 88977 1731 111607 1242 5.8 0.238563 97984 1027 2.6 0.370518 99769 1305 10.5 0.737971 100506 1294 12.7 0.829787 94492 1092 9.4 0.674921 95262 1900 4.3 0.529577 90009 1746 1.1 0.253445 112024 1248 0.4 0.110613 2.053261e+11 2.656504e+06 {"rings": [[[-9989043.0946, 4300705.9503], [-9...
4 5 01779778 06 CA California 403503931312 20463871877 9632116 4019 473280 5293 13945 824 70111 2050 118935 2794 139069 2870 90663 1976 29631 1189 10926 665 9158836 6208 1249040 1197 1287203 2032 1351804 2786 1255168 2815 1218690 5003 1239157 4885 1557774 4047 4.9 0.054885 1262985 1453 1.1 0.065230 1357314 2886 5.2 0.150634 1470739 3946 8.1 0.188729 1394237 4020 10.0 0.203828 1309353 5379 6.9 0.148209 1268788 5028 2.3 0.093253 1568700 4101 0.7 0.042353 6.500880e+11 5.274812e+06 {"rings": [[[-13198544.1255, 3897778.6019], [-...

DataFrame for OB-GYN providers by state

We will use provider_data_layer to subset OB-GYN providers

In [85]:
%%time

# Get provider data for obgyn providers only
taxonomy_codes = ['207V00000X','207VC0200X','207VF0040X','207VX0201X','207VG0400X',
                  '207VH0002X','207VM0101X','207VB0002X','207VX0000X','207VE0102X',
                  '363LX0001X','163WR1000X','163WW0101X','282NW0100X']

out_fields = ['user_npi','user_entity_type','user_provider_gender','user_taxonomy_code_1',
              'user_full_address','postal','city','subregion','region','regionabbr']

where_str = "user_taxonomy_code_1 in ({})".format(",".join(f"'{x}'" for x in taxonomy_codes))
out_str = "{}".format(",".join(f"{x}" for x in out_fields))

obgyn_df = provider_data_layer.query(where=where_str,
                                     out_fields=out_str, as_df=True)

obgyn_df.head()
Wall time: 1min 56s
Out[85]:
SHAPE city objectid postal region regionabbr subregion user_entity_type user_full_address user_npi user_provider_gender user_taxonomy_code_1
0 {"x": -8198939.4947, "y": 5358600.421800002, "... Glens Falls 260 12801 New York NY Warren County Individual 45 HUDSON AVE, , GLENS FALLS, NY 128014313 1.659501e+09 F 207V00000X
1 {"x": -8223678.4574, "y": 5061654.650899999, "... Putnam Valley 393 10579 New York NY Putnam County Organization 11 PEEKSKILL HOLLOW RD # 204, , PUTNAM VALLEY,... 1.013428e+09 None 207VX0201X
2 {"x": -8232526.3719999995, "y": 4962867.355700... Brooklyn 455 11225 New York NY Kings County Individual 249 EMPIRE BLVD, , BROOKLYN, NY 112253402 1.841430e+09 M 207V00000X
3 {"x": -8603908.3159, "y": 5292933.3891, "spati... Canandaigua 574 14424 New York NY Ontario County Individual 335 PARRISH ST, , CANANDAIGUA, NY 144241728 1.235132e+09 M 207V00000X
4 {"x": -8193198.8078000005, "y": 4973209.799699... Garden City 734 11530 New York NY Nassau County Organization 901 STEWART AVE, , GARDEN CITY, NY 115304893 1.346479e+09 None 207VM0101X
In [86]:
# Create dataframe of obgyn provider counts by state
obgyn_count_df = pd.DataFrame(obgyn_df['regionabbr'].value_counts().reset_index().values, 
                              columns=['regionabbr','Provider_Count'])
In [87]:
# Plot OB-GYN Providers by State

plt.figure(figsize=(25,12))
sns.barplot(obgyn_count_df['regionabbr'].iloc[:-8], 
            obgyn_count_df['Provider_Count'].iloc[:-8], 
            color='lightcoral')
plt.title('OB-GYN Providers by State', fontsize=22)
plt.xlabel('States', fontsize=18)
plt.ylabel('Provider Count', fontsize=18);

Merge Provider Count and Population Dataframes

In [88]:
# Merge provider count and women_df at state level
state_obgyn_df = pd.merge(fertility_df,obgyn_count_df,right_on='regionabbr', 
                          left_on='STUSPS',how='inner')

Create 2 new attributes: women_per_prov and mother_per_prov to capture the number of women and mothers per OB-GYN provider

In [89]:
# Create new columns that shows provider by women pop
state_obgyn_df['women_per_prov'] = state_obgyn_df['B13016_001E']/state_obgyn_df['Provider_Count']

# Create new columns that shows provider by mother pop
state_obgyn_df['mother_per_prov'] = state_obgyn_df['B13016_002E']/state_obgyn_df['Provider_Count']
In [90]:
# Arrange dataframe by mother_per_prov descending
state_obgyn_df = state_obgyn_df.sort_values(by=['mother_per_prov'], ascending=False)
In [91]:
# Subset dataframe for specific columns
state_obgyn_df = state_obgyn_df.loc[:,['OBJECTID','SHAPE','regionabbr','Provider_Count',
                                       'women_per_prov','mother_per_prov']]
state_obgyn_df.head()
Out[91]:
OBJECTID SHAPE regionabbr Provider_Count women_per_prov mother_per_prov
34 35 {'rings': [[[-10823452.8858, 6274958.149], [-1... ND 102 1661.49 111.353
44 45 {'rings': [[[-12139394.6091, 5012444.5138], [-... UT 526 1441.92 99.9943
12 13 {'rings': [[[-12918526.7316, 6275006.0277], [-... ID 250 1535.15 93.66
3 4 {'rings': [[[-9989043.0946, 4300705.9503], [-9... AR 440 1568.29 91.3909
41 42 {'rings': [[[-10749419.778, 5769979.5139], [-1... SD 137 1368.78 91.0584
In [92]:
# Check geometry type
state_obgyn_df.spatial.geometry_type
Out[92]:
['polygon']

Plot Shortage

Let's plot the number of Mothers per OBGYN provider accross all states on a map.

In [113]:
obgyn_shortage_map = gis.map('USA', zoomlevel=4)
obgyn_shortage_map
Out[113]:

From this map, we can see that number of women (ages 15 to 50) who had a birth in the past 12 months per OBGYN provider is high in Utah, North Dakota, South Dakota and Arkansas.

State No. of Mothers per OBGYN provider
ND 112
UT 100
AR 93
SD 93
ID 92
Define Renderer
In [94]:
# Define Renderer
stateObgynTest = {"renderer": { #This tells python to use JS autocasting
                 "type": "classBreaks",  
                 "field":"mother_per_prov",
                 "transparency":.5,
                 "minValue":1}}
In [95]:
# Define Manual Class breaks
stateObgynTest['renderer']["classBreakInfos"] = [{
  "classMaxValue": 23.00,
  "label": "0 - 23.00",
  "description": "0 - 23.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [255,247,236,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}, {
  "classMaxValue": 46.00,
  "label": "23.001 - 46.00",
  "description": "23.001 - 46.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [253,220,174,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}, {
  "classMaxValue": 69.00,
  "label": "46.001 - 69.00",
  "description": "46.001 - 69.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [252,177,123,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}, {
  "classMaxValue": 92.00,
  "label": "69.001 - 92.00",
  "description": "69.001 - 92.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [241,109,75,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}, {
  "classMaxValue": 115.00,
  "label": "92.001 - 115.00",
  "description": "92.001 - 115.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [200,28,18,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}]
Plot Map
In [96]:
# Plot Map using defined Renderer
state_obgyn_df.spatial.plot(map_widget=obgyn_shortage_map, renderer=stateObgynTest['renderer'])
Out[96]:
True
In [97]:
# Add Legend
obgyn_shortage_map.legend=True

Plot Mothers per OBGYN Provider

In [114]:
# Plot No. of Mothers (age 15 to 50) per OB-GYN Provider by State

plt.figure(figsize=(25,12))
sns.barplot(state_obgyn_df['regionabbr'], state_obgyn_df['mother_per_prov'],color='lightcoral')
plt.title('No. of Mothers (age 15 to 50) per OB-GYN Provider by State', fontsize=22)
plt.xlabel('States', fontsize=18)
plt.ylabel('No. of Mothers (age 15 to 50)', fontsize=18)
plt.xticks(fontsize=15)
plt.yticks(fontsize=15);

On average, there were ~112 mothers and 1655 women per provider in ND compared to ~31 mothers and 662 women per provider in DC. The difference is drastic.

Exploring North Dakota

North Dakota has the highest number of mothers and women per OB-GYN provider. From the fertility map above, we also saw that percent of women (ages 15 to 50) who had a birth in the past 12 months is high in North Dakota.

Let's explore North Dakota to find out which counties have the lowest number of OBGYN providers.

DataFrame for women population in North Dakota

In [115]:
# State population df
ND_fertility_df = fertility_county.query(where="STATE='North Dakota'", as_df=True)
ND_fertility_df.head()
Out[115]:
OBJECTID COUNTYNS GEOID ALAND AWATER NAME State B13016_001E B13016_001M B13016_002E B13016_002M B13016_003E B13016_003M B13016_004E B13016_004M B13016_005E B13016_005M B13016_006E B13016_006M B13016_007E B13016_007M B13016_008E B13016_008M B13016_009E B13016_009M B13016_010E B13016_010M B13016_011E B13016_011M B13016_012E B13016_012M B13016_013E B13016_013M B13016_014E B13016_014M B13016_015E B13016_015M B13016_016E B13016_016M B13016_017E B13016_017M B13016_calc_pctBirthsE B13016_calc_pctBirthsM B13016_calc_num15to19E B13016_calc_num15to19M B13016_calc_pct15to19E B13016_calc_pct15to19M B13016_calc_num20to24E B13016_calc_num20to24M B13016_calc_pct20to24E B13016_calc_pct20to24M B13016_calc_num25to29E B13016_calc_num25to29M B13016_calc_pct25to29E B13016_calc_pct25to29M B13016_calc_num30to34E B13016_calc_num30to34M B13016_calc_pct30to34E B13016_calc_pct30to34M B13016_calc_num35to39E B13016_calc_num35to39M B13016_calc_pct35to39E B13016_calc_pct35to39M B13016_calc_num40to44E B13016_calc_num40to44M B13016_calc_pct40to44E B13016_calc_pct40to44M B13016_calc_num45to50E B13016_calc_num45to50M B13016_calc_pct45to50E B13016_calc_pct45to50M Shape__Area Shape__Length SHAPE
0 1991 01034210 38001 2557900104 2894336 Adams County North Dakota 425 50 7 10 0 9 0 9 0 9 0 9 7 10 0 9 0 9 418 55 49 25 51 23 50 22 68 23 38 24 96 29 66 28 1.6 2.352622 49 27 0.0 18.367347 51 25 0.0 17.647059 50 24 0.0 18.000000 68 25 0.0 13.235294 45 26 15.6 20.323613 96 30 0.0 9.375000 66 29 0.0 13.636364 5.324157e+09 330639.744151 {"rings": [[[-11354354.1023, 5813340.1041], [-...
1 1992 01034225 38003 3863107416 56591280 Barnes County North Dakota 2185 110 145 63 0 15 0 15 112 52 11 12 22 32 0 15 0 15 2040 124 365 63 364 87 178 63 236 12 274 77 251 58 372 60 6.6 2.876939 365 65 0.0 4.109589 364 88 0.0 4.120879 290 82 38.6 14.222107 247 17 4.5 4.848621 296 83 7.4 10.608024 251 60 0.0 5.976096 372 62 0.0 4.032258 8.403601e+09 374027.679170 {"rings": [[[-10904992.8409, 5981421.7966], [-...
2 1993 01034216 38005 3596569006 131708143 Benson County North Dakota 1359 11 118 37 12 13 51 22 23 13 18 11 7 7 7 7 0 13 1241 39 227 18 156 23 178 13 170 10 163 26 131 24 216 12 8.7 2.712308 239 22 5.0 5.419660 207 32 24.6 9.922118 201 18 11.4 6.385968 188 15 9.6 5.800980 170 27 4.1 4.065382 138 25 5.1 4.988533 216 18 0.0 6.018519 8.343652e+09 537801.031736 {"rings": [[[-11075502.1995, 6168791.7355], [-...
3 1994 01035616 38007 2975481849 11990485 Billings County North Dakota 159 31 3 4 0 9 0 9 1 3 0 9 2 3 0 9 0 9 156 31 16 16 7 6 13 12 31 17 32 15 26 16 31 15 1.9 2.515275 16 18 0.0 56.250000 7 11 0.0 128.571429 14 12 7.1 20.535318 31 19 0.0 29.032258 34 15 5.9 8.433258 26 18 0.0 34.615385 31 17 0.0 29.032258 6.425825e+09 369444.155012 {"rings": [[[-11476951.3137, 5995895.8254], [-...
4 1995 01034227 38009 4321196488 74864998 Bottineau County North Dakota 1182 44 81 46 0 13 11 11 10 10 18 25 42 35 0 13 0 13 1101 62 167 38 157 43 122 11 133 19 148 42 140 34 234 37 6.9 3.882560 167 40 0.0 7.784431 168 44 6.5 6.319066 132 15 7.6 7.526685 151 31 11.9 16.374422 190 55 22.1 17.273950 140 36 0.0 9.285714 234 39 0.0 5.555556 1.011984e+10 483713.596488 {"rings": [[[-11152289.0833, 6274727.6819], [-...

DataFrame for OB-GYN providers in North Dakota by County

In [138]:
obgyn_ND_df = obgyn_df[obgyn_df['regionabbr']=='ND']
obgyn_ND_df.head()
Out[138]:
SHAPE city objectid postal region regionabbr subregion user_entity_type user_full_address user_npi user_provider_gender user_taxonomy_code_1
58458 {"x": -10774354.7173, "y": 5923277.750399999, ... Fargo 4972067 58102 North Dakota ND Cass County Individual 801 BROADWAY N, , FARGO, ND 58122 1.851398e+09 M 207VM0101X
58459 {"x": -10805394.5552, "y": 6092239.3112, "spat... Grand Forks 4972187 58201 North Dakota ND Grand Forks County Individual 1000 SOUTH COLUMBIA ROAD, , GRAND FORKS, ND 58... 1.366440e+09 M 207V00000X
58460 {"x": -11104110.0435, "y": 6247432.4749, "spat... Belcourt 4972639 58316 North Dakota ND Rolette County Individual 1 HOSPITAL ROAD NORTH, , BELCOURT, ND 58316 1.164496e+09 M 207V00000X
58461 {"x": -11218390.8501, "y": 5910867.419699997, ... Bismarck 4972840 58501 North Dakota ND Burleigh County Individual 1000 E ROSSER AVE, , BISMARCK, ND 585014414 1.982658e+09 M 207V00000X
58462 {"x": -11218390.8501, "y": 5910867.419699997, ... Bismarck 4972850 58501 North Dakota ND Burleigh County Individual 1000 E ROSSER AVE, , BISMARCK, ND 585014414 1.093769e+09 M 207V00000X
In [139]:
obgyn_ND_df.shape
Out[139]:
(102, 12)
In [140]:
# Create dataframe of obgyn provider counts by county
obgyn_NDcounty_df = pd.DataFrame(obgyn_ND_df['subregion'].value_counts().reset_index().values, columns=['County','Provider_Count'])
obgyn_NDcounty_df.head()
Out[140]:
County Provider_Count
0 Cass County 37
1 Burleigh County 22
2 Grand Forks County 13
3 Ward County 10
4 Williams County 8

Merge Provider Count and Population DataFrames

In [141]:
# Merge provider count and women data at county level for ND
county_obgyn_df = pd.merge(ND_fertility_df,obgyn_NDcounty_df,left_on='NAME', right_on='County',how='left')
In [142]:
# Look at null values
county_obgyn_df[county_obgyn_df['Provider_Count'].isnull()][['NAME','Provider_Count']]
Out[142]:
NAME Provider_Count
0 Adams County NaN
1 Barnes County NaN
2 Benson County NaN
3 Billings County NaN
4 Bottineau County NaN
5 Bowman County NaN
6 Burke County NaN
9 Cavalier County NaN
10 Dickey County NaN
11 Divide County NaN
12 Dunn County NaN
13 Eddy County NaN
14 Emmons County NaN
15 Foster County NaN
16 Golden Valley County NaN
18 Grant County NaN
19 Griggs County NaN
20 Hettinger County NaN
21 Kidder County NaN
22 LaMoure County NaN
23 Logan County NaN
24 McHenry County NaN
25 McIntosh County NaN
26 McKenzie County NaN
27 McLean County NaN
28 Mercer County NaN
29 Morton County NaN
30 Mountrail County NaN
31 Nelson County NaN
32 Oliver County NaN
33 Pembina County NaN
34 Pierce County NaN
35 Ramsey County NaN
36 Ransom County NaN
37 Renville County NaN
40 Sargent County NaN
41 Sheridan County NaN
43 Slope County NaN
45 Steele County NaN
47 Towner County NaN
48 Traill County NaN
49 Walsh County NaN
51 Wells County NaN
In [147]:
county_obgyn_df[county_obgyn_df['Provider_Count'].isnull()].shape
Out[147]:
(43, 76)

We can see that:

  • 43 out of 53 counties in ND do not have any Obgyn healthcare providers.

We will replace NaN values for Provider Count in these counties with 1 to plot them on the map to see which counties have highest number of mothers per provider.

In [121]:
# Replace NaN with 1
county_obgyn_df['Provider_Count'].replace(np.nan,1,inplace=True)
In [122]:
# Create new columns that shows provider by women pop
county_obgyn_df['women_per_prov'] = county_obgyn_df['B13016_001E']/county_obgyn_df['Provider_Count']

# Create new columns that shows provider by mother pop
county_obgyn_df['mother_per_prov'] = county_obgyn_df['B13016_002E']/county_obgyn_df['Provider_Count']
In [123]:
# Arrange dataframe by mother_per_prov descending and then B13016_002E (women who gave birht) descending 
county_obgyn_df = county_obgyn_df.sort_values(by=['mother_per_prov','B13016_002E'],
                                              ascending=False)[['OBJECTID','SHAPE','NAME','Provider_Count',
                                                                'B13016_001E','B13016_002E','women_per_prov',
                                                                'mother_per_prov']]
county_obgyn_df.columns = ['OBJECTID','SHAPE','Name','Provider_Count', "Total Women (15 to 50)",
                           "Women who had birth (past 12 months)", 'women_per_prov', 'mother_per_prov']
county_obgyn_df.head()
Out[123]:
OBJECTID SHAPE Name Provider_Count Total Women (15 to 50) Women who had birth (past 12 months) women_per_prov mother_per_prov
29 2020 {'rings': [[[-11236143.3632, 5939274.8243], [-... Morton County 1 6598 555 6598.0 555.0
38 2029 {'rings': [[[-10773989.0107, 5881971.3849], [-... Richland County 1 3342 228 3342.0 228.0
46 2037 {'rings': [[[-11050272.8328, 5995646.2405], [-... Stutsman County 2 4237 315 2118.5 157.5
10 2001 {'rings': [[[-10913159.7795, 5825785.1309], [-... Dickey County 1 1011 151 1011.0 151.0
1 1992 {'rings': [[[-10904992.8409, 5981421.7966], [-... Barnes County 1 2185 145 2185.0 145.0

Plot Shortage

Let's plot the number of Mothers per OBGYN provider for all counties in North Dakota on a map.

In [133]:
obgyn_countyshortage_map = gis.map('North Dakota, USA', zoomlevel=6)
obgyn_countyshortage_map
Out[133]:

We can see that:

  • Morton County seems to be the worst with 555 women who gave birth but no provider.
Define Renderer
In [125]:
# Define Renderer
obgynCountyTest = {"renderer": { #This tells python to use JS autocasting
                 "type": "classBreaks",  
                 "field":"mother_per_prov",
                 "transparency":.5,
                 "minValue":1}}
In [129]:
# Define Manual Class breaks
obgynCountyTest['renderer']["classBreakInfos"] = [{
  "classMaxValue": 100.00,
  "label": "0 - 100.00",
  "description": "0 - 100.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [255,247,236,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}, {
  "classMaxValue": 200.00,
  "label": "100.001 - 200.00",
  "description": "100.001 - 200.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [253,220,174,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}, {
  "classMaxValue": 300.00,
  "label": "200.001 - 300.00",
  "description": "200.001 - 300.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [252,177,123,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}, {
  "classMaxValue": 400.00,
  "label": "300.001 - 400.00",
  "description": "300.001 - 400.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [241,109,75,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}, {
  "classMaxValue": 700.00,
  "label": "400.001 - 700.00",
  "description": "400.001 - 700.00",
  "symbol": {
    "type": "esriSFS",
    "style": "esriSFSSolid",
    "color": [200,28,18,178.5],
    "outline": {
      "style": "esriSLSSolid",
      "type": "esriSLS",
      "color": [128,128,128,255],
      "width": 2
    }
  }
}]
Plot Map
In [130]:
# Plot Map using defined Renderer
county_obgyn_df.spatial.plot(map_widget=obgyn_countyshortage_map, renderer=obgynCountyTest['renderer'])
Out[130]:
True
In [131]:
# Add legend
obgyn_countyshortage_map.legend=True
In [132]:
# Save as a Web Map Item
obgyn_cs_item = obgyn_countyshortage_map.save({'title':'obgyn_countyshortage_map',
                                                'snippet':'shortage map',
                                                'tags':'Women Health',
                                                'typeKeywords':'WebMap'})
obgyn_cs_item
Out[132]:
obgyn_countyshortage_map
shortage mapWeb Map by portaladmin
Last Modified: August 11, 2020
0 comments, 0 views
Morton County Infographic

To visualize high level key facts about a geography and gain greater insights, various infographics can be created using ArcGIS Business Analyst. Learn more about ArcGIS Business Analyst.

The image below shows an infographic of key facts for Morton County, ND.

North Dakota has the highest number of women (ages 15 to 50) who had a birth in the past 12 months per OBGYN provider. Also:

  1. There were ~112 mothers and 1655 women per provider in ND compared to ~31 mothers and 662 women per provider in DC.
  2. 43 out of 53 counties in ND do not have any OBGYN healthcare provider.
  3. Morton County seems to be the worst with 463 women who gave birth but no provider.

Summary

To summarize, we explored the shortage of Mental Health and OBGYN Providers accross United States. We saw that:

  1. Mississippi has the highest number of people per Mental Healthcare Provider
    • There were ~2754 people per mental healthcare provider in Mississippi compared to ~364 people per provider in DC.
    • 18 counties in Mississippi do not have any mental healthcare provider.
    • Marshall County seems to be the worst with 1 provider for 37137 people. Some other counties with high population per provider are Pontotoc, Neshoba, Scott and Union.
  1. North Dakota has the highest number of women (15 to 50) who had a birth in the past 12 months per OBGYN provider.
    • There were ~112 mothers and 1655 women per provider in ND compared to ~31 mothers and 662 women per provider in DC.
    • 43 out of 53 counties in ND do not have any OBGYN healthcare provider.
    • Morton County seems to be the worst with 463 women who gave birth but no provider.

References