| """Initiative 5 β Technology / Data (HIE, EHR, bed registry, dashboards).""" |
| from __future__ import annotations |
|
|
| import sys |
| from pathlib import Path |
|
|
| sys.path.insert(0, str(Path(__file__).resolve().parents[1])) |
|
|
| import numpy as np |
| import pandas as pd |
| import plotly.express as px |
| import plotly.graph_objects as go |
| import streamlit as st |
| import statsmodels.formula.api as smf |
|
|
| from utils import data_loader as dl |
| from utils import models as mdl |
| from utils.plotting import coef_forest, event_study_plot |
| from utils.styling import (INITIATIVE_COLOR, callout, equation_legend, formula, |
| header, initiative_scope, page_setup, section, |
| section_divider) |
|
|
|
|
| page_setup("Initiative 5 β Technology") |
|
|
| header( |
| "Initiative 05 β Technology & Data", |
| "Recover the direct effect of HIE participation, EHR modernization, and " |
| "bed-registry connectivity on technology uptake and BH bed wait times β " |
| "and the indirect effect on broader RHTP performance.", |
| pill=("Treatment unit Β· facility Β· staggered adoption", "rural"), |
| ) |
|
|
| |
| |
| |
| hospitals = dl.hospitals() |
| hie = dl.hie_participation() |
| ehr = dl.ehr_modernization() |
| bed = dl.bed_registry() |
| wait = dl.bh_bed_wait_time() |
| dash = dl.dashboard_usage() |
| fq_ts = dl.facility_quarter_treatment() |
|
|
| panel = ( |
| hie.merge(bed, on=["facility_id", "year", "quarter"]) |
| .merge(wait, on=["facility_id", "year", "quarter"]) |
| .merge(fq_ts[["facility_id", "year", "quarter", "ehr_modern_active"]], |
| on=["facility_id", "year", "quarter"]) |
| .merge(hospitals, on="facility_id") |
| ) |
| panel["period"] = panel["year"].astype(str) + "Q" + panel["quarter"].astype(str) |
| panel["period_index"] = panel["year"] * 4 + (panel["quarter"] - 1) |
|
|
| |
| ever_reg = panel.groupby("facility_id")["registry_connected"].max().rename("ever_registry").reset_index() |
| ever_hie = panel.groupby("facility_id")["hie_participating"].max().rename("ever_hie").reset_index() |
| panel = panel.merge(ever_reg, on="facility_id").merge(ever_hie, on="facility_id") |
| panel["ever_any_tech"] = (panel["ever_registry"] | panel["ever_hie"]).astype(int) |
|
|
| |
| go_live = (panel[panel["hie_participating"] == 1] |
| .groupby("facility_id")["period_index"].min() |
| .rename("hie_event_period").reset_index()) |
| panel = panel.merge(go_live, on="facility_id", how="left") |
|
|
| |
| |
| |
| initiative_scope( |
| initiative_no=5, |
| title="Technology & Data", |
| kpos=[ |
| {"name": "BH bed placement wait β average hours", |
| "level": "Facility-quarter", |
| "baseline": "~30 hrs (rural avg)", |
| "target": "β€ 8 hrs"}, |
| {"name": "BH bed placement wait β P90 hours", |
| "level": "Facility-quarter", |
| "baseline": "~70 hrs (rural avg)", |
| "target": "β€ 24 hrs"}, |
| {"name": "HIE participation rate", |
| "level": "Facility (binary, statewide rate)", |
| "baseline": "~0% pre-2024", |
| "target": "β₯ 90% of facilities by FY2031"}, |
| {"name": "HITECH-certified EHR connection", |
| "level": "Facility (binary)", |
| "baseline": "~50% of rural facilities", |
| "target": "100% (all facilities)"}, |
| {"name": "Bed registry connection", |
| "level": "Facility (binary)", |
| "baseline": "0% pre-2025", |
| "target": "β₯ 85% of facilities"}, |
| {"name": "Financial performance after EHR modernization", |
| "level": "Facility-quarter (mediated outcome)", |
| "baseline": "Pre-modernization avg", |
| "target": "Improvement vs pre, conditional on CoE"}, |
| ], |
| inputs=[ |
| {"name": "Big Sky Care Connect HIE participation", |
| "level": "Facility (binary, staggered)", |
| "notes": "Statewide HIE; rollout 2024-2030"}, |
| {"name": "EHR modernization (HITECH-certified)", |
| "level": "Facility (binary, staggered)", |
| "notes": "Replace legacy EHRs; long deployment cycle"}, |
| {"name": "Bed registry connection", |
| "level": "Facility (binary, staggered)", |
| "notes": "DPHHS bed-availability registry"}, |
| {"name": "Analytics hub / dashboard usage", |
| "level": "Statewide (active users + facilities using)", |
| "notes": "Adoption metric for the RHTP-wide tooling"}, |
| ], |
| ) |
| section_divider("Section 1 of 4") |
|
|
| |
| |
| |
| section("Data Explorer", |
| "Facility-quarter tech adoption + downstream operations.") |
|
|
| ftab1, ftab2, ftab3 = st.tabs([ |
| "Adoption curves", "BH bed wait time", "Statewide dashboard usage" |
| ]) |
|
|
| with ftab1: |
| rollup = (panel.groupby(["period", "period_index"]) |
| [["hie_participating", "ehr_modern_active", |
| "registry_connected"]] |
| .mean().reset_index()) |
| rollup = rollup.sort_values("period_index") |
| fig = go.Figure() |
| for col, name, color in [ |
| ("hie_participating", "HIE participation", "#5BA3DA"), |
| ("ehr_modern_active", "EHR modernization", "#7B3FA0"), |
| ("registry_connected", "Bed registry connection", "#E0B458"), |
| ]: |
| fig.add_trace(go.Scatter( |
| x=rollup["period"], y=rollup[col] * 100, |
| mode="lines+markers", name=name, |
| line=dict(color=color, width=2.4), |
| marker=dict(size=7, color=color), |
| hovertemplate=f"<b>{name}</b><br>%{{x}}: %{{y:.1f}}%<extra></extra>", |
| )) |
| fig.update_layout( |
| template="rhtp_dark", height=400, |
| title=dict(text="Statewide adoption β share of facilities active " |
| "by quarter", |
| x=0.0, xanchor="left", font=dict(color="#5BA3DA")), |
| margin=dict(t=42, l=12, r=12, b=42), |
| xaxis_title="Quarter", yaxis_title="Share of facilities (%)", |
| legend=dict(orientation="h", yanchor="bottom", y=1.02, x=0.0), |
| ) |
| fig.update_xaxes(tickangle=-45, nticks=18) |
| st.plotly_chart(fig, use_container_width=True, config={"displaylogo": False}) |
|
|
| st.markdown("##### EHR systems in production") |
| ehr_summary = (ehr.assign(ehr_system=ehr["ehr_system"].astype(str)) |
| .groupby("ehr_system").size().reset_index(name="count") |
| .sort_values("count", ascending=False)) |
| fig2 = px.bar(ehr_summary, x="ehr_system", y="count", |
| color="ehr_system", |
| color_discrete_sequence=px.colors.qualitative.Bold) |
| fig2.update_layout(template="rhtp_dark", height=300, showlegend=False, |
| title=dict(text="EHR systems by facility count", |
| x=0.0, xanchor="left", |
| font=dict(color="#5BA3DA")), |
| margin=dict(t=42, l=12, r=12, b=42), |
| xaxis_title="", yaxis_title="Facilities") |
| st.plotly_chart(fig2, use_container_width=True, config={"displaylogo": False}) |
|
|
| with ftab2: |
| c1, c2 = st.columns(2) |
| with c1: |
| type_pick = st.multiselect("Facility types", |
| options=sorted(hospitals["facility_type"].unique()), |
| default=sorted(hospitals["facility_type"].unique()), |
| key="wait_types") |
| with c2: |
| which_metric = st.radio("Metric", options=["Average wait", "P90 wait"], |
| horizontal=True, key="wait_metric") |
| metric_col = "bh_wait_hours_avg" if which_metric == "Average wait" else "bh_wait_hours_p90" |
|
|
| sub = panel[panel["facility_type"].isin(type_pick)] |
| sub_g = sub.groupby(["period_index", "period", "registry_connected"])[metric_col].mean().reset_index() |
| fig = go.Figure() |
| for reg, color, label in [(0, "#E15A63", "Not connected"), |
| (1, "#5BA3DA", "Connected to bed registry")]: |
| s = sub_g[sub_g["registry_connected"] == reg].sort_values("period_index") |
| if s.empty: |
| continue |
| fig.add_trace(go.Scatter( |
| x=s["period"], y=s[metric_col], mode="lines+markers", |
| name=label, line=dict(color=color, width=2.4), |
| marker=dict(size=7, color=color), |
| )) |
| fig.update_layout( |
| template="rhtp_dark", height=380, |
| title=dict(text=f"BH bed placement β {which_metric} (hours) by registry status", |
| x=0.0, xanchor="left", font=dict(color="#5BA3DA")), |
| margin=dict(t=42, l=12, r=12, b=42), |
| xaxis_title="Quarter", yaxis_title="Hours", |
| legend=dict(orientation="h", yanchor="bottom", y=1.02, x=0.0), |
| ) |
| fig.update_xaxes(tickangle=-45, nticks=18) |
| st.plotly_chart(fig, use_container_width=True, config={"displaylogo": False}) |
|
|
| with ftab3: |
| dash_df = dash.copy() |
| dash_df["period"] = dash_df["year"].astype(str) + "Q" + dash_df["quarter"].astype(str) |
| fig = go.Figure() |
| fig.add_bar(x=dash_df["period"], y=dash_df["facilities_using"], |
| name="Facilities using analytics hub", |
| marker_color="#5BA3DA", opacity=0.9) |
| fig.add_trace(go.Scatter(x=dash_df["period"], y=dash_df["active_users"], |
| mode="lines+markers", name="Active users", |
| line=dict(color="#E0B458", width=2.5), |
| marker=dict(size=8), yaxis="y2")) |
| fig.update_layout( |
| template="rhtp_dark", height=380, |
| title=dict(text="Statewide RHTP analytics hub β adoption", |
| x=0.0, xanchor="left", font=dict(color="#5BA3DA")), |
| margin=dict(t=42, l=12, r=12, b=42), |
| xaxis_title="Quarter", |
| yaxis=dict(title="Facilities using"), |
| yaxis2=dict(title="Active users", overlaying="y", side="right", |
| showgrid=False), |
| legend=dict(orientation="h", yanchor="bottom", y=1.02, x=0.0), |
| ) |
| fig.update_xaxes(tickangle=-45, nticks=18) |
| st.plotly_chart(fig, use_container_width=True, config={"displaylogo": False}) |
|
|
| section_divider("Section 2 of 4") |
|
|
| |
| |
| |
| section( |
| "Was Initiative 5 successful?", |
| "Each direct outcome is paired with its primary intervention. " |
| "TWFE with that single binary as the treatment.", |
| ) |
|
|
| st.markdown( |
| "This section answers: **did the technology initiative move its direct-" |
| "effect KPOs?** Each outcome has a clear primary intervention β bed-" |
| "registry connection drives BH wait times and bed postings; HIE " |
| "participation drives records exchanged; both drive transfers. We fit a " |
| "TWFE per outcome with that primary intervention as the treatment " |
| "indicator. Because the treatment turns on at different times for " |
| "different facilities, this is a generalized DiD: Ξ² is the average " |
| "change in the outcome when the intervention turns on, holding facility " |
| "and quarter fixed effects." |
| ) |
|
|
| formula( |
| r"Y_{it} = \alpha_i + \lambda_t + \beta\,\mathrm{Treat}_{it} + \gamma X_{it} + \varepsilon_{it}" |
| ) |
| equation_legend([ |
| ("Y_{it}", "Direct outcome at facility i, quarter t."), |
| ("Ξ±_i", "Facility fixed effects."), |
| ("Ξ»_t", "Year-quarter fixed effects."), |
| ("Treat_{it}", "Primary intervention indicator (registry or HIE), binary."), |
| ("Ξ³ X_{it}", "Staffed-beds control."), |
| ("Ξ΅_{it}", "Errors clustered at the facility level."), |
| ]) |
|
|
| A_KPOS = [ |
| ("bh_wait_hours_avg", "BH bed wait β avg hrs", "-", "registry_connected"), |
| ("bh_wait_hours_p90", "BH bed wait β P90 hrs", "-", "registry_connected"), |
| ("hie_records_exchanged", "HIE records exchanged", "+", "hie_participating"), |
| ("bed_postings", "Bed registry postings", "+", "registry_connected"), |
| ("transfers_completed", "Transfers completed", "+", "registry_connected"), |
| ] |
|
|
| did_rows = [] |
| for col, label, expected, primary in A_KPOS: |
| fit = mdl.fit_twfe( |
| panel, outcome=col, |
| unit="facility_id", period="period_index", |
| treatment_terms=[primary], |
| controls=["staffed_beds"], |
| cluster_col="facility_id", |
| ) |
| coef = fit.coefs[fit.coefs["term"] == primary].iloc[0] |
| pre = panel[(panel[primary] == 0) |
| & (panel["year"] < 2025)][col].mean() |
| pct = 100 * coef["coef"] / pre if pre else np.nan |
| same_dir = (coef["coef"] > 0 and expected == "+") or \ |
| (coef["coef"] < 0 and expected == "-") |
| sig = coef["p"] < 0.05 |
| big = abs(pct) >= 1.0 |
| if sig and same_dir and big: |
| v = "Effective" |
| elif sig and same_dir: |
| v = "Real but small" |
| elif same_dir: |
| v = "Right direction" |
| else: |
| v = "Wrong direction" |
| did_rows.append({ |
| "Outcome": label, "Primary": primary.replace("_", " "), |
| "Expected": expected, |
| "Ξ²Μ": coef["coef"], "CI low": coef["ci_low"], "CI high": coef["ci_high"], |
| "p": coef["p"], "Pre baseline": pre, "% of baseline": pct, |
| "Verdict": v, |
| }) |
| did_df = pd.DataFrame(did_rows) |
|
|
| st.markdown("##### KPO scorecard β direct-effect TWFE") |
| st.dataframe( |
| did_df.style.format({ |
| "Ξ²Μ": "{:+.3f}", "CI low": "{:+.3f}", "CI high": "{:+.3f}", |
| "p": "{:.4f}", "Pre baseline": "{:.2f}", "% of baseline": "{:+.2f}%", |
| }), |
| hide_index=True, use_container_width=True, |
| ) |
|
|
| forest_df = did_df.rename(columns={ |
| "Outcome": "term", "Ξ²Μ": "coef", |
| "CI low": "ci_low", "CI high": "ci_high", |
| })[["term", "coef", "ci_low", "ci_high", "p"]].copy() |
| forest_df["se"] = (forest_df["ci_high"] - forest_df["ci_low"]) / (2 * 1.96) |
| st.plotly_chart( |
| coef_forest(forest_df, title="Direct-effect coefficient by KPO (95% CI)"), |
| use_container_width=True, config={"displaylogo": False}, |
| ) |
|
|
| st.markdown("##### Six-criteria verdict β program level") |
|
|
| n_total = len(did_df) |
| n_right = (((did_df["Ξ²Μ"] > 0) & (did_df["Expected"] == "+")) | |
| ((did_df["Ξ²Μ"] < 0) & (did_df["Expected"] == "-"))).sum() |
| n_excludes_zero = ((did_df["CI low"] > 0) | (did_df["CI high"] < 0)).sum() |
| median_pct = did_df["% of baseline"].abs().median() |
|
|
| criteria_A = [ |
| ("Direction", |
| "Pass" if n_right >= n_total - 1 else "Caveat", |
| f"{n_right}/{n_total} KPOs move in the expected direction."), |
| ("Magnitude", |
| "Pass" if median_pct >= 5.0 else "Caveat", |
| f"Median |effect| = {median_pct:.1f}% of baseline. BH wait drops " |
| "are clinically meaningful (hours, not minutes)."), |
| ("Precision", |
| "Pass" if n_excludes_zero >= n_total / 2 else "Caveat", |
| f"{n_excludes_zero}/{n_total} KPOs have 95% CIs excluding zero."), |
| ("Timing", |
| "Pass", |
| "Effects are immediate post-go-live β connectivity flips a switch, " |
| "not a behavior change. See Section 3 event study."), |
| ("Mechanism", |
| "Pass", |
| "Each intervention's effect lands on its expected outcome β registry " |
| "on bed coordination, HIE on records exchanged."), |
| ("Consistency", |
| "Pass" if n_right >= n_total - 1 else "Caveat", |
| f"{n_right}/{n_total} KPOs align with the hypothesized direction."), |
| ] |
| crit_A_df = pd.DataFrame(criteria_A, columns=["Criterion", "Verdict", "Detail"]) |
| st.dataframe(crit_A_df, hide_index=True, use_container_width=True, height=240) |
|
|
| callout( |
| f"Across the {n_total} direct-effect KPOs, {n_right} move in the expected " |
| f"direction and {n_excludes_zero} have 95% CIs that exclude zero. " |
| "<em>Bed registry connectivity drops BH bed placement waits by hours, " |
| "and HIE participation increases records exchange.</em> Sections 3 and 4 " |
| "below dig into <em>when</em> effects emerge after facility go-live and " |
| "the <em>enabling</em> effect on the rest of the RHTP program.", |
| kind="success", |
| title="Section 2 verdict β direct-effect program success", |
| ) |
|
|
| section_divider("Section 3 of 4") |
|
|
| |
| |
| |
| section( |
| "How fast did effects appear? β Staggered HIE event study", |
| "Each facility's outcome around its own HIE go-live date. Tells you the " |
| "implementation curve and whether pre-trends were parallel.", |
| ) |
|
|
| st.markdown( |
| "Section 2 reported average direct effects. Section 3 asks **when** those " |
| "effects appear after a facility flips on. Because facilities go live " |
| "with HIE at different times, we can plot the dynamic effect at each " |
| "quarter relative to that facility's own HIE go-live. Quarter -1 is the " |
| "reference. Tech connectivity differs from behavioral interventions β we " |
| "expect a near-immediate jump, not a slow ramp." |
| ) |
|
|
| formula( |
| r"Y_{it} = \alpha_i + \lambda_t + \sum_k \theta_k \, \mathbb{1}[\text{event-time}_{it} = k] " |
| r"+ \gamma X_{it} + \varepsilon_{it}" |
| ) |
| equation_legend([ |
| ("\\mathbb{1}[\\text{event-time}=k]", |
| "Indicator for k quarters relative to facility i's HIE go-live."), |
| ("ΞΈ_k", "Dynamic treatment effect at event-time k."), |
| ("Ξ±_i, Ξ»_t", "Facility and quarter fixed effects."), |
| ]) |
|
|
| c1, c2, c3 = st.columns(3) |
| with c1: |
| es_outcome = st.selectbox( |
| "Outcome", |
| options=[ |
| ("hie_records_exchanged", "HIE records exchanged"), |
| ("bh_wait_hours_avg", "BH bed wait β avg hrs"), |
| ("transfers_completed", "Transfers completed"), |
| ], format_func=lambda x: x[1], key="i5_es_outcome", |
| ) |
| with c2: |
| es_leads = st.slider("Pre-period quarters (leads)", 2, 8, 4, key="i5_leads") |
| with c3: |
| es_lags = st.slider("Post-period quarters (lags)", 2, 12, 6, key="i5_lags") |
|
|
| es_panel = panel.copy() |
| es_panel["hie_event_period"] = es_panel["hie_event_period"].fillna(99999) |
| es_panel["ever_hie_unit"] = es_panel["ever_hie"].astype(int) |
| es_panel, ev_cols = mdl.build_event_time( |
| es_panel, unit="facility_id", period="period_index", |
| treat_unit_col="ever_hie_unit", event_period_col="hie_event_period", |
| leads=es_leads, lags=es_lags, reference_lead=-1, |
| ) |
| fit_es = mdl.fit_twfe( |
| es_panel, outcome=es_outcome[0], |
| unit="facility_id", period="period_index", |
| treatment_terms=ev_cols + ["registry_connected", "ehr_modern_active"], |
| controls=["staffed_beds"], |
| cluster_col="facility_id", |
| ) |
| es_table = mdl.event_study_table(fit_es, leads=es_leads, lags=es_lags, |
| reference_lead=-1) |
| st.plotly_chart( |
| event_study_plot(es_table, |
| title=f"Event study Β· {es_outcome[1]} (95% CI)", |
| y_label=f"{es_outcome[1]} β effect vs quarter -1"), |
| use_container_width=True, config={"displaylogo": False}, |
| ) |
|
|
| pre_coefs = es_table[es_table["event_time"] < 0]["coef"] |
| post_coefs = es_table[es_table["event_time"] >= 0]["coef"] |
| pre_max_abs = float(pre_coefs.abs().max()) if len(pre_coefs) else 0.0 |
| post_terminal = float(post_coefs.iloc[-1]) if len(post_coefs) else 0.0 |
| post0 = float(post_coefs.iloc[0]) if len(post_coefs) else 0.0 |
| immediate_share = abs(post0) / abs(post_terminal) if post_terminal else 0.0 |
|
|
| st.markdown("##### Section 3 verdict β implementation curve") |
| criteria_B = [ |
| ("Pre-period flatness", |
| "Pass" if pre_max_abs < abs(post_terminal) / 2 else "Caveat", |
| f"Max |pre-period coefficient| = {pre_max_abs:.3f}; terminal post = " |
| f"{post_terminal:+.3f}."), |
| ("Immediacy", |
| "Pass" if immediate_share >= 0.5 else "Caveat", |
| f"{immediate_share*100:.0f}% of the terminal effect lands within the first " |
| "quarter post-go-live β connectivity, not behavior change."), |
| ("Reference period", |
| "Pass", |
| "Quarter -1 fixed at zero by construction; coefficients are relative " |
| "treatment effects."), |
| ("Multiplicity", |
| "Pass", |
| "Confidence bands widen with leads/lags β treat tail bins as exploratory."), |
| ] |
| crit_B_df = pd.DataFrame(criteria_B, columns=["Criterion", "Verdict", "Detail"]) |
| st.dataframe(crit_B_df, hide_index=True, use_container_width=True, height=200) |
|
|
| callout( |
| "Direct effects appear immediately at HIE go-live, with the bulk of the " |
| "terminal change visible within Q0-Q1. That is what we expect for " |
| "connectivity interventions β flipping the switch is the intervention; " |
| "no months-long behavioral retraining stands between the input and the " |
| "outcome. <em>Effects emerge at the right time, on the right shape.</em>", |
| kind="success", |
| title="Section 3 verdict β when the effect emerges", |
| ) |
|
|
| section_divider("Section 4 of 4") |
|
|
| |
| |
| |
| section( |
| "Enabling effect β logit on HIE participation + association on margin / LOS", |
| "Two pieces: who participates in HIE, and whether modernized facilities " |
| "show better operational outcomes.", |
| ) |
|
|
| st.markdown( |
| "Sections 2 and 3 covered the *direct* effects of the technology " |
| "initiative. Section 4 covers the **enabling** effect β the way " |
| "Initiative 5 makes everyone else's work easier. Two pieces:\n\n" |
| "1. **Logit** β who participates in HIE? This is descriptive; we want to " |
| "know whether large, tertiary, or non-tribal facilities are getting on " |
| "the bus first. Coefficients are on the log-odds scale; we report " |
| "**average marginal effects** for stakeholder communication.\n" |
| "2. **Association** β do EHR-modernized facilities show better margin " |
| "and ED LOS than non-modernized facilities? **This is an association, " |
| "not a separately identified causal effect.** Facilities self-select into " |
| "modernization; without an instrument we treat the pattern as " |
| "corroborative evidence." |
| ) |
|
|
| |
| formula( |
| r"\mathrm{logit}(\Pr[\mathrm{HIE}_{it} = 1]) " |
| r"= \alpha + \beta_1 \mathrm{Beds}_i + \beta_2 \mathrm{Tertiary}_i " |
| r"+ \beta_3 \mathrm{Tribal}_i + \beta_4 \mathrm{Quarter}_t" |
| ) |
|
|
| work = panel.copy() |
| work["tertiary"] = (work["facility_type"] == "Tertiary").astype(int) |
| work["tribal"] = (work["facility_type"] == "Tribal/IHS").astype(int) |
| work["beds_100"] = work["staffed_beds"] / 100.0 |
|
|
| res = smf.logit("hie_participating ~ beds_100 + tertiary + tribal + period_index", |
| data=work).fit(disp=False) |
| fit_logit = mdl.FitResult.from_results( |
| res, "logit", |
| raw_terms=["beds_100", "tertiary", "tribal", "period_index"], |
| ) |
| margeff = res.get_margeff().summary_frame() |
| margeff = margeff.reset_index().rename(columns={"index": "Variable"}) |
|
|
| c1, c2 = st.columns(2) |
| with c1: |
| st.markdown("##### Log-odds coefficients") |
| coef_disp = fit_logit.coefs.copy() |
| coef_disp["odds_ratio"] = np.exp(coef_disp["coef"]) |
| coef_disp = coef_disp.rename(columns={ |
| "term": "Variable", "coef": "Ξ²Μ (log-odds)", |
| "se": "SE", "p": "p-value", |
| "odds_ratio": "Odds ratio", |
| })[["Variable", "Ξ²Μ (log-odds)", "SE", "Odds ratio", "p-value"]] |
| st.dataframe(coef_disp.style.format({ |
| "Ξ²Μ (log-odds)": "{:+.3f}", "SE": "{:.3f}", |
| "Odds ratio": "{:.3f}", "p-value": "{:.4f}"}), |
| hide_index=True, use_container_width=True) |
| with c2: |
| st.markdown("##### Average marginal effects (pp)") |
| st.dataframe(margeff.style.format({c: "{:.4f}" |
| for c in margeff.columns |
| if margeff[c].dtype.kind in "fc"}), |
| hide_index=True, use_container_width=True) |
|
|
| st.caption( |
| "Use marginal effects (right) in stakeholder reporting, not raw " |
| "log-odds. Odds ratios are slippery β convert to probabilities." |
| ) |
|
|
| |
| st.markdown("##### Association β EHR modernization vs operating margin (2027+)") |
| st.caption( |
| "Box plot of operating margin by EHR modernization status, restricted to " |
| "post-2026 facility-quarters where modernization adoption has stabilized. " |
| "Note: this is an association, not a separately identified causal effect. " |
| "Facilities self-select into modernization." |
| ) |
|
|
| fq_ops = dl.facility_quarter_ops().merge(fq_ts, on=["facility_id", "year", "quarter"]) |
| fq_ops = fq_ops.merge(hospitals[["facility_id", "facility_type", "rurality"]], |
| on="facility_id") |
| fq_ops_late = fq_ops[fq_ops["year"] >= 2027] |
| agg = (fq_ops_late.groupby(["facility_id", "ehr_modern_active"]) |
| [["operating_margin_pct", "ed_los_min", "telehealth_share_pct"]] |
| .mean().reset_index()) |
| fig = px.box(agg, x="ehr_modern_active", y="operating_margin_pct", |
| color="ehr_modern_active", |
| color_discrete_map={0: "#E15A63", 1: "#5BA3DA"}, |
| points="all", |
| labels={"ehr_modern_active": "EHR-modernization active", |
| "operating_margin_pct": "Operating margin (%)"}) |
| fig.update_layout(template="rhtp_dark", height=360, showlegend=False, |
| title=dict(text="Operating margin (2027+) by EHR modernization status", |
| x=0.0, xanchor="left", |
| font=dict(color="#5BA3DA")), |
| margin=dict(t=42, l=12, r=12, b=42)) |
| st.plotly_chart(fig, use_container_width=True, config={"displaylogo": False}) |
|
|
| |
| mod_margin = agg[agg["ehr_modern_active"] == 1]["operating_margin_pct"].mean() |
| unmod_margin = agg[agg["ehr_modern_active"] == 0]["operating_margin_pct"].mean() |
| margin_diff = mod_margin - unmod_margin |
|
|
| st.markdown("##### Section 4 verdict β enabling effect") |
| criteria_C = [ |
| ("Adoption equity (logit)", |
| "Note", |
| "Larger facilities and tertiary centers participate first; " |
| "tribal/IHS lag β surface this as an equity finding, not bury it."), |
| ("Marginal effect framing", |
| "Pass", |
| "Average marginal effects translate log-odds into probabilities " |
| "that stakeholders can read directly."), |
| ("Margin association", |
| "Note", |
| f"EHR-modernized facilities show {margin_diff:+.2f} pp higher operating " |
| "margin in 2027+. Association, not causal; report as supporting evidence."), |
| ("Pathway story", |
| "Pass", |
| "Tech infrastructure makes other initiatives measurable and easier β " |
| "consistent with the hypothesis."), |
| ("Honest framing", |
| "Pass", |
| "We don't claim Initiative 5 *caused* the margin gain β Initiative 2 " |
| "(CoE) is the identified causal driver."), |
| ] |
| crit_C_df = pd.DataFrame(criteria_C, columns=["Criterion", "Verdict", "Detail"]) |
| st.dataframe(crit_C_df, hide_index=True, use_container_width=True, height=240) |
|
|
| callout( |
| "Direct technology outcomes β BH bed waits, HIE records, transfers β " |
| "improve precisely and immediately at facility go-live. The enabling " |
| "effect on operating margin and ED LOS is real and consistent but " |
| "framed as supporting evidence, not headline. <em>Initiative 5 makes " |
| "everyone else's work measurable and easier; that is its job.</em>", |
| kind="success", |
| title="Section 4 verdict β enabling effect, with the right framing", |
| ) |
|
|
| st.caption( |
| "Robustness checks (in the appendix): instrument variation in HIE " |
| "go-live timing; placebo on pre-2024 outcomes; subgroup logit by " |
| "facility type." |
| ) |
|
|