Calculating Indices: SRI, SCAI, and GAI¶
In this notebook, we will calculate the Stroke Risk Index (SRI), Stroke Care Access Index (SCAI), and Geographic Accessibility Index (GAI), using predefined variables.
Reading Data from Database and Importing Index Pipeline¶
In [1]:
# Importing packages
import sqlite3
import pandas as pd
import sys
In [2]:
sys.path.insert(0, "../src")
In [3]:
# Importing pipeline
from index_pipeline import build_index
In [4]:
con = sqlite3.connect("../data/stroke_burden.db")
In [5]:
df = pd.read_sql("SELECT * FROM master", con)
In [6]:
# Creating SCAI dataframe
#
# hospitals_per_100k and stroke_centers_per_100k are excluded from the index
# (kept in EDA) and pcnt_insured enters as the uninsured rate — see
# docs/DECISIONS.md (2026-07-06).
scai = df[[
"fips",
"hospital_beds_per_100k",
"pcp_per_100k",
"neurologists_per_100k",
"pcnt_insured"
]].copy()
# Recast insurance coverage as the uninsured rate: right-skewed, so log1p
# applies; higher = worse access, so it gets flipped in build_index below.
scai["pcnt_uninsured"] = 100 - scai["pcnt_insured"]
In [7]:
# Creating GAI dataframe
gai = df[[
"fips",
"drive_time_min",
"drive_time_advanced",
"nearest_stroke_distance",
"nearest_stroke_distance_advanced"
]].copy()
In [8]:
# Creating SRI dataframe
sri = df[[
"fips",
"pop_density",
"pcnt_65_plus",
"poverty_rate",
"pcnt_low_income",
"pcnt_bachelors",
"smoking_prevalence",
"obesity_prevalence",
"diabetes_prevalence",
"physical_inactivity",
"hypertension_prevalence",
"high_cholesterol_prevalence",
"binge_drinking_prevalence",
"stroke_prevalence"
]].copy()
Building Indices¶
SCAI¶
In [9]:
scai_result = build_index(
scai,
[
"hospital_beds_per_100k",
"pcp_per_100k",
"neurologists_per_100k",
"pcnt_uninsured"
],
flip=["pcnt_uninsured"],
transforms={"pcnt_uninsured": "log1p"},
name="scai"
)
df["scai"] = scai_result.scores
In [10]:
scai_result.explained_variance_ratio
Out[10]:
0.5394857770201571
In [11]:
scai_result.loadings
Out[11]:
hospital_beds_per_100k 0.518748 pcp_per_100k 0.610016 neurologists_per_100k 0.594107 pcnt_uninsured 0.076270 Name: scai_pc1_loading, dtype: float64
In [12]:
# Skew check: should be empty — any variable listed here has |skew| > 2
# without a transform and needs a decision (see docs/DECISIONS.md)
scai_result.high_skew
Out[12]:
['neurologists_per_100k']
GAI¶
In [13]:
gai_result = build_index(
gai,
[
"drive_time_min",
"drive_time_advanced",
"nearest_stroke_distance",
"nearest_stroke_distance_advanced"
],
flip=[
"drive_time_min",
"drive_time_advanced",
"nearest_stroke_distance",
"nearest_stroke_distance_advanced"
],
transforms={
"drive_time_min":"log1p",
"drive_time_advanced":"log1p",
"nearest_stroke_distance":"log1p",
"nearest_stroke_distance_advanced":"log1p"
},
name="gai"
)
df["gai"] = gai_result.scores
In [14]:
gai_result.explained_variance_ratio
Out[14]:
0.7706484916219887
In [15]:
gai_result.loadings
Out[15]:
drive_time_min 0.487536 drive_time_advanced 0.508339 nearest_stroke_distance 0.505386 nearest_stroke_distance_advanced 0.498483 Name: gai_pc1_loading, dtype: float64
SRI¶
In [16]:
sri_result = build_index(
sri,
[
"pop_density",
"pcnt_65_plus",
"poverty_rate",
"pcnt_low_income",
"pcnt_bachelors",
"smoking_prevalence",
"obesity_prevalence",
"diabetes_prevalence",
"physical_inactivity",
"hypertension_prevalence",
"high_cholesterol_prevalence",
"binge_drinking_prevalence",
"stroke_prevalence"
],
flip=["pcnt_bachelors"],
transforms={"pop_density": "log1p"},
name="sri"
)
In [17]:
sri_result.explained_variance_ratio
Out[17]:
0.5223875266272114
In [18]:
sri_result.loadings
Out[18]:
pop_density -0.220684 pcnt_65_plus 0.155486 poverty_rate 0.245481 pcnt_low_income 0.325243 pcnt_bachelors 0.351806 smoking_prevalence 0.361232 obesity_prevalence 0.296924 diabetes_prevalence 0.294431 physical_inactivity 0.297536 hypertension_prevalence 0.324855 high_cholesterol_prevalence 0.064245 binge_drinking_prevalence -0.004078 stroke_prevalence 0.365741 Name: sri_pc1_loading, dtype: float64