import%20marimo%0A%0A__generated_with%20%3D%20%220.23.9%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Sewer-overflow%20(I%26I)%20vulnerability%20index%20(SP3)%0A%0A%20%20%20%20A%20**relative**%20sewershed-level%20read%20on%20**wet-weather%20sanitary-sewer-overflow**%0A%20%20%20%20risk%20%E2%80%94%20driven%20by%20inflow%20%26%20infiltration%20(cracked%20clay%20pipe%2C%20unsealed%20manholes%2C%0A%20%20%20%20deep%20manholes)%2C%20not%20topography.%20**Limits%20up%20front%3A**%20relative%20percentile%20screen%2C%0A%20%20%20%20not%20an%20absolute%20overflow%20probability%3B%20built%20on%20condition%20attributes%20with%20gaps%0A%20%20%20%20(lift-station%20%60CRITICALITY%60%20is%20unusable%2C%20pipe-lining%20status%20only%20~22%25%20known)%3B%20and%0A%20%20%20%20there%20is%20**no%20overflow-event%20ground%20truth**%20(EPA%20ECHO%20has%20no%20FL%20data%3B%20FDEP%20scrape%0A%20%20%20%20deferred).%20Credibility%20rests%20on%20(1)%20established%20I%26I%20drivers%20and%20(2)%20being%20a%0A%20%20%20%20**distinct**%20layer%20from%20pluvial%20%E2%80%94%20shown%20below.%20The%20surge%20comparison%20is%0A%20%20%20%20**inconclusive**%3A%20at%20%60max_dist_km%3D60%60%20the%20high-water-mark%20coverage%20is%20sparse%2C%20so%20the%0A%20%20%20%20surge%20surface%20is%20near-constant%20across%20sewersheds%20and%20the%20correlation%20is%20uninformative.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20sys%0A%20%20%20%20import%20pathlib%0A%0A%20%20%20%20_root%20%3D%20pathlib.Path(__file__).resolve().parents%5B1%5D%0A%20%20%20%20if%20str(_root)%20not%20in%20sys.path%3A%0A%20%20%20%20%20%20%20%20sys.path.insert(0%2C%20str(_root))%0A%0A%20%20%20%20import%20duckdb%0A%20%20%20%20import%20geopandas%20as%20gpd%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20pandas%20as%20pd%0A%0A%20%20%20%20from%20analysis.catmodel%20import%20exposure%2C%20hazard%0A%20%20%20%20from%20analysis.stormwater%20import%20sewer%0A%0A%20%20%20%20con%20%3D%20duckdb.connect(str(_root%20%2F%20%22analytics.db%22)%2C%20read_only%3DTrue)%0A%20%20%20%20return%20con%2C%20exposure%2C%20gpd%2C%20hazard%2C%20mo%2C%20np%2C%20pd%2C%20plt%2C%20sewer%0A%0A%0A%40app.cell%0Adef%20_(con)%3A%0A%20%20%20%20shed%20%3D%20con.execute(%22SELECT%20*%20FROM%20sewershed_overflow_risk%20WHERE%20sewer_overflow_score%20IS%20NOT%20NULL%22).df()%0A%20%20%20%20shed%5B%5B%22vcp_unlined_pct%22%2C%20%22no_inflow_dish_pct%22%2C%20%22median_mhdepth_ft%22%2C%20%22sewer_overflow_score%22%5D%5D.describe()%0A%20%20%20%20return%20(shed%2C)%0A%0A%0A%40app.cell%0Adef%20_(con%2C%20gpd%2C%20plt)%3A%0A%20%20%20%20%23%20Choropleth%20over%20the%20sub-basin%20polygons.%20Coverage%20is%20honest%20about%20three%20states%3A%0A%20%20%20%20%23%20%20%20coloured%20%3D%20scored%20sewershed%3B%20grey%20%3D%20sewershed%20exists%20but%20unscored%20(missing%20an%0A%20%20%20%20%23%20%20%20I%26I%20component)%3B%20blank%2Fbasemap%20%3D%20no%20sewershed%20at%20all%20(septic%20%2F%20unserved%20area).%0A%20%20%20%20%23%20Left-join%20(not%20inner)%20so%20the%20~9%20unscored%20sewersheds%20render%20as%20grey%20holes%2C%20not%20gaps.%0A%20%20%20%20_df%20%3D%20con.execute(%22SELECT%20BASINID%2C%20geometry_wkt%20FROM%20wastewater_sub_basins%22).df()%0A%20%20%20%20_poly%20%3D%20gpd.GeoDataFrame(_df%2C%20geometry%3Dgpd.GeoSeries.from_wkt(_df%5B%22geometry_wkt%22%5D)%2C%20crs%3D%22EPSG%3A4326%22)%0A%20%20%20%20_sc%20%3D%20con.execute(%22SELECT%20BASINID%2C%20sewer_overflow_score%20FROM%20sewershed_overflow_risk%22).df()%0A%20%20%20%20_poly%20%3D%20_poly.merge(_sc%2C%20on%3D%22BASINID%22%2C%20how%3D%22left%22)%0A%20%20%20%20_scored%20%3D%20_poly%5B_poly%5B%22sewer_overflow_score%22%5D.notna()%5D%0A%20%20%20%20_unscored%20%3D%20_poly%5B_poly%5B%22sewer_overflow_score%22%5D.isna()%5D%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(8%2C%208))%0A%20%20%20%20_unscored.plot(ax%3D_ax%2C%20color%3D%22%23dddddd%22%2C%20edgecolor%3D%22white%22%2C%20linewidth%3D0.3)%0A%20%20%20%20_scored.plot(column%3D%22sewer_overflow_score%22%2C%20cmap%3D%22YlOrRd%22%2C%20legend%3DTrue%2C%20ax%3D_ax%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20edgecolor%3D%22white%22%2C%20linewidth%3D0.3%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20legend_kwds%3D%7B%22label%22%3A%20%22sewer_overflow_score%22%7D)%0A%20%20%20%20_ax.set_title(%22Sewer-overflow%20(I%26I)%20vulnerability%20by%20sewershed%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22grey%20%3D%20sewershed%20present%20but%20unscored%20%C2%B7%20blank%20%3D%20septic%20%2F%20no%20sewershed%22)%0A%20%20%20%20_fig%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(con%2C%20exposure%2C%20hazard%2C%20shed)%3A%0A%20%20%20%20%23%20Distinctness%3A%20aggregate%20SP1%20pluvial%20%2B%20cat-model%20surge%20depth%20to%20the%20sewershed%2C%0A%20%20%20%20%23%20then%20correlate%20against%20sewer_overflow_score.%0A%20%20%20%20pluv%20%3D%20con.execute(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20SELECT%20d.sub_basin_id%20AS%20BASINID%2C%20AVG(r.pluvial_risk_score)%20AS%20avg_pluvial%0A%20%20%20%20%20%20%20%20FROM%20parcel_pluvial_risk%20r%0A%20%20%20%20%20%20%20%20JOIN%20parcel_drainage_features%20d%20ON%20r.parcel_id%20%3D%20d.parcel_id%0A%20%20%20%20%20%20%20%20WHERE%20d.sub_basin_id%20IS%20NOT%20NULL%20AND%20r.pluvial_risk_score%20IS%20NOT%20NULL%0A%20%20%20%20%20%20%20%20GROUP%20BY%20d.sub_basin_id%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20).df()%0A%20%20%20%20exp%20%3D%20exposure.load_exposure(con)%0A%20%20%20%20exp%20%3D%20hazard.assign_flood_depth(exp%2C%20hazard.load_hwm(con)%2C%20max_dist_km%3D60)%0A%20%20%20%20pdf%20%3D%20con.execute(%22SELECT%20parcel_id%2C%20sub_basin_id%20FROM%20parcel_drainage_features%20WHERE%20sub_basin_id%20IS%20NOT%20NULL%22).df()%0A%20%20%20%20surge%20%3D%20(exp.merge(pdf%2C%20on%3D%22parcel_id%22%2C%20how%3D%22inner%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20.query(%22depth_ft%20%3E%20-9999%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20.groupby(%22sub_basin_id%22)%5B%22depth_ft%22%5D.mean()%0A%20%20%20%20%20%20%20%20%20%20%20%20%20.rename(%22avg_surge_depth%22).reset_index().rename(columns%3D%7B%22sub_basin_id%22%3A%20%22BASINID%22%7D))%0A%20%20%20%20m%20%3D%20shed.merge(pluv%2C%20on%3D%22BASINID%22%2C%20how%3D%22left%22).merge(surge%2C%20on%3D%22BASINID%22%2C%20how%3D%22left%22)%0A%20%20%20%20print(%22sewersheds%3A%22%2C%20len(m))%0A%20%20%20%20print(f%22corr(sewer_overflow%2C%20avg_pluvial)%3A%20%7Bm%5B'sewer_overflow_score'%5D.corr(m%5B'avg_pluvial'%5D)%3A.3f%7D%22)%0A%20%20%20%20print(f%22corr(sewer_overflow%2C%20avg_surge_depth)%3A%20%7Bm%5B'sewer_overflow_score'%5D.corr(m%5B'avg_surge_depth'%5D)%3A.3f%7D%22)%0A%20%20%20%20print(%22NOTE%3A%20the%20surge%20surface%20is%20near-constant%20across%20sewersheds%20(sparse%20HWM%20at%20%22%0A%20%20%20%20%20%20%20%20%20%20%22max_dist_km%3D60)%2C%20so%20the%20surge%20correlation%20is%20uninformative%3B%20the%20pluvial%20%22%0A%20%20%20%20%20%20%20%20%20%20%22correlation%20is%20the%20operative%20distinctness%20test.%22)%0A%20%20%20%20return%20(m%2C)%0A%0A%0A%40app.cell%0Adef%20_(m%2C%20plt)%3A%0A%20%20%20%20%23%20Compound%20'double-jeopardy'%3A%20sewersheds%20high%20in%20BOTH%20sewer-overflow%20and%20pluvial.%0A%20%20%20%20_t_sew%20%3D%20m%5B%22sewer_overflow_score%22%5D.quantile(0.75)%0A%20%20%20%20_t_pluv%20%3D%20m%5B%22avg_pluvial%22%5D.quantile(0.75)%0A%20%20%20%20_both%20%3D%20m%5B(m%5B%22sewer_overflow_score%22%5D%20%3E%3D%20_t_sew)%20%26%20(m%5B%22avg_pluvial%22%5D%20%3E%3D%20_t_pluv)%5D%0A%20%20%20%20print(f%22double-jeopardy%20sewersheds%20(top-quartile%20both)%3A%20%7Blen(_both)%7D%20of%20%7Blen(m)%7D%22)%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(7%2C%206))%0A%20%20%20%20_ax.scatter(m%5B%22avg_pluvial%22%5D%2C%20m%5B%22sewer_overflow_score%22%5D%2C%20s%3D18%2C%20color%3D%22%239ecae1%22)%0A%20%20%20%20_ax.scatter(_both%5B%22avg_pluvial%22%5D%2C%20_both%5B%22sewer_overflow_score%22%5D%2C%20s%3D28%2C%20color%3D%22%23cb181d%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20label%3D%22high%20both%22)%0A%20%20%20%20_ax.axhline(_t_sew%2C%20color%3D%22%23999%22%2C%20ls%3D%22--%22%2C%20lw%3D0.8)%3B%20_ax.axvline(_t_pluv%2C%20color%3D%22%23999%22%2C%20ls%3D%22--%22%2C%20lw%3D0.8)%0A%20%20%20%20_ax.set_xlabel(%22avg%20pluvial_risk_score%20(sewershed)%22)%3B%20_ax.set_ylabel(%22sewer_overflow_score%22)%0A%20%20%20%20_ax.set_title(%22Double%20jeopardy%3A%20rain%20ponds%20AND%20the%20sewer%20backs%20up%22)%0A%20%20%20%20_ax.legend()%0A%20%20%20%20_fig%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(np%2C%20pd%2C%20sewer%2C%20shed)%3A%0A%20%20%20%20%23%20Weight-sensitivity%3A%20is%20the%20top-quartile%20sewershed%20set%20stable%20under%20reweighting%3F%0A%20%20%20%20base_inputs%20%3D%20shed.set_index(%22BASINID%22)%5B%0A%20%20%20%20%20%20%20%20%5B%22vcp_unlined_pct%22%2C%20%22no_inflow_dish_pct%22%2C%20%22median_mhdepth_ft%22%5D%5D%0A%0A%20%20%20%20def%20_topq(scores)%3A%0A%20%20%20%20%20%20%20%20thr%20%3D%20np.nanpercentile(scores.values%2C%2075)%0A%20%20%20%20%20%20%20%20return%20set(scores.index%5Bscores.values%20%3E%3D%20thr%5D)%0A%0A%20%20%20%20base%20%3D%20_topq(sewer.sewer_overflow_index(base_inputs)%5B%22sewer_overflow_score%22%5D)%0A%20%20%20%20rows%20%3D%20%5B%5D%0A%20%20%20%20for%20w%20in%20%5B(0.5%2C%200.25%2C%200.25)%2C%20(0.25%2C%200.5%2C%200.25)%2C%20(0.25%2C%200.25%2C%200.5)%5D%3A%0A%20%20%20%20%20%20%20%20alt%20%3D%20sewer.sewer_overflow_index(base_inputs%2C%20w_vcp%3Dw%5B0%5D%2C%20w_inflow%3Dw%5B1%5D%2C%20w_depth%3Dw%5B2%5D)%5B%22sewer_overflow_score%22%5D%0A%20%20%20%20%20%20%20%20s%20%3D%20_topq(alt)%0A%20%20%20%20%20%20%20%20rows.append(%7B%22weights(vcp%2Cinflow%2Cdepth)%22%3A%20w%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22top_quartile_jaccard_vs_equal%22%3A%20round(len(base%20%26%20s)%20%2F%20len(base%20%7C%20s)%2C%203)%7D)%0A%20%20%20%20pd.DataFrame(rows)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
b37c7986447272add03b51cf706c0de2