Spaces:
Sleeping
Sleeping
Commit ·
b845f1b
1
Parent(s): 94c25c0
Implemented percentage and RURAL vs URBAN columns to summary.
Browse files
app.py
CHANGED
|
@@ -74,6 +74,83 @@ STREAM_CHUNK_ROWS = 400_000 # rows per chunk while splitting the input by coun
|
|
| 74 |
# on big states, so it's OFF by default. Set True if you actually need that file.
|
| 75 |
WRITE_RAWFILE = False
|
| 76 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
|
| 78 |
def _mem_mb():
|
| 79 |
"""Current process memory (RSS) in MB. psutil if available, else peak RSS."""
|
|
@@ -1012,29 +1089,21 @@ def run_pipeline(uploaded_files, reference_file, blank_cities_not_in_list,
|
|
| 1012 |
_blank(g, ORIG_ZIP), _blank(g, ORIG_TRACT))
|
| 1013 |
city_fill, zip_fill, tract_fill = (_fill(g, RW_CITY),
|
| 1014 |
_fill(g, RW_ZIP), _fill(g, RW_TRACT))
|
| 1015 |
-
summary_rows.append(
|
| 1016 |
-
|
| 1017 |
-
|
| 1018 |
-
|
| 1019 |
-
|
| 1020 |
-
|
| 1021 |
-
|
| 1022 |
-
|
| 1023 |
-
|
| 1024 |
-
|
| 1025 |
-
|
| 1026 |
-
|
| 1027 |
-
|
| 1028 |
-
|
| 1029 |
-
|
| 1030 |
-
"Total Census Assignment": int((tract_blank & tract_fill).sum()),
|
| 1031 |
-
})
|
| 1032 |
-
|
| 1033 |
-
summary_cols = ["County", "FIPS", "Total Records in County", "Total Subdivisions",
|
| 1034 |
-
"Total Clusters Made", "Total Blank Cities", "Total Cities Assignment",
|
| 1035 |
-
"Total Blank Zips", "Total Zips Assignment", "Total Blank Census",
|
| 1036 |
-
"Total Census Assignment"]
|
| 1037 |
-
summary_df = pd.DataFrame(summary_rows, columns=summary_cols)
|
| 1038 |
_step(log, f"Summary: {len(summary_df)} county row(s).")
|
| 1039 |
|
| 1040 |
summary_path = out_dir / "Summary.csv"
|
|
@@ -1284,18 +1353,18 @@ def process_county(cdf, wl, fips_col, cluster_offset,
|
|
| 1284 |
_blank(g, "DDPPropertyZIPFinal"), _blank(g, "DDPRevisedCensusTract"))
|
| 1285 |
city_fill, zip_fill, tract_fill = (_fill(g, "Replace with Cities"),
|
| 1286 |
_fill(g, "Replace with Zip Codes"), _fill(g, "Replace with Census Tracts"))
|
| 1287 |
-
summary_row =
|
| 1288 |
-
|
| 1289 |
-
|
| 1290 |
-
|
| 1291 |
-
|
| 1292 |
-
|
| 1293 |
-
|
| 1294 |
-
|
| 1295 |
-
|
| 1296 |
-
|
| 1297 |
-
|
| 1298 |
-
|
| 1299 |
|
| 1300 |
return {
|
| 1301 |
"validated": cdf, "audit": audit, "cluster_df": cluster_df,
|
|
@@ -1482,11 +1551,7 @@ def run_streaming(uploaded_files, reference_file, blank_cities_not_in_list,
|
|
| 1482 |
outputs.extend(output_parts)
|
| 1483 |
outputs.extend(bad_files) # malformed-row files for review
|
| 1484 |
|
| 1485 |
-
|
| 1486 |
-
"Total Clusters Made", "Total Blank Cities", "Total Cities Assignment",
|
| 1487 |
-
"Total Blank Zips", "Total Zips Assignment", "Total Blank Census",
|
| 1488 |
-
"Total Census Assignment"]
|
| 1489 |
-
summary_df = pd.DataFrame(summary_rows, columns=summary_cols)
|
| 1490 |
summary_path = out_dir / "Summary.csv"
|
| 1491 |
summary_df.to_csv(summary_path, index=False)
|
| 1492 |
outputs.append(str(summary_path))
|
|
|
|
| 74 |
# on big states, so it's OFF by default. Set True if you actually need that file.
|
| 75 |
WRITE_RAWFILE = False
|
| 76 |
|
| 77 |
+
# Threshold for the RURAL/URBAN label (records <= this -> RURAL, else URBAN).
|
| 78 |
+
RURAL_MAX_RECORDS = 10000
|
| 79 |
+
|
| 80 |
+
# State FIPS prefix (first 2 digits of a county FIPS) -> USPS abbreviation, so the
|
| 81 |
+
# Summary can fill the tracking file's "State" column with no manual lookup.
|
| 82 |
+
STATE_FIPS_ABBR = {
|
| 83 |
+
"01": "AL", "02": "AK", "04": "AZ", "05": "AR", "06": "CA", "08": "CO",
|
| 84 |
+
"09": "CT", "10": "DE", "11": "DC", "12": "FL", "13": "GA", "15": "HI",
|
| 85 |
+
"16": "ID", "17": "IL", "18": "IN", "19": "IA", "20": "KS", "21": "KY",
|
| 86 |
+
"22": "LA", "23": "ME", "24": "MD", "25": "MA", "26": "MI", "27": "MN",
|
| 87 |
+
"28": "MS", "29": "MO", "30": "MT", "31": "NE", "32": "NV", "33": "NH",
|
| 88 |
+
"34": "NJ", "35": "NM", "36": "NY", "37": "NC", "38": "ND", "39": "OH",
|
| 89 |
+
"40": "OK", "41": "OR", "42": "PA", "44": "RI", "45": "SC", "46": "SD",
|
| 90 |
+
"47": "TN", "48": "TX", "49": "UT", "50": "VT", "51": "VA", "53": "WA",
|
| 91 |
+
"54": "WV", "55": "WI", "56": "WY",
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
# Exact column layout of the tracking file's Main sheet (+ RURAL/URBAN at the end),
|
| 95 |
+
# so a whole run can be pasted in one block with NO formulas left in Excel.
|
| 96 |
+
SUMMARY_COLUMNS = [
|
| 97 |
+
"Date Processed", "State", "County", "FIPS",
|
| 98 |
+
"Overall Total Dataset City Assignment", "Overall Total Undetermined City Assignment",
|
| 99 |
+
"Overall Total Dataset Zip Assignment", "Overall Total Undetermined Zip Assignment",
|
| 100 |
+
"Overall Total Dataset Census Tract Assignment", "Overall Total Undetermined Census Tract Assignment",
|
| 101 |
+
"Total Records in County", "Total Subdivisions In County", "Total Clusters Made",
|
| 102 |
+
"Total Undetermined City", "Total City Assignments", "Total Undetermined Zip",
|
| 103 |
+
"Total Zip Assignments", "Total Blank Census", "Total Census Assignments",
|
| 104 |
+
"Added By", "Processing Times/Notes", "Run", "RURAL/URBAN",
|
| 105 |
+
"Total Blanks before run", "% of Total Blanks before run",
|
| 106 |
+
"Total Blanks after run", "% of Total Blanks after run",
|
| 107 |
+
]
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def _pct(num, den):
|
| 111 |
+
"""Percentage num/den*100, rounded to 2 dp; 0 when the denominator is 0
|
| 112 |
+
(replaces the Excel formulas, which would show #DIV/0! in that case)."""
|
| 113 |
+
return round(num / den * 100, 2) if den else 0
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def _summary_row(name, fips, records, subdivisions, clusters,
|
| 117 |
+
undet_city, city_assign, undet_zip, zip_assign, blank_census, census_assign):
|
| 118 |
+
"""Build one fully-computed Summary row matching SUMMARY_COLUMNS. Percentages,
|
| 119 |
+
run date, state, and RURAL/URBAN are all computed here so the tracking file
|
| 120 |
+
needs no formulas. Used by BOTH the streaming and memory paths (no drift)."""
|
| 121 |
+
blanks_before = undet_city + undet_zip + blank_census
|
| 122 |
+
filled = city_assign + zip_assign + census_assign
|
| 123 |
+
blanks_after = blanks_before - filled # blanks the program couldn't resolve
|
| 124 |
+
return {
|
| 125 |
+
"Date Processed": time.strftime("%m/%d/%Y"),
|
| 126 |
+
"State": STATE_FIPS_ABBR.get(str(fips).strip()[:2], ""),
|
| 127 |
+
"County": name,
|
| 128 |
+
"FIPS": fips,
|
| 129 |
+
"Overall Total Dataset City Assignment": _pct(city_assign, records),
|
| 130 |
+
"Overall Total Undetermined City Assignment": _pct(city_assign, undet_city),
|
| 131 |
+
"Overall Total Dataset Zip Assignment": _pct(zip_assign, records),
|
| 132 |
+
"Overall Total Undetermined Zip Assignment": _pct(zip_assign, undet_zip),
|
| 133 |
+
"Overall Total Dataset Census Tract Assignment": _pct(census_assign, records),
|
| 134 |
+
"Overall Total Undetermined Census Tract Assignment": _pct(census_assign, blank_census),
|
| 135 |
+
"Total Records in County": records,
|
| 136 |
+
"Total Subdivisions In County": subdivisions,
|
| 137 |
+
"Total Clusters Made": clusters,
|
| 138 |
+
"Total Undetermined City": undet_city,
|
| 139 |
+
"Total City Assignments": city_assign,
|
| 140 |
+
"Total Undetermined Zip": undet_zip,
|
| 141 |
+
"Total Zip Assignments": zip_assign,
|
| 142 |
+
"Total Blank Census": blank_census,
|
| 143 |
+
"Total Census Assignments": census_assign,
|
| 144 |
+
"Added By": "",
|
| 145 |
+
"Processing Times/Notes": "",
|
| 146 |
+
"Run": "",
|
| 147 |
+
"RURAL/URBAN": "RURAL" if records <= RURAL_MAX_RECORDS else "URBAN",
|
| 148 |
+
"Total Blanks before run": blanks_before,
|
| 149 |
+
"% of Total Blanks before run": round(blanks_before / records, 2) if records else 0,
|
| 150 |
+
"Total Blanks after run": blanks_after,
|
| 151 |
+
"% of Total Blanks after run": round(blanks_after / records, 2) if records else 0,
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
|
| 155 |
def _mem_mb():
|
| 156 |
"""Current process memory (RSS) in MB. psutil if available, else peak RSS."""
|
|
|
|
| 1089 |
_blank(g, ORIG_ZIP), _blank(g, ORIG_TRACT))
|
| 1090 |
city_fill, zip_fill, tract_fill = (_fill(g, RW_CITY),
|
| 1091 |
_fill(g, RW_ZIP), _fill(g, RW_TRACT))
|
| 1092 |
+
summary_rows.append(_summary_row(
|
| 1093 |
+
name, fips,
|
| 1094 |
+
records=len(gfull),
|
| 1095 |
+
subdivisions=int(gfull["LegalSubDivision"].count())
|
| 1096 |
+
if "LegalSubDivision" in gfull.columns else 0,
|
| 1097 |
+
clusters=int(g["Cluster_ID"].nunique()) if "Cluster_ID" in g.columns else 0,
|
| 1098 |
+
undet_city=int(city_blank.sum()),
|
| 1099 |
+
city_assign=int((city_blank & city_fill).sum()),
|
| 1100 |
+
undet_zip=int(zip_blank.sum()),
|
| 1101 |
+
zip_assign=int((zip_blank & zip_fill).sum()),
|
| 1102 |
+
blank_census=int(tract_blank.sum()),
|
| 1103 |
+
census_assign=int((tract_blank & tract_fill).sum()),
|
| 1104 |
+
))
|
| 1105 |
+
|
| 1106 |
+
summary_df = pd.DataFrame(summary_rows, columns=SUMMARY_COLUMNS)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1107 |
_step(log, f"Summary: {len(summary_df)} county row(s).")
|
| 1108 |
|
| 1109 |
summary_path = out_dir / "Summary.csv"
|
|
|
|
| 1353 |
_blank(g, "DDPPropertyZIPFinal"), _blank(g, "DDPRevisedCensusTract"))
|
| 1354 |
city_fill, zip_fill, tract_fill = (_fill(g, "Replace with Cities"),
|
| 1355 |
_fill(g, "Replace with Zip Codes"), _fill(g, "Replace with Census Tracts"))
|
| 1356 |
+
summary_row = _summary_row(
|
| 1357 |
+
name, fips,
|
| 1358 |
+
records=len(cdf),
|
| 1359 |
+
subdivisions=int(cdf["LegalSubDivision"].count()) if "LegalSubDivision" in cdf.columns else 0,
|
| 1360 |
+
clusters=n_clusters,
|
| 1361 |
+
undet_city=int(city_blank.sum()),
|
| 1362 |
+
city_assign=int((city_blank & city_fill).sum()),
|
| 1363 |
+
undet_zip=int(zip_blank.sum()),
|
| 1364 |
+
zip_assign=int((zip_blank & zip_fill).sum()),
|
| 1365 |
+
blank_census=int(tract_blank.sum()),
|
| 1366 |
+
census_assign=int((tract_blank & tract_fill).sum()),
|
| 1367 |
+
)
|
| 1368 |
|
| 1369 |
return {
|
| 1370 |
"validated": cdf, "audit": audit, "cluster_df": cluster_df,
|
|
|
|
| 1551 |
outputs.extend(output_parts)
|
| 1552 |
outputs.extend(bad_files) # malformed-row files for review
|
| 1553 |
|
| 1554 |
+
summary_df = pd.DataFrame(summary_rows, columns=SUMMARY_COLUMNS)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1555 |
summary_path = out_dir / "Summary.csv"
|
| 1556 |
summary_df.to_csv(summary_path, index=False)
|
| 1557 |
outputs.append(str(summary_path))
|