saulsolis09 commited on
Commit
b845f1b
·
1 Parent(s): 94c25c0

Implemented percentage and RURAL vs URBAN columns to summary.

Browse files
Files changed (1) hide show
  1. app.py +105 -40
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
- "County": name,
1017
- "FIPS": fips,
1018
- # true totals from the full frame
1019
- "Total Records in County": len(gfull),
1020
- # how many of those records carry a LegalSubDivision (non-unique)
1021
- "Total Subdivisions": int(gfull["LegalSubDivision"].count())
1022
- if "LegalSubDivision" in gfull.columns else 0,
1023
- "Total Clusters Made": int(g["Cluster_ID"].nunique())
1024
- if "Cluster_ID" in g.columns else 0,
1025
- "Total Blank Cities": int(city_blank.sum()),
1026
- "Total Cities Assignment": int((city_blank & city_fill).sum()),
1027
- "Total Blank Zips": int(zip_blank.sum()),
1028
- "Total Zips Assignment": int((zip_blank & zip_fill).sum()),
1029
- "Total Blank Census": int(tract_blank.sum()),
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
- "County": name, "FIPS": fips,
1289
- "Total Records in County": len(cdf),
1290
- "Total Subdivisions": int(cdf["LegalSubDivision"].count()) if "LegalSubDivision" in cdf.columns else 0,
1291
- "Total Clusters Made": n_clusters,
1292
- "Total Blank Cities": int(city_blank.sum()),
1293
- "Total Cities Assignment": int((city_blank & city_fill).sum()),
1294
- "Total Blank Zips": int(zip_blank.sum()),
1295
- "Total Zips Assignment": int((zip_blank & zip_fill).sum()),
1296
- "Total Blank Census": int(tract_blank.sum()),
1297
- "Total Census Assignment": int((tract_blank & tract_fill).sum()),
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
- summary_cols = ["County", "FIPS", "Total Records in County", "Total Subdivisions",
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))