#!/usr/bin/env python3 """ Recompute every figure in "Four traps in the Sarasota County commercial property record, counted" from the counties' own public bulk files. https://glvtl.com/field-study/sarasota-commercial-record/ By Chris Klebl, Sterling Digital Partners. Free to reuse with attribution. Standard library only. No install step. python3 count_sarasota.py # download both counties, then count python3 count_sarasota.py sarasota.zip # Sarasota from disk, Manatee downloaded python3 count_sarasota.py sarasota.zip nal.zip sdf.zip # all three from disk python3 count_sarasota.py --sarasota-only sarasota.zip python3 count_sarasota.py --manatee-only nal.zip sdf.zip The study covers two counties. The two halves are counted from two different public files and printed in two clearly separated sections below. SARASOTA COUNTY Sarasota County Property Appraiser, SCPA_Parcels_Sales_CSV.zip, from the appraiser's Download Data page. https://www.sarasotapropertyappraiser.gov/downloads/download-data/ Published figures come from the file dated 2026-09-18 10:00:53 GMT, downloaded and counted 2026-09-19. MANATEE COUNTY Florida Department of Revenue, PTO Data Portal, Tax Roll Data Files: the 2026 preliminary NAL and SDF for Manatee, which is DOR county 51. (County 41 is Indian River. Manatee is 51.) https://floridarevenue.com/property/dataportal/Pages/default.aspx?path=/property/dataportal/Documents/PTO%20Data%20Portal/Tax%20Roll%20Data%20Files Published figures come from the files dated 2026-07-27 11:06:51 GMT (NAL) and 2026-07-27 11:03:59 GMT (SDF), downloaded and counted 2026-09-20. WHY THE DOR FILE AND NOT THE COUNTY'S OWN The Manatee County Property Appraiser publishes the same two files at https://www.manateepao.gov/tax-roll-data/ as PUBLIC_NAL.CSV and PUBLIC_SDF.CSV. Those are rebuilt nightly: they carry no version, and the copy you download tomorrow is not the copy any figure was counted from, so no reader can ever get back to the file behind a published number. The DOR copies of the same 2026 preliminary roll are static and dated, so they can be cited and re-fetched. Every figure this script prints for Manatee is derived from the DOR files. Where a figure moved between the two, the difference is reported on the page and in REPORT.md rather than smoothed over. REPRODUCIBILITY The rolls are revised continuously and the DOR republishes each cycle, so a later file will NOT reproduce these numbers exactly. That is expected. What should reproduce is the method: same universe, same columns, same arithmetic. If a number moves, the date moved with it. PRIVACY Aggregate counts only. No owner name, mailing address, fiduciary field, grantor/grantee or legal description is read or printed by this script. The only parcel-level rows it prints are the two worked examples the page already publishes, and for those it prints the parcel identifier, the situs address, the use code and the jurisdiction -- nothing else. NOT REPRODUCIBLE FROM THE ROLL The Rome Ave tax-history figures on the page (the 28-row bill history, the 2018 and 2025 amounts, and the "not sold since 2002" claim) come from the Manatee County Tax Collector's web record, not from the bulk roll. This script cannot and does not reproduce them. It does reproduce the parcel's jurisdiction, situs city and school/non-school split, which is the part the roll actually carries. """ import csv, io, sys, zipfile, statistics, collections, urllib.request # ---------------------------------------------------------------- Sarasota --- URL = "https://www.sarasotapropertyappraiser.gov/downloads/SCPA_Parcels_Sales_CSV.zip" PARCELS = "Parcel_Sales_CSV/Sarasota.csv" SALES = "Parcel_Sales_CSV/ParcelSales.csv" # DOR use codes 1000-4999 are commercial and industrial. CRE_LO, CRE_HI = 1000, 4999 # Qualification codes the appraiser treats as an open-market sale. QUALIFIED = {"0", "01", "02", "03", "04", "05", "06", "07", "6", "7"} SALES_FROM_YEAR = 2024 # ----------------------------------------------------------------- Manatee --- DOR = ("https://floridarevenue.com/property/dataportal/Documents/" "PTO%20Data%20Portal/Tax%20Roll%20Data%20Files") MAN_NAL_URL = DOR + "/NAL/2026P/Manatee%2051%20Preliminary%20NAL%202026.zip" MAN_SDF_URL = DOR + "/SDF/2026P/Manatee%2051%20Preliminary%20SDF%202026.zip" MAN_NAL_MEMBER = "NAL51P202601.csv" MAN_SDF_MEMBER = "SDF51P202601.csv" # DOR use codes 010-049 are commercial and industrial in the DOR standard file. # (Sarasota's own file writes the same codes four digits wide: 1000-4999.) MAN_CRE_LO, MAN_CRE_HI = 10, 49 # Qualification codes the appraiser treats as an open-market sale, as the DOR # standard file writes them. MAN_QUALIFIED = {"0", "00", "01", "02", "03", "04", "05", "06", "07", "1", "2", "3", "4", "5", "6", "7"} # Manatee's sale file carries one roll cycle, 2025 and 2026, not a history. MAN_SALES_FROM_YEAR = 2025 # Jurisdiction comes from TAX_AUTH_CD against the appraiser's own published # table, https://www.manateepao.gov/data/tax_district_in_manatee.csv, where each # code carries a TAXDIST_TYPE. The municipal types are CITY and SPECIAL CITY # DIST. Seven such codes appear on the real property roll; 0020 (City of # Palmetto -- TPP) is tangible personal property and never appears in NAL. # Everything else -- UNINCORPORATED (0001) and UNICORP SPECIALS DIST (the fire # and MSTU districts) -- is unincorporated county. MAN_MUNICIPAL = { "0019", # CITY OF PALMETTO "0021", # CITY OF BRADENTON "0023", # CITY OF ANNA MARIA "0024", # CITY OF HOLMES BEACH "0025", # CITY OF BRADENTON BEACH "0156", # LONGBOAT KEY GULFSIDE DISTRICT "0256", # LONGBOAT KEY BAYSIDE DISTRICT } # Manatee writes its directional as a SUFFIX -- "4208 1ST AVE W" -- not as the # prefix Sarasota puts in its own LOCD column. 88,045 addresses on the roll end # in one of these tokens and 222 lead with one; none does both. PHY_ADDR2 is # empty on every row, so no unit or condominium-wing label can leak into this # column and be mistaken for a direction. MAN_DIRECTIONS = {"N", "S", "E", "W", "NE", "NW", "SE", "SW"} # Worked examples the page publishes. Parcel identifiers only; both are already # public on the appraiser's own search. MAN_EXAMPLE_ADDRESSES = [("4208", "1ST AVE", "BRADENTON")] MAN_EXAMPLE_PARCELS = {"6656500003"} # 1302 Rome Ave csv.field_size_limit(1 << 24) UA = {"User-Agent": "count_sarasota/2.0"} def money(s): s = (s or "").replace(",", "").replace("$", "").strip() try: return float(s) except ValueError: return None def usecode(s): s = (s or "").strip() return int(s) if s.isdigit() and len(s) == 4 else None def intcode(s): s = (s or "").strip() return int(s) if s.isdigit() else None def rows(zf, member): """Stream one CSV member without unpacking the archive to disk.""" with zf.open(member) as fh: yield from csv.DictReader(io.TextIOWrapper(fh, encoding="utf-8", errors="replace")) def fetch(url, path=None): if path: return zipfile.ZipFile(path) sys.stderr.write("downloading %s ...\n" % url) req = urllib.request.Request(url, headers=UA) with urllib.request.urlopen(req, timeout=600) as r: info = r.headers.get("Last-Modified", "not stated") blob = r.read() sys.stderr.write("source file Last-Modified: %s\n\n" % info) return zipfile.ZipFile(io.BytesIO(blob)) pct = lambda n, d: "%.1f%%" % (100.0 * n / d) if d else "n/a" line = lambda k, v: print("%-46s %s" % (k, v)) # ============================================================================ # SARASOTA COUNTY # ============================================================================ def count_sarasota(path=None): zf = fetch(URL, path) total = live = 0 cre_accounts = set() values = [] # (just, assessed, taxable) situs_jurisdiction = collections.Counter() # situs city SARASOTA -> municipality situs_jurisdiction_cre = collections.Counter() directions = collections.defaultdict(set) # (number, street, city) -> {N,S,E,W,""} parcels_at_key = collections.defaultdict(set) for r in rows(zf, PARCELS): total += 1 if not (r["Status"] or "").startswith("OPEN"): continue live += 1 code = usecode(r["STCD"]) is_cre = code is not None and CRE_LO <= code <= CRE_HI city = (r["LOCCITY"] or "").strip().upper() muni = (r["Municipality"] or "").strip() if is_cre: cre_accounts.add(r["ACCOUNT"]) j, a, t = money(r["JUST"]), money(r["ASSD"]), money(r["TXBL"]) if j and j > 0 and a is not None and t is not None: values.append((j, a, t)) if city == "SARASOTA": situs_jurisdiction[muni] += 1 if is_cre: situs_jurisdiction_cre[muni] += 1 num, street = (r["LOCN"] or "").strip(), (r["LOCS"] or "").strip().upper() if num and street: key = (num, street, city) directions[key].add((r["LOCD"] or "").strip().upper()) parcels_at_key[key].add(r["ACCOUNT"]) def sales_window(from_year): by_code = collections.Counter() sales = collections.Counter() nominal = sold = 0 for r in rows(zf, SALES): if r["Account"] not in cre_accounts: continue year = None for tok in (r["SaleDate"] or "").replace("-", "/").split("/"): if len(tok) == 4 and tok.isdigit(): year = int(tok) if year is None or year < from_year: continue sold += 1 q = (r["QualCode"] or "").strip() by_code[q] += 1 sales["qualified" if q in QUALIFIED else "not qualified"] += 1 price = money(r["SalePrice"]) if price is not None and price <= 100: nominal += 1 return sold, sales, by_code, nominal sold, sales, by_code, nominal = sales_window(SALES_FROM_YEAR) # ---------- report ---------- print("=" * 74) print("SARASOTA COUNTY COMMERCIAL PROPERTY RECORD - COUNTS") print("=" * 74) line("rows in Sarasota.csv", f"{total:,}") line("live parcels (Status begins OPEN)", f"{live:,}") line("commercial / industrial (DOR 1000-4999)", f"{len(cre_accounts):,}") print("\n-- Finding 01: sales qualification, %d onward --" % SALES_FROM_YEAR) line("recorded sales on commercial parcels", f"{sold:,}") for k in ("qualified", "not qualified"): line(" %s" % k, f"{sales[k]:,} ({pct(sales[k], sold)})") line(" recorded at $100 or less", f"{nominal:,} ({pct(nominal, sold)})") print(" top codes:") for code, n in by_code.most_common(8): print(" %-6s %6d %s" % (code, n, pct(n, sold))) print("\n-- Finding 02: 'SARASOTA' situs city by jurisdiction --") tot_s = sum(situs_jurisdiction.values()) line("live parcels with SARASOTA situs city", f"{tot_s:,}") for k, n in situs_jurisdiction.most_common(): line(" %s" % (k or "(blank)"), f"{n:,} ({pct(n, tot_s)})") tot_c = sum(situs_jurisdiction_cre.values()) line("commercial subset", f"{tot_c:,}") for k, n in situs_jurisdiction_cre.most_common(): line(" %s" % (k or "(blank)"), f"{n:,} ({pct(n, tot_c)})") print("\n-- Finding 03: assessed against just value (commercial) --") n = len(values) line("parcels with JUST > 0 and ASSD/TXBL present", f"{n:,}") line("median ASSD / JUST", "%.1f%%" % (100 * statistics.median(a / j for j, a, t in values))) line("median TXBL / JUST", "%.1f%%" % (100 * statistics.median(t / j for j, a, t in values))) strictly_below = sum(1 for j, a, t in values if a < j) strictly_above = sum(1 for j, a, t in values if a > j) line(" assessed strictly below just", f"{strictly_below:,} ({pct(strictly_below, n)})") line(" assessed strictly above just", f"{strictly_above:,} ({pct(strictly_above, n)})") for thr in (0.99, 0.95, 0.90, 0.75, 0.50): c = sum(1 for j, a, t in values if a / j < thr) line(" assessed below %d%% of just" % (thr * 100), f"{c:,} ({pct(c, n)})") tj = sum(j for j, a, t in values) ta = sum(a for j, a, t in values) line("total just value", f"${tj:,.0f}") line("total assessed value", f"${ta:,.0f}") line("aggregate gap", f"${tj - ta:,.0f} ({pct(tj - ta, tj)})") print("\n-- Finding 04: directional address collisions --") multi = {k: v for k, v in directions.items() if len([d for d in v if d]) > 1} line("distinct (number, street, city) keys", f"{len(directions):,}") line("keys with 2+ non-blank directions", f"{len(multi):,}") line("parcels behind them", f"{sum(len(parcels_at_key[k]) for k in multi):,}") # The side-by-side table on the page compares the two counties on a matched # sales window. Manatee's sale file holds 2025 and 2026 only, so Sarasota is # cut to 2025 onward for that comparison and only for that comparison. The # 2024-onward figures above are the ones Finding 01 publishes. m_sold, m_sales, _, m_nominal = sales_window(MAN_SALES_FROM_YEAR) print("\n-- Matched window for the county comparison: %d onward --" % MAN_SALES_FROM_YEAR) line("recorded sales on commercial parcels", f"{m_sold:,}") line(" not qualified", f"{m_sales['not qualified']:,} ({pct(m_sales['not qualified'], m_sold)})") line(" recorded at $100 or less", f"{m_nominal:,} ({pct(m_nominal, m_sold)})") return { "cre": len(cre_accounts), "keys": len(directions), "multi": len(multi), "below75": sum(1 for j, a, t in values if a / j < 0.75), "valued": n, "gap": tj - ta, "sold_matched": m_sold, "notqual_matched": m_sales["not qualified"], "nominal_matched": m_nominal, } # ============================================================================ # MANATEE COUNTY # ============================================================================ def man_is_cre(s): c = intcode(s) return c is not None and MAN_CRE_LO <= c <= MAN_CRE_HI def man_address_key(phy_addr1, city): """((house number, street with its direction removed, situs city), direction). Mirrors the Sarasota key, which is house number + street + situs city with the direction taken out into its own field. Manatee has no separate direction column, so the direction is lifted out of the address string: Manatee writes it as a suffix ("4208 1ST AVE W"), with a small number of rows writing it as a prefix instead. """ t = " ".join((phy_addr1 or "").upper().split()).split() if len(t) < 2: return None, None house, rest, direction = t[0], t[1:], "" if len(rest) > 1 and rest[-1] in MAN_DIRECTIONS: direction, rest = rest[-1], rest[:-1] elif len(rest) > 1 and rest[0] in MAN_DIRECTIONS: direction, rest = rest[0], rest[1:] if not rest: return None, None return (house, " ".join(rest), city), direction def count_manatee(nal_path=None, sdf_path=None): nal = fetch(MAN_NAL_URL, nal_path) sdf = fetch(MAN_SDF_URL, sdf_path) total = cre = incorporated = 0 multifam10 = 0 jv_sum = av_sd_sum = av_nsd_sum = 0.0 ratios, school_equals_just = [], 0 below = collections.Counter() city_all = collections.defaultdict(collections.Counter) # city -> inc / uninc city_cre = collections.defaultdict(collections.Counter) sarasota_zip = collections.Counter() directions = collections.defaultdict(set) parcels_at_key = collections.defaultdict(set) examples = collections.defaultdict(list) for r in rows(nal, MAN_NAL_MEMBER): total += 1 commercial = man_is_cre(r["DOR_UC"]) if intcode(r["DOR_UC"]) == 3: multifam10 += 1 muni = (r["TAX_AUTH_CD"] or "").strip() in MAN_MUNICIPAL if muni: incorporated += 1 city = (r["PHY_CITY"] or "").strip().upper() bucket = "incorporated" if muni else "unincorporated" city_all[city][bucket] += 1 if commercial: city_cre[city][bucket] += 1 if city == "SARASOTA": sarasota_zip[(r["PHY_ZIPCD"] or "").strip()[:5]] += 1 key, d = man_address_key(r["PHY_ADDR1"], city) if key: directions[key].add(d) parcels_at_key[key].add(r["PARCEL_ID"]) if key in MAN_EXAMPLE_ADDRESSES: examples[key].append((r["PARCEL_ID"], " ".join((r["PHY_ADDR1"] or "").upper().split()), city, (r["PHY_ZIPCD"] or "").strip()[:5], (r["DOR_UC"] or "").strip(), bucket)) if (r["PARCEL_ID"] or "").strip() in MAN_EXAMPLE_PARCELS: examples["parcel"].append( (r["PARCEL_ID"], " ".join((r["PHY_ADDR1"] or "").upper().split()), city, (r["PHY_ZIPCD"] or "").strip()[:5], (r["DOR_UC"] or "").strip(), bucket, money(r["JV"]), money(r["AV_SD"]), money(r["AV_NSD"]))) if not commercial: continue cre += 1 j, s, n = money(r["JV"]), money(r["AV_SD"]), money(r["AV_NSD"]) if j: jv_sum += j if s is not None: av_sd_sum += s if n is not None: av_nsd_sum += n if j and j > 0 and n is not None: ratios.append(n / j) for thr in (99, 95, 90, 75, 50): if n < j * thr / 100.0: below[thr] += 1 if j and s is not None and s == j: school_equals_just += 1 sold = not_qualified = nominal = 0 by_code = collections.Counter() sale_years = collections.Counter() for r in rows(sdf, MAN_SDF_MEMBER): sale_years[(r["SALE_YR"] or "").strip()] += 1 if not man_is_cre(r["DOR_UC"]): continue year = intcode(r["SALE_YR"]) if year is None or year < MAN_SALES_FROM_YEAR: continue sold += 1 q = (r["QUAL_CD"] or "").strip() by_code[q] += 1 if q not in MAN_QUALIFIED: not_qualified += 1 price = money(r["SALE_PRC"]) if price is not None and price <= 100: nominal += 1 # ---------- report ---------- print("\n") print("=" * 74) print("MANATEE COUNTY COMMERCIAL PROPERTY RECORD - COUNTS") print("=" * 74) line("rows in %s" % MAN_NAL_MEMBER, f"{total:,}") line("commercial / industrial (DOR 010-049)", f"{cre:,} ({pct(cre, total)})") line(" excluded: use 003, multi-family 10+ units", f"{multifam10:,}") line("incorporated (TAX_AUTH_CD is a city code)", f"{incorporated:,} ({pct(incorporated, total)})") line("unincorporated", f"{total - incorporated:,} ({pct(total - incorporated, total)})") print("\n-- Manatee 01: sales qualification, %d onward --" % MAN_SALES_FROM_YEAR) print(" Manatee's sale file holds one roll cycle, not a history:") print(" %s" % ", ".join("%s: %s" % (y or "(blank)", f"{n:,}") for y, n in sorted(sale_years.items()))) line("recorded sales on commercial parcels", f"{sold:,}") line(" not qualified", f"{not_qualified:,} ({pct(not_qualified, sold)})") line(" qualified", f"{sold - not_qualified:,} ({pct(sold - not_qualified, sold)})") line(" recorded at $100 or less", f"{nominal:,} ({pct(nominal, sold)})") print(" top codes:") for code, n in by_code.most_common(8): print(" %-6s %6d %s" % (code or "(blank)", n, pct(n, sold))) print("\n-- Manatee 02: postal city against jurisdiction --") for name in ("SARASOTA", "BRADENTON"): a = city_all[name] t = sum(a.values()) line("parcels with %s situs city" % name, f"{t:,}") line(" unincorporated", f"{a['unincorporated']:,} ({pct(a['unincorporated'], t)})") line(" incorporated", f"{a['incorporated']:,} ({pct(a['incorporated'], t)})") c = city_cre[name] tc = sum(c.values()) line(" commercial subset", f"{tc:,}") line(" unincorporated", f"{c['unincorporated']:,} ({pct(c['unincorporated'], tc)})") line(" incorporated", f"{c['incorporated']:,} ({pct(c['incorporated'], tc)})") print(" SARASOTA situs city by ZIP:") for z, n in sarasota_zip.most_common(5): print(" %-8s %6d %s" % (z or "(blank)", n, pct(n, sum(sarasota_zip.values())))) print("\n-- Manatee 03: the cap, school against non-school (commercial) --") line("total just value (JV)", f"${jv_sum:,.0f}") line("total school assessed (AV_SD)", f"${av_sd_sum:,.0f} ({pct(av_sd_sum, jv_sum)} of just)") line("total non-school assessed (AV_NSD)", f"${av_nsd_sum:,.0f} ({pct(av_nsd_sum, jv_sum)} of just)") line("value sheltered from non-school levies only", f"${av_sd_sum - av_nsd_sum:,.0f}") line("parcels where school assessed = just", f"{school_equals_just:,} ({pct(school_equals_just, cre)})") print("\n-- Manatee 04: assessed against just value (commercial) --") line("parcels counted", f"{len(ratios):,}") line("median AV_NSD / JV", "%.1f%%" % (100 * statistics.median(ratios))) for thr in (99, 95, 90, 75, 50): line(" assessed below %d%% of just" % thr, f"{below[thr]:,} ({pct(below[thr], cre)})") line("aggregate gap (JV - AV_NSD)", f"${jv_sum - av_nsd_sum:,.0f} ({pct(jv_sum - av_nsd_sum, jv_sum)})") print("\n-- Manatee 05: directional address collisions --") print(" key = (house number, street with its direction removed, situs city)") print(" directions counted: %s" % " ".join(sorted(MAN_DIRECTIONS))) multi = {k: v for k, v in directions.items() if len([d for d in v if d]) > 1} line("distinct address keys", f"{len(directions):,}") line("keys with 2+ directions", f"{len(multi):,} ({pct(len(multi), len(directions))})") line("parcels behind them", f"{sum(len(parcels_at_key[k]) for k in multi):,}") print("\n-- Manatee: worked examples the page publishes --") for key in MAN_EXAMPLE_ADDRESSES: print(" %s %s, %s:" % (key[0], key[1], key[2])) for pid, addr, city, z, uc, bucket in sorted(examples.get(key, [])): print(" %-12s %-22s %-11s %-6s use %-4s %s" % (pid, addr, city, z, uc, bucket)) for row in examples.get("parcel", []): pid, addr, city, z, uc, bucket, j, s, n = row print(" parcel %s:" % pid) print(" %-22s %-11s %-6s use %s" % (addr, city, z, uc)) print(" jurisdiction: %s" % bucket) print(" just $%s | school assessed $%s | non-school assessed $%s" % (f"{j:,.0f}", f"{s:,.0f}", f"{n:,.0f}")) print(" sheltered from non-school levies: $%s" % f"{s - n:,.0f}") print(" The tax-bill history for this parcel is NOT in the roll. The page's") print(" 28-row bill history, its 2018 and 2025 amounts and the date of last") print(" sale come from the Manatee County Tax Collector's web record and are") print(" not reproduced by this script.") print("\nAggregate counts only. No owner, mailing, fiduciary, grantor/grantee or") print("legal-description field is read or printed by this script.") return { "total": total, "cre": cre, "incorporated": incorporated, "keys": len(directions), "multi": len(multi), "parcels_multi": sum(len(parcels_at_key[k]) for k in multi), "sold": sold, "not_qualified": not_qualified, "nominal": nominal, "below75": below[75], "jv": jv_sum, "av_sd": av_sd_sum, "av_nsd": av_nsd_sum, "school_equals_just": school_equals_just, "multifam10": multifam10, } # ============================================================================ def main(argv): sarasota_only = "--sarasota-only" in argv manatee_only = "--manatee-only" in argv paths = [a for a in argv if not a.startswith("--")] s = m = None if manatee_only: m = count_manatee(*(paths + [None, None])[:2]) elif sarasota_only: s = count_sarasota(paths[0] if paths else None) else: s = count_sarasota(paths[0] if len(paths) > 0 else None) m = count_manatee(paths[1] if len(paths) > 1 else None, paths[2] if len(paths) > 2 else None) if s and m: print("\n") print("=" * 74) print("SIDE BY SIDE (sales on the matched 2025-onward window)") print("=" * 74) print("%-40s %14s %14s" % ("", "Sarasota", "Manatee")) row = lambda k, a, b: print("%-40s %14s %14s" % (k, a, b)) row("commercial sales not qualified", pct(s["notqual_matched"], s["sold_matched"]), pct(m["not_qualified"], m["sold"])) row(" recorded at $100 or less", pct(s["nominal_matched"], s["sold_matched"]), pct(m["nominal"], m["sold"])) row("commercial parcels", f"{s['cre']:,}", f"{m['cre']:,}") row("assessed below 75% of just", pct(s["below75"], s["valued"]), pct(m["below75"], m["cre"])) row("just-to-assessed gap", f"${s['gap'] / 1e9:,.2f}bn", f"${(m['jv'] - m['av_nsd']) / 1e6:,.1f}m") row("address keys with 2+ directions", f"{s['multi']:,}", f"{m['multi']:,}") row(" as a share of all keys", pct(s["multi"], s["keys"]), pct(m["multi"], m["keys"])) print("\nSarasota is counted from a roll file dated 2026-09-18; Manatee from the") print("DOR 2026 preliminary files dated 2026-07-27. Both dates belong to every") print("figure above.") if __name__ == "__main__": main(sys.argv[1:])