Data
Data loading, transformation, and registry: MOE propagation, cross-tabulation, NAICS/SOC crosswalk, sample datasets.
Data module for siege_utilities.
After ELE-2437, data/ narrowed to hold statistics primitives
(data.statistics) + the RDH provider shim. Other concerns moved:
Engine abstractions →
siege_utilities.enginesSample datasets + crosswalks →
siege_utilities.reference
Top-level imports at this package are preserved (re-exports from the new homes) for one deprecation window so existing callers still work.
All re-exports use PEP 562 lazy loading so that importing
siege_utilities.data does not pull in heavy transitive dependencies
(geopandas via RDH, etc.) unless the caller actually uses them.
- class siege_utilities.data.RDHClient[source]
Bases:
objectClient for the Redistricting Data Hub download API.
- Parameters:
username (str, optional) – RDH account email. Falls back to
RDH_USERNAMEenv var.password (str, optional) – RDH account password. Falls back to
RDH_PASSWORDenv var.base_url (str, optional) – Override API endpoint (for testing).
cache_dir (Path or str, optional) – Directory for caching downloaded files. Defaults to
~/.cache/siege_utilities/rdh/.
- __init__(username=None, password=None, base_url='https://redistrictingdatahub.org/wp-json/download/list', cache_dir=None)[source]
- credentials_present()[source]
Return True if a username and password are both set.
This is a presence check only – it does NOT contact RDH or verify that the credentials authenticate. A live check is impractical here: the RDH catalog endpoint is WAF-gated to allowlisted ingest hosts (#1115), so from other hosts an authenticated-style request returns the website HTML rather than the JSON API. Callers needing a true liveness check should call
list_datasets()and handle theValueErrorit now raises on an API error envelope.- Return type:
- validate_credentials()[source]
Backward-compatible alias for
credentials_present().Retained for existing callers. The name is a misnomer kept for compatibility: it checks credential presence, not live authentication (see
credentials_present()). (#1115)- Return type:
- list_datasets(states=None, format=None, year=None, dataset_type=None, geography=None, official=None)[source]
List available datasets with optional filters.
- Parameters:
states (list of str, optional) – State abbreviations to filter (e.g.
["VA", "MD"]). Due to API limits, queries more than 4 states sequentially.format (str, optional) –
"csv"or"shp".year (str, optional) – Four-digit year.
dataset_type (str, optional) – Filter keyword in title.
geography (str, optional) – Geographic level filter.
official (bool, optional) – If True, only return official/enacted datasets.
- Return type:
list of RDHDataset
- download_dataset(dataset, output_dir=None, use_cache=True)[source]
Download a dataset file.
- Parameters:
dataset (RDHDataset or str) – Dataset object or direct download URL.
output_dir (Path, optional) – Where to save. Defaults to cache_dir.
use_cache (bool) – If True, skip download if file already exists.
- Return type:
Path to the downloaded file (or extracted directory for ZIPs).
- load_csv(dataset, **kwargs)[source]
Download (if needed) and load a CSV dataset into a DataFrame.
- Parameters:
dataset (RDHDataset, str, or Path) – Dataset to load.
**kwargs – Passed to
pd.read_csv().
- Return type:
pd.DataFrame
- load_shapefile(dataset, **kwargs)[source]
Download (if needed) and load a shapefile into a GeoDataFrame.
- Parameters:
dataset (RDHDataset, str, or Path) – Dataset to load.
**kwargs – Passed to
gpd.read_file().
- Return type:
gpd.GeoDataFrame
- get_enacted_plans(state, chamber='congress', year=None, format='shp')[source]
Find enacted redistricting plan datasets for a state.
- Parameters:
state (str) – Two-letter state abbreviation.
chamber (str) –
"congress","state_senate", or"state_house".year (str, optional) – Filter by year.
format (str, default "shp") – Dataset format filter. Only
"shp"is loadable viaload_shapefile(); other formats are returned as metadata only.
- Return type:
- get_precinct_data(state, year=None, format='shp')[source]
Find precinct-level datasets for a state.
- Parameters:
- Return type:
- get_cvap_data(state, year=None)[source]
Find CVAP (Citizen Voting Age Population) datasets.
- Parameters:
- Return type:
- get_pl94171_data(state, year=None)[source]
Find PL 94-171 redistricting data datasets.
- Parameters:
- Return type:
- static to_crosstab_input(df, geography_col='GEOID', variable_cols=None)[source]
Reshape an RDH DataFrame into long format for cross-tabulation.
Returns a DataFrame with columns
[geography, variable, value]suitable for use withsiege_utilities.data.cross_tabulation.contingency_table().- Parameters:
- Return type:
DataFrame with columns
geography,variable,value.
- class siege_utilities.data.RDHDataset[source]
Bases:
objectMetadata for a single RDH dataset entry.
- __init__(title, url, state='', format='', year='', geography='', dataset_type='', official=False, file_size='', raw=<factory>)
- class siege_utilities.data.RDHDataFormat[source]
-
File format for RDH downloads.
- CSV = 'csv'
- SHAPEFILE = 'shp'
- __new__(value)
- class siege_utilities.data.RDHDatasetType[source]
-
Categories of RDH datasets.
- PL94171 = 'pl94171'
- CVAP = 'cvap'
- ACS5 = 'acs5'
- ELECTION = 'election'
- LEGISLATIVE = 'legislative'
- PRECINCT = 'precinct'
- VOTER_FILE = 'voter_file'
- PROJECTION = 'projection'
- __new__(value)
- siege_utilities.data.polsby_popper(geometry, source_crs=None)[source]
Compute Polsby-Popper compactness score for a geometry.
Score = (4 * pi * area) / perimeter^2 Range: 0 (least compact) to 1 (circle).
- Parameters:
geometry (shapely geometry) – Must be in a projected (equal-area) CRS for meaningful results.
source_crs (optional) – If provided and geographic, geometry is reprojected to equal-area before calculation. Pass the CRS (e.g., “EPSG:4326”) when calling with unprojected data.
- Return type:
- siege_utilities.data.reock(geometry, source_crs=None)[source]
Compute Reock compactness score for a geometry.
Score = area / area_of_minimum_bounding_circle Range: 0 (least compact) to 1 (circle).
- Parameters:
geometry (shapely geometry) – Must be in a projected (equal-area) CRS for meaningful results.
source_crs (optional) – If provided and geographic, geometry is reprojected to equal-area.
- Return type:
- siege_utilities.data.convex_hull_ratio(geometry, source_crs=None)[source]
Compute convex hull ratio for a geometry.
Score = area / convex_hull_area Range: 0 to 1.
- Parameters:
geometry (shapely geometry) – Must be in a projected (equal-area) CRS for meaningful results.
source_crs (optional) – If provided and geographic, geometry is reprojected to equal-area.
- Return type:
- siege_utilities.data.schwartzberg(geometry, source_crs=None)[source]
Compute Schwartzberg compactness score.
Score = 1 / (perimeter / circumference_of_equal_area_circle) Range: 0 to 1.
- Parameters:
geometry (shapely geometry) – Must be in a projected (equal-area) CRS for meaningful results.
source_crs (optional) – If provided and geographic, geometry is reprojected to equal-area.
- Return type:
- siege_utilities.data.compute_compactness(gdf, district_id_col='GEOID', geometry_col='geometry')[source]
Compute all compactness metrics for a GeoDataFrame of districts.
Automatically reprojects to an equal-area CRS (Mollweide) before calculating area-dependent metrics.
- siege_utilities.data.compare_plans(plan_a, plan_b, population_col='TOTPOP', district_id_col='GEOID')[source]
Compare two redistricting plans on key metrics.
- Parameters:
- Return type:
dict with comparison metrics.
- siege_utilities.data.demographic_profile(plan_gdf, census_gdf, district_id_col='GEOID', population_cols=None)[source]
Overlay Census ACS data onto a redistricting plan.
Performs a spatial join between district geometries and Census block/tract geometries, then aggregates demographics per district.
- Parameters:
plan_gdf (GeoDataFrame) – District plan with geometries.
census_gdf (GeoDataFrame) – Census data with geometries and demographic columns.
district_id_col (str) – Column in plan_gdf identifying districts.
population_cols (list of str, optional) – Demographic columns to aggregate. Defaults to common ACS fields.
- Return type:
DataFrame with one row per district and summed demographic columns.
- siege_utilities.data.fetch_enacted_plan(state, chamber='congress', year=None, client=None)[source]
Fetch enacted district plan as GeoDataFrame with boundaries + attributes.
- Parameters:
- Return type:
GeoDataFrame with district boundaries and attributes.
- Raises:
FileNotFoundError – If no matching enacted plan dataset is found.
- siege_utilities.data.fetch_precinct_results(state, year=None, client=None)[source]
Fetch precinct boundaries with election results as GeoDataFrame.
- siege_utilities.data.fetch_cvap(state, year=None, geography='tract', client=None)[source]
Fetch CVAP (Citizen Voting Age Population) data as DataFrame.
- siege_utilities.data.fetch_pl94171(state, year=None, geography='block', client=None)[source]
Fetch PL 94-171 redistricting population data as DataFrame.
- siege_utilities.data.fetch_demographic_summary(state, year=None, client=None)[source]
Fetch ACS 5-year demographic summary by district.
- class siege_utilities.data.CompactnessScores[source]
Bases:
objectCompactness metrics for a district geometry.
- class siege_utilities.data.Engine[source]
Bases:
EnumSupported DataFrame engine back-ends.
- PANDAS = 'pandas'
- DUCKDB = 'duckdb'
- SPARK = 'spark'
- POSTGIS = 'postgis'
- class siege_utilities.data.DataFrameEngine[source]
Bases:
ABCAbstract base class for engine-agnostic DataFrame operations.
- abstractmethod read_csv(path, **kwargs)[source]
Read a CSV file and return a DataFrame-like object.
- abstractmethod read_parquet(path, **kwargs)[source]
Read a Parquet file and return a DataFrame-like object.
- abstractmethod query(sql, **kwargs)[source]
Execute sql and return a DataFrame-like object.
Back-end specific keyword arguments (e.g.
tablefor DuckDB,connectionfor PostGIS) are passed through via kwargs.
- abstractmethod groupby_agg(df, group_cols, agg_dict)[source]
Group df by group_cols and aggregate according to agg_dict.
agg_dict maps column names to aggregation function names (
"sum","mean","count","min","max").
- abstractmethod filter(df, condition)[source]
Return rows of df that satisfy condition.
The type of condition is engine-specific (boolean Series for pandas, SQL expression string for DuckDB/Spark, etc.).
- abstractmethod join(left, right, on, how='inner')[source]
Join left and right on column(s) on using join type how.
- abstractmethod read_spatial(path, *, crs=None, **kwargs)[source]
Read a spatial file (GeoJSON, Shapefile, GeoParquet, GPKG).
Returns an engine-native spatial DataFrame. crs defaults to
EPSG:4326(viageo.crs.get_default_crs).
- abstractmethod spatial_join(left, right, how='inner', predicate='intersects', *, left_geom='geometry', right_geom='geometry')[source]
Spatial join using a geometric predicate.
predicate is one of: intersects, contains, within, touches, crosses, overlaps.
- abstractmethod buffer(df, distance, geometry_col='geometry', *, crs=None)[source]
Buffer geometries by distance (in CRS units).
Returns a new DataFrame with the geometry column replaced by buffered geometries.
- abstractmethod distance(df, other, geometry_col='geometry', other_geom='geometry')[source]
Compute distances between geometries.
other may be a DataFrame (row-aligned pairwise) or a single geometry (shapely object / WKT string).
- abstractmethod to_geodataframe(df, geometry_col='geometry', *, crs=None)[source]
Convert df to a GeoPandas
GeoDataFrame.Always returns a
geopandas.GeoDataFrameregardless of engine.
- from_geodataframe(gdf, geometry_col='geometry')[source]
Convert a GeoPandas
GeoDataFrameto this engine’s native format.The inverse of
to_geodataframe(). Geometry is serialized as WKB-hex (DuckDB) or WKT (Spark/PostGIS) for engine-native spatial operations.Subclasses should override this for engine-specific conversion. The default implementation returns the GeoDataFrame unchanged (suitable for PandasEngine).
- index_points(df, lat_col, lon_col, *, grid=None, resolution=None, level=None, as_token=True)[source]
Compute a grid (H3 or S2) cell ID for each row.
Default: round-trip through pandas. Engines that benefit from a native path (Spark via pandas UDF, DuckDB via SQL when the spatial extension is loaded) should override.
See
siege_utilities.geo.grids.index_points()for the kwarg inference rules – passresolution=for H3,level=for S2, or setgrid=explicitly.Returns the same engine-native frame type as
dfwould have been augmented with – for the default, that’s a column appended to a pandas-converted copy. Engines that override return their native shape.
- index_polygon(geometry, *, grid=None, resolution=None, level=None, max_cells=None, min_level=None, max_level=None, refine=True)[source]
Cover a polygon with grid cells.
Engine-agnostic by construction – the input is a single geometry, not a frame, so no engine-specific dispatch is required at this level. Provided here for API symmetry with
index_points()and so consumer code can acceptengineas a config knob and callengine.index_polygon(...)uniformly.
- point_in_polygon(points_df, polygons_df, *, points_geom='geometry', polygons_geom='geometry')[source]
Find which polygon each point falls within.
Delegates to
spatial_join()withpredicate='within'.
- dissolve(df, by, geometry_col='geometry', **agg_kwargs)[source]
Dissolve/merge geometries grouped by by columns.
Default converts to GeoDataFrame and uses
gdf.dissolve(). Engines may override for native performance.
- load_polygons(path, *, geometry_col='geometry', format='auto', crs=None)[source]
Load polygon geometries from any supported spatial format.
Handles GeoJSON, Shapefile, GeoParquet, GPKG, WKT CSV. Returns engine-native DataFrame with a geometry column.
- Parameters:
path (str) – Path to the spatial file.
geometry_col (str) – Name for the geometry column in the returned DataFrame. If the source data uses a different column name it will be renamed.
format (str) – Spatial format hint.
"auto"(default) infers from the file extension. Explicit values:"geojson","shapefile","gpkg","geopackage","parquet","geoparquet".crs (str or None) – Target CRS. Defaults to
EPSG:4326viaget_default_crs().
- Return type:
- load_points(df, lat_col='lat', lon_col='lon', geometry_col='geometry')[source]
Create point geometries from latitude/longitude columns.
Adds a
geometrycolumn with WKT POINT strings (or native geometry objects, depending on engine). Engines may override for native point construction.
- load_lines(path, *, geometry_col='geometry', format='auto', crs=None)[source]
Load line/multiline geometries from any supported spatial format.
Same interface as
load_polygons()– works for roads, rivers, transit routes, or any linear feature.- Parameters:
path (str) – Path to the spatial file.
geometry_col (str) – Name for the geometry column in the returned DataFrame. If the source data uses a different column name it will be renamed.
format (str) – Spatial format hint.
"auto"(default) infers from the file extension. Explicit values:"geojson","shapefile","gpkg","geopackage","parquet","geoparquet".crs (str or None) – Target CRS. Defaults to
EPSG:4326viaget_default_crs().
- Return type:
- assign_boundaries(points, polygons, *, point_geom='geometry', polygon_geom='geometry', how='left')[source]
Assign each point to its containing polygon(s).
Core DSTK replacement: given addresses (points) and boundaries (polygons), determine which boundary contains each address.
Default delegates to
spatial_join()with predicate=’contains’ (reversed: polygon contains point). Engines may override for broadcast optimization or native spatial indexing.
- intersect_boundaries(poly_a, poly_b, *, a_geom='geometry', b_geom='geometry')[source]
Find overlapping regions between two polygon boundary sets.
Returns all pairs of polygons that intersect, with both sets of attributes. Useful for county-to-district apportionment or boundary change analysis.
Default delegates to
spatial_join()with predicate=’intersects’. Engines may override to compute intersection geometry and area.
- apportion(source_polys, target_polys, weight_col, *, source_geom='geometry', target_geom='geometry')[source]
Apportion a value from source polygons to target polygons by overlap.
Distributes
weight_colfrom source to target proportional to the fraction of source area that overlaps each target. Example: apportion tract population to congressional districts.Default: intersect, compute area ratios via GeoPandas, aggregate. Engines may override for native area computation (Sedona ST_Area, PostGIS ST_Area, etc.).
- nearest(points, targets, *, k=1, max_distance=None, point_geom='geometry', target_geom='geometry')[source]
Find the k nearest targets for each point.
Default: brute-force via GeoPandas sjoin_nearest (supports k=1 only). Engines should override for native spatial indexing (Sedona KNN, PostGIS ST_DWithin + ORDER BY distance, DuckDB spatial) and k>1.
- multi_assign(points, boundary_layers, *, point_geom='geometry')[source]
Assign points to multiple boundary layers simultaneously.
Calls
assign_boundaries()for each layer and renames every polygon-side column to be{layer_name}_{column}so layers do not collide on shared schemas (TIGER layers sharegeoid/namecolumns – without prefixing, the second join silently overwrites the first layer’s columns).- Parameters:
- Returns:
Points enriched with
{layer_name}_*columns for every polygon-side column from each boundary layer. Bare polygon column names never appear in the output.- Return type:
DataFrame
- class siege_utilities.data.PandasEngine[source]
Bases:
DataFrameEngineEngine backed by pandas (and optionally GeoPandas).
- read_csv(path, **kwargs)[source]
Read a CSV file.
**kwargs: Forwarded to
pandas.read_csv().
- read_parquet(path, **kwargs)[source]
Read a Parquet file.
**kwargs: Forwarded to
pandas.read_parquet().
- query(sql, **kwargs)[source]
Execute SQL via
pandas.read_sql.Requires a
connectionkeyword argument (a SQLAlchemy engine or DBAPI2 connection).
- groupby_agg(df, group_cols, agg_dict)[source]
Group df by group_cols and aggregate according to agg_dict.
agg_dict maps column names to aggregation function names (
"sum","mean","count","min","max").
- filter(df, condition)[source]
Return rows of df that satisfy condition.
The type of condition is engine-specific (boolean Series for pandas, SQL expression string for DuckDB/Spark, etc.).
- join(left, right, on, how='inner')[source]
Join left and right on column(s) on using join type how.
- read_spatial(path, *, crs=None, **kwargs)[source]
Read a spatial file.
**kwargs: Forwarded to
geopandas.read_file().
- spatial_join(left, right, how='inner', predicate='intersects', *, left_geom='geometry', right_geom='geometry')[source]
Spatial join using a geometric predicate.
predicate is one of: intersects, contains, within, touches, crosses, overlaps.
- buffer(df, distance, geometry_col='geometry', *, crs=None)[source]
Buffer geometries by distance (in CRS units).
Returns a new DataFrame with the geometry column replaced by buffered geometries.
- class siege_utilities.data.DuckDBEngine[source]
Bases:
DataFrameEngineEngine backed by DuckDB.
- read_csv(path, **kwargs)[source]
Read a CSV via DuckDB’s
read_csv_auto.Engine-specific kwargs (DuckDB’s
read_csv_autooptions likeheader,delim,columns) are not yet forwarded; pass the equivalent options inside a custom SQL viaquery()if you need to override auto-detection.
- read_parquet(path, **kwargs)[source]
Read a Parquet file via DuckDB’s
read_parquet.Engine-specific kwargs are not yet forwarded; use
query()for DuckDB-specific options.
- query(sql, **kwargs)[source]
Execute sql against the DuckDB connection.
If a
tablekeyword is supplied together with a pandas DataFrame value, that DataFrame is first registered as a virtual table so it can be referenced in sql.
- groupby_agg(df, group_cols, agg_dict)[source]
Group df by group_cols and aggregate according to agg_dict.
agg_dict maps column names to aggregation function names (
"sum","mean","count","min","max").
- filter(df, condition)[source]
Return rows of df that satisfy condition.
The type of condition is engine-specific (boolean Series for pandas, SQL expression string for DuckDB/Spark, etc.).
- join(left, right, on, how='inner')[source]
Join left and right on column(s) on using join type how.
- read_spatial(path, *, crs=None, **kwargs)[source]
Read a spatial file via DuckDB’s
ST_Read.**kwargs: Forwarded to
geopandas.read_file()(unused in the current DuckDB path but accepted for interface compatibility).
- spatial_join(left, right, how='inner', predicate='intersects', *, left_geom='geometry', right_geom='geometry')[source]
Spatial join using a geometric predicate.
predicate is one of: intersects, contains, within, touches, crosses, overlaps.
- buffer(df, distance, geometry_col='geometry', *, crs=None)[source]
Buffer geometries by distance (in CRS units).
Returns a new DataFrame with the geometry column replaced by buffered geometries.
- distance(df, other, geometry_col='geometry', other_geom='geometry')[source]
Compute distances between geometries.
other may be a DataFrame (row-aligned pairwise) or a single geometry (shapely object / WKT string).
- class siege_utilities.data.SparkEngine[source]
Bases:
DataFrameEngineEngine backed by PySpark.
- Parameters:
spark (optional) – An existing
SparkSession. When None the engine creates or retrieves the active session on first use.
- read_csv(path, **kwargs)[source]
Read a CSV file via Spark.
**kwargs: Forwarded to Spark’s
DataFrameReader.csv().
- read_parquet(path, **kwargs)[source]
Read a Parquet file via Spark.
**kwargs: Forwarded to Spark’s
DataFrameReader.parquet().
- query(sql, **kwargs)[source]
Execute sql via Spark SQL.
SparkEngine.query accepts no engine-specific kwargs;
self._session.sql()takes no options beyond the SQL text. The**kwargsparameter is present for interface symmetry with DuckDBEngine (which acceptstable/df) and PostGISEngine (which acceptsgeom_col). A non-empty kwargs is a caller error and is raised to surface the mistake rather than silently ignored.
- groupby_agg(df, group_cols, agg_dict)[source]
Group df by group_cols and aggregate according to agg_dict.
agg_dict maps column names to aggregation function names (
"sum","mean","count","min","max").
- filter(df, condition)[source]
Return rows of df that satisfy condition.
The type of condition is engine-specific (boolean Series for pandas, SQL expression string for DuckDB/Spark, etc.).
- join(left, right, on, how='inner')[source]
Join left and right on column(s) on using join type how.
- read_spatial(path, *, crs=None, **kwargs)[source]
Read a spatial file via GeoPandas and convert to a Spark DataFrame.
**kwargs: Forwarded to
geopandas.read_file().
- spatial_join(left, right, how='inner', predicate='intersects', *, left_geom='geometry', right_geom='geometry')[source]
Spatial join using a geometric predicate.
predicate is one of: intersects, contains, within, touches, crosses, overlaps.
- buffer(df, distance, geometry_col='geometry', *, crs=None)[source]
Buffer geometries by distance (in CRS units).
Returns a new DataFrame with the geometry column replaced by buffered geometries.
- distance(df, other, geometry_col='geometry', other_geom='geometry')[source]
Compute distances between geometries.
other may be a DataFrame (row-aligned pairwise) or a single geometry (shapely object / WKT string).
- to_geodataframe(df, geometry_col='geometry', *, crs=None)[source]
Convert df to a GeoPandas
GeoDataFrame.Always returns a
geopandas.GeoDataFrameregardless of engine.
- load_polygons(path, *, geometry_col='geometry', format='auto', crs=None)[source]
Load polygons – Spark-native parquet/json reader.
- load_points(df, lat_col='lat', lon_col='lon', geometry_col='geometry')[source]
Create point geometries – Spark-native WKT string construction.
- class siege_utilities.data.PostGISEngine[source]
Bases:
DataFrameEngineEngine backed by PostGIS via SQLAlchemy + GeoPandas.
- Parameters:
connection_string (str) – A SQLAlchemy connection string, e.g.
"postgresql://user:pass@host:5432/dbname".
- index_points(df, lat_col, lon_col, *, grid=None, resolution=None, level=None, as_token=True)[source]
PostGIS does not natively compute H3 / S2 cell IDs.
For S2-keyed warehouse joins, the recommended pattern is:
Compute cell IDs ONCE during ingest (e.g. via the PandasEngine or in your ETL job using
s2_index_points).Store as a
BIGINTcolumn on the row.For region queries, generate
(range_min, range_max)pairs vias2_cells_to_ranges()and filter withWHERE s2_cell BETWEEN range_min AND range_max.
Computing cell IDs at query time over PostGIS would defeat the whole point – the database can’t index a value it has to compute per-row. Hence this engine raises rather than silently degrade.
- read_csv(path, **kwargs)[source]
Read a CSV file.
**kwargs: Forwarded to
pandas.read_csv().
- read_parquet(path, **kwargs)[source]
Read a parquet file. Returns a
GeoDataFramewhen the file carries GeoParquet metadata, a plainDataFrameotherwise.The decision is by file inspection (pyarrow schema metadata), not by exception-fallback. An earlier implementation tried
gpd.read_parquetfirst and silently fell back topd.read_parqueton any exception – that pattern hid real gpd errors (a corrupt geoparquet file, an out-of-date geopandas) as “this must not be a geoparquet file.” The current path checks the parquet metadata directly so the decision is explicit.
- query(sql, **kwargs)[source]
Execute sql against the PostGIS database.
Pass
geom_col="<column>"to indicate a geometry column in the result; the query then returns aGeoDataFramewith that column parsed as geometry. Omitgeom_col(or pass an empty string) for queries that return no geometry; the result is a plainDataFrame.The Geo-vs-plain decision is made by the caller (via the presence of
geom_col), not by exception-fallback. An earlier implementation triedgpd.read_postgisfirst and silently fell back topd.read_sqlon any exception – that pattern hid real PostGIS errors (a malformed SQL, a missing column) as “this must not have a geometry column.”
- groupby_agg(df, group_cols, agg_dict)[source]
Group df by group_cols and aggregate according to agg_dict.
agg_dict maps column names to aggregation function names (
"sum","mean","count","min","max").
- filter(df, condition)[source]
Return rows of df that satisfy condition.
The type of condition is engine-specific (boolean Series for pandas, SQL expression string for DuckDB/Spark, etc.).
- join(left, right, on, how='inner')[source]
Join left and right on column(s) on using join type how.
- read_spatial(path, *, crs=None, **kwargs)[source]
Read a spatial file or execute a spatial SQL query.
**kwargs: Forwarded to
geopandas.read_file().
- spatial_join(left, right, how='inner', predicate='intersects', *, left_geom='geometry', right_geom='geometry')[source]
Spatial join using a geometric predicate.
predicate is one of: intersects, contains, within, touches, crosses, overlaps.
- buffer(df, distance, geometry_col='geometry', *, crs=None)[source]
Buffer geometries by distance (in CRS units).
Returns a new DataFrame with the geometry column replaced by buffered geometries.
- siege_utilities.data.get_engine(name, **kwargs)[source]
Return an engine instance for the requested back-end.
- Parameters:
- Return type:
Submodules
Deprecation shim — re-exports from siege_utilities.data.statistics.cross_tabulation.
Moved during ELE-2437 (statistics primitives grouped under data/statistics/).
Will be removed in v4.0.0.
- class siege_utilities.data.cross_tabulation.ChiSquareResult[source]
Bases:
objectResult of a chi-square test of independence.
- expected_freq
Expected frequency table under independence.
- Type:
- expected_freq: pandas.DataFrame
- __init__(statistic, p_value, dof, expected_freq)
- Parameters:
statistic (float)
p_value (float)
dof (int)
expected_freq (pandas.DataFrame)
- Return type:
None
- class siege_utilities.data.cross_tabulation.CrossTabSpec[source]
Bases:
objectSpecification for a cross-tabulation operation.
- siege_utilities.data.cross_tabulation.chi_square_test(ct)[source]
Run a chi-square test of independence on a contingency table.
- Parameters:
ct (DataFrame) – Contingency table (without margins). If margins are present (row/column named
'Total'), they are stripped automatically.- Return type:
- siege_utilities.data.cross_tabulation.contingency_table(df, row_var, col_var, value_var=None, weight_var=None, aggfunc='sum', margins=True)[source]
Build a contingency table (cross-tabulation) from df.
- Parameters:
df (DataFrame) – Input data.
row_var (str) – Column for rows.
col_var (str) – Column for columns.
value_var (str, optional) – Column to aggregate. If
None, counts occurrences.weight_var (str, optional) – Column of weights (multiplied into value_var before aggregation).
aggfunc (str) – Aggregation function:
'sum','mean', or'count'.margins (bool) – If
True, add row/column totals.
- Return type:
DataFrame with row_var as index, col_var as columns, and aggregated values.
- siege_utilities.data.cross_tabulation.moe_cross_tab(estimates_df, moes_df, row_var, col_var, value_var, moe_var)[source]
Propagate ACS margins of error through a cross-tabulation.
Computes the MOE of each cell sum using the Census Bureau’s root-sum-of- squares rule. Returns a DataFrame shaped like the contingency table with the propagated MOE in each cell.
- Parameters:
estimates_df (DataFrame) – Data with estimate values.
moes_df (DataFrame) – Data with corresponding MOE values (same structure as estimates_df).
row_var (str) – Row and column variables.
col_var (str) – Row and column variables.
value_var (str) – Name of the estimate column in estimates_df.
moe_var (str) – Name of the MOE column in moes_df.
- Return type:
DataFrame of propagated MOEs with shape matching the contingency table.
- siege_utilities.data.cross_tabulation.rate_table(ct, normalize='all')[source]
Normalize a contingency table to proportions.
- Parameters:
ct (DataFrame) – Contingency table (output of
contingency_table()).normalize (str) –
'all'(divide by grand total),'index'(row proportions),'columns'(column proportions).
- Return type:
DataFrame of proportions summing to 1.0 along the requested axis.
- Raises:
ZeroDivisionError – When the grand total, any row sum, or any column sum is zero (normalization is mathematically undefined).
ValueError – When normalize is not one of the accepted values.
Deprecation shim — re-exports from siege_utilities.data.statistics.moe_propagation.
Moved during ELE-2437. Will be removed in v4.0.0.
- class siege_utilities.data.moe_propagation.Estimate[source]
Bases:
objectAn ACS estimate with its margin of error at 90% confidence.
- property cv: float
Coefficient of variation (SE / |estimate|), as a proportion.
- siege_utilities.data.moe_propagation.flag_unreliable(estimates, cv_threshold=0.4)[source]
Return indices and Estimates that exceed cv_threshold.
- siege_utilities.data.moe_propagation.moe_difference(a, b)[source]
Propagate MOE through the difference a - b.
Uses the same root-sum-of-squares formula as
moe_sum().
- siege_utilities.data.moe_propagation.moe_product(a, b)[source]
Propagate MOE through a product a * b (independent estimates).
Uses the delta-method approximation:
SE_ab = sqrt(a^2 * SE_b^2 + b^2 * SE_a^2)
- siege_utilities.data.moe_propagation.moe_proportion(numerator, denominator)[source]
Propagate MOE for a proportion (numerator / denominator).
Uses the Census Bureau’s recommended formula for proportions where the numerator is a subset of the denominator:
SE_p = sqrt(SE_num^2 - p^2 * SE_den^2) / denominator
If the radicand is negative (which can happen with small samples), falls back to the conservative approximation:
SE_p = sqrt(SE_num^2 + p^2 * SE_den^2) / denominator
- Parameters:
- Returns:
Proportion with propagated MOE, bounded to [0, 1].
- Return type:
- Raises:
ZeroDivisionError – If the denominator estimate is zero.
- siege_utilities.data.moe_propagation.moe_ratio(numerator, denominator)[source]
Propagate MOE for a ratio of non-nested estimates.
Uses the delta method:
SE_r = sqrt(SE_num^2 + r^2 * SE_den^2) / denominator
- Parameters:
- Return type:
- Raises:
ZeroDivisionError – If the denominator estimate is zero.
- siege_utilities.data.moe_propagation.moe_sum(estimates)[source]
Propagate MOE through a sum of independent ACS estimates.
MOE_sum = sqrt(sum(MOE_i^2))
Deprecation shim — re-exports from siege_utilities.reference.naics_soc_crosswalk.
Moved during ELE-2437 (reference data grouped under reference/).
Will be removed in v4.0.0.
- class siege_utilities.data.naics_soc_crosswalk.NAICSCode[source]
Bases:
objectA NAICS industry code with hierarchy metadata.
- siege_utilities.data.naics_soc_crosswalk.parse_naics(code)[source]
Parse a NAICS code string into a
NAICSCode.- Parameters:
code (str) – 2-6 digit NAICS code.
- Return type:
- Raises:
ValueError – If the code is not 2-6 digits.
- siege_utilities.data.naics_soc_crosswalk.naics_ancestors(code)[source]
Return ancestor codes from sector down to the given code.
>>> naics_ancestors("541511") ['54', '541', '5415', '54151', '541511']
- siege_utilities.data.naics_soc_crosswalk.naics_to_sector(code)[source]
Return (sector_code, sector_title) for any NAICS code.
- siege_utilities.data.naics_soc_crosswalk.crosswalk_naics(code, from_year=2017, to_year=2022)[source]
Map a NAICS code from one revision to another.
- class siege_utilities.data.naics_soc_crosswalk.SOCCode[source]
Bases:
objectA Standard Occupational Classification code.
- siege_utilities.data.naics_soc_crosswalk.soc_to_major_group(code)[source]
Return (major_code, title) for any SOC code.
- siege_utilities.data.naics_soc_crosswalk.fuzzy_match_naics(text, candidates=None, threshold=0.5)[source]
Simple token-overlap fuzzy match of text against NAICS sector titles.
- Parameters:
- Returns:
Sorted by score descending.
- Return type:
- siege_utilities.data.naics_soc_crosswalk.get_naics_lookup()[source]
Return a combined NAICS lookup table (sectors + subsectors).
- siege_utilities.data.naics_soc_crosswalk.get_soc_lookup()[source]
Return a combined SOC lookup table (major groups + minor groups).
- siege_utilities.data.naics_soc_crosswalk.naics_title(code)[source]
Look up the title for a NAICS code at any level.
Checks subsector-level first, then falls back to sector. Returns ‘Unknown’ if not found.
- siege_utilities.data.naics_soc_crosswalk.soc_title(code)[source]
Look up the title for an SOC code at any level.
Checks minor-group level first, then falls back to major group. Returns ‘Unknown’ if not found.
- siege_utilities.data.naics_soc_crosswalk.filter_by_naics(records, naics_prefix)[source]
Filter NLRB case records by NAICS code prefix.
Works with any iterable of objects that have a
naics_codeattribute (NLRBCaseRecord, NLRBCase model instances, or dicts with ‘naics_code’).- Parameters:
records – Iterable of records to filter.
naics_prefix (str) – NAICS prefix to match (e.g., ‘622’ for hospitals, ‘62’ for all health care).
- Returns:
List of matching records.
- Return type:
Example:
# Show all elections in NAICS 622 (hospitals) hospital_cases = filter_by_naics(result.cases, "622")
- siege_utilities.data.naics_soc_crosswalk.filter_by_naics_sector(records, sector_code)[source]
Filter records by 2-digit NAICS sector code.
- siege_utilities.data.naics_soc_crosswalk.group_by_naics_sector(records)[source]
Group records by their 2-digit NAICS sector code.
Deprecation shim — re-exports from siege_utilities.geo.providers.redistricting_data_hub.
RDH is an external spatial data source; moved to geo/providers/ under
ELE-2438 (D3) so all spatial sources live in one place. Will be removed
in v4.0.0.
- class siege_utilities.data.redistricting_data_hub.RDHClient[source]
Bases:
objectClient for the Redistricting Data Hub download API.
- Parameters:
username (str, optional) – RDH account email. Falls back to
RDH_USERNAMEenv var.password (str, optional) – RDH account password. Falls back to
RDH_PASSWORDenv var.base_url (str, optional) – Override API endpoint (for testing).
cache_dir (Path or str, optional) – Directory for caching downloaded files. Defaults to
~/.cache/siege_utilities/rdh/.
- __init__(username=None, password=None, base_url='https://redistrictingdatahub.org/wp-json/download/list', cache_dir=None)[source]
- credentials_present()[source]
Return True if a username and password are both set.
This is a presence check only – it does NOT contact RDH or verify that the credentials authenticate. A live check is impractical here: the RDH catalog endpoint is WAF-gated to allowlisted ingest hosts (#1115), so from other hosts an authenticated-style request returns the website HTML rather than the JSON API. Callers needing a true liveness check should call
list_datasets()and handle theValueErrorit now raises on an API error envelope.- Return type:
- validate_credentials()[source]
Backward-compatible alias for
credentials_present().Retained for existing callers. The name is a misnomer kept for compatibility: it checks credential presence, not live authentication (see
credentials_present()). (#1115)- Return type:
- list_datasets(states=None, format=None, year=None, dataset_type=None, geography=None, official=None)[source]
List available datasets with optional filters.
- Parameters:
states (list of str, optional) – State abbreviations to filter (e.g.
["VA", "MD"]). Due to API limits, queries more than 4 states sequentially.format (str, optional) –
"csv"or"shp".year (str, optional) – Four-digit year.
dataset_type (str, optional) – Filter keyword in title.
geography (str, optional) – Geographic level filter.
official (bool, optional) – If True, only return official/enacted datasets.
- Return type:
list of RDHDataset
- download_dataset(dataset, output_dir=None, use_cache=True)[source]
Download a dataset file.
- Parameters:
dataset (RDHDataset or str) – Dataset object or direct download URL.
output_dir (Path, optional) – Where to save. Defaults to cache_dir.
use_cache (bool) – If True, skip download if file already exists.
- Return type:
Path to the downloaded file (or extracted directory for ZIPs).
- load_csv(dataset, **kwargs)[source]
Download (if needed) and load a CSV dataset into a DataFrame.
- Parameters:
dataset (RDHDataset, str, or Path) – Dataset to load.
**kwargs – Passed to
pd.read_csv().
- Return type:
pd.DataFrame
- load_shapefile(dataset, **kwargs)[source]
Download (if needed) and load a shapefile into a GeoDataFrame.
- Parameters:
dataset (RDHDataset, str, or Path) – Dataset to load.
**kwargs – Passed to
gpd.read_file().
- Return type:
gpd.GeoDataFrame
- get_enacted_plans(state, chamber='congress', year=None, format='shp')[source]
Find enacted redistricting plan datasets for a state.
- Parameters:
state (str) – Two-letter state abbreviation.
chamber (str) –
"congress","state_senate", or"state_house".year (str, optional) – Filter by year.
format (str, default "shp") – Dataset format filter. Only
"shp"is loadable viaload_shapefile(); other formats are returned as metadata only.
- Return type:
- get_precinct_data(state, year=None, format='shp')[source]
Find precinct-level datasets for a state.
- Parameters:
- Return type:
- get_cvap_data(state, year=None)[source]
Find CVAP (Citizen Voting Age Population) datasets.
- Parameters:
- Return type:
- get_pl94171_data(state, year=None)[source]
Find PL 94-171 redistricting data datasets.
- Parameters:
- Return type:
- static to_crosstab_input(df, geography_col='GEOID', variable_cols=None)[source]
Reshape an RDH DataFrame into long format for cross-tabulation.
Returns a DataFrame with columns
[geography, variable, value]suitable for use withsiege_utilities.data.cross_tabulation.contingency_table().- Parameters:
- Return type:
DataFrame with columns
geography,variable,value.
- class siege_utilities.data.redistricting_data_hub.RDHDataset[source]
Bases:
objectMetadata for a single RDH dataset entry.
- __init__(title, url, state='', format='', year='', geography='', dataset_type='', official=False, file_size='', raw=<factory>)
- class siege_utilities.data.redistricting_data_hub.RDHDataFormat[source]
-
File format for RDH downloads.
- CSV = 'csv'
- SHAPEFILE = 'shp'
- __new__(value)
- class siege_utilities.data.redistricting_data_hub.RDHDatasetType[source]
-
Categories of RDH datasets.
- PL94171 = 'pl94171'
- CVAP = 'cvap'
- ACS5 = 'acs5'
- ELECTION = 'election'
- LEGISLATIVE = 'legislative'
- PRECINCT = 'precinct'
- VOTER_FILE = 'voter_file'
- PROJECTION = 'projection'
- __new__(value)
- siege_utilities.data.redistricting_data_hub.polsby_popper(geometry, source_crs=None)[source]
Compute Polsby-Popper compactness score for a geometry.
Score = (4 * pi * area) / perimeter^2 Range: 0 (least compact) to 1 (circle).
- Parameters:
geometry (shapely geometry) – Must be in a projected (equal-area) CRS for meaningful results.
source_crs (optional) – If provided and geographic, geometry is reprojected to equal-area before calculation. Pass the CRS (e.g., “EPSG:4326”) when calling with unprojected data.
- Return type:
- siege_utilities.data.redistricting_data_hub.reock(geometry, source_crs=None)[source]
Compute Reock compactness score for a geometry.
Score = area / area_of_minimum_bounding_circle Range: 0 (least compact) to 1 (circle).
- Parameters:
geometry (shapely geometry) – Must be in a projected (equal-area) CRS for meaningful results.
source_crs (optional) – If provided and geographic, geometry is reprojected to equal-area.
- Return type:
- siege_utilities.data.redistricting_data_hub.convex_hull_ratio(geometry, source_crs=None)[source]
Compute convex hull ratio for a geometry.
Score = area / convex_hull_area Range: 0 to 1.
- Parameters:
geometry (shapely geometry) – Must be in a projected (equal-area) CRS for meaningful results.
source_crs (optional) – If provided and geographic, geometry is reprojected to equal-area.
- Return type:
- siege_utilities.data.redistricting_data_hub.schwartzberg(geometry, source_crs=None)[source]
Compute Schwartzberg compactness score.
Score = 1 / (perimeter / circumference_of_equal_area_circle) Range: 0 to 1.
- Parameters:
geometry (shapely geometry) – Must be in a projected (equal-area) CRS for meaningful results.
source_crs (optional) – If provided and geographic, geometry is reprojected to equal-area.
- Return type:
- siege_utilities.data.redistricting_data_hub.compute_compactness(gdf, district_id_col='GEOID', geometry_col='geometry')[source]
Compute all compactness metrics for a GeoDataFrame of districts.
Automatically reprojects to an equal-area CRS (Mollweide) before calculating area-dependent metrics.
- class siege_utilities.data.redistricting_data_hub.CompactnessScores[source]
Bases:
objectCompactness metrics for a district geometry.
- siege_utilities.data.redistricting_data_hub.compare_plans(plan_a, plan_b, population_col='TOTPOP', district_id_col='GEOID')[source]
Compare two redistricting plans on key metrics.
- Parameters:
- Return type:
dict with comparison metrics.
- siege_utilities.data.redistricting_data_hub.demographic_profile(plan_gdf, census_gdf, district_id_col='GEOID', population_cols=None)[source]
Overlay Census ACS data onto a redistricting plan.
Performs a spatial join between district geometries and Census block/tract geometries, then aggregates demographics per district.
- Parameters:
plan_gdf (GeoDataFrame) – District plan with geometries.
census_gdf (GeoDataFrame) – Census data with geometries and demographic columns.
district_id_col (str) – Column in plan_gdf identifying districts.
population_cols (list of str, optional) – Demographic columns to aggregate. Defaults to common ACS fields.
- Return type:
DataFrame with one row per district and summed demographic columns.
- siege_utilities.data.redistricting_data_hub.fetch_enacted_plan(state, chamber='congress', year=None, client=None)[source]
Fetch enacted district plan as GeoDataFrame with boundaries + attributes.
- Parameters:
- Return type:
GeoDataFrame with district boundaries and attributes.
- Raises:
FileNotFoundError – If no matching enacted plan dataset is found.
- siege_utilities.data.redistricting_data_hub.fetch_precinct_results(state, year=None, client=None)[source]
Fetch precinct boundaries with election results as GeoDataFrame.
- siege_utilities.data.redistricting_data_hub.fetch_cvap(state, year=None, geography='tract', client=None)[source]
Fetch CVAP (Citizen Voting Age Population) data as DataFrame.
- siege_utilities.data.redistricting_data_hub.fetch_pl94171(state, year=None, geography='block', client=None)[source]
Fetch PL 94-171 redistricting population data as DataFrame.
- siege_utilities.data.redistricting_data_hub.fetch_demographic_summary(state, year=None, client=None)[source]
Fetch ACS 5-year demographic summary by district.
Deprecation shim — re-exports from siege_utilities.reference.sample_data.
Moved during ELE-2437. Will be removed in v4.0.0.
- siege_utilities.data.sample_data.list_available_datasets()[source]
List all available sample datasets with descriptions and metadata.
- siege_utilities.data.sample_data.get_dataset_info(dataset_name)[source]
Get detailed information about a specific dataset.
- siege_utilities.data.sample_data.load_sample_data(dataset_name, **kwargs)[source]
Load a sample dataset by name.
- Parameters:
dataset_name (str) – Name of the dataset to load
**kwargs – Additional arguments for dataset generation
- Returns:
DataFrame or GeoDataFrame with sample data
- Raises:
ValueError – If dataset name is not recognized
ImportError – If required dependencies are not available
- Return type:
- siege_utilities.data.sample_data.get_census_boundaries(year=2020, geographic_level='tract', state_fips=None, county_fips=None)[source]
Download Census geographic boundaries.
- Parameters:
- Returns:
GeoDataFrame with boundaries.
- Raises:
ImportError – If geo extras are not installed.
RuntimeError – If no boundaries are returned from Census source.
- Return type:
- siege_utilities.data.sample_data.get_census_data(year=2020, data_type='demographics', geographic_level='tract', state_fips=None, county_fips=None)[source]
Get Census demographic/attribute data.
- Parameters:
- Returns:
DataFrame with Census data or None if failed
- Return type:
pandas.DataFrame | None
- siege_utilities.data.sample_data.join_boundaries_and_data(boundaries, data, boundary_id_col='geoid', data_id_col='geoid')[source]
Join geographic boundaries with attribute data.
- Parameters:
boundaries (geopandas.GeoDataFrame) – GeoDataFrame with geographic boundaries
data (pandas.DataFrame) – DataFrame with attribute data
boundary_id_col (str) – Column name for boundary identifiers
data_id_col (str) – Column name for data identifiers
- Returns:
GeoDataFrame with joined data.
- Raises:
KeyError – If the specified ID columns do not exist.
ValueError – If the join produces zero records.
- Return type:
- siege_utilities.data.sample_data.create_sample_dataset(year=2020, geographic_level='tract', state_fips='06', county_fips='037', include_geometry=True)[source]
Create a real-world sample dataset by combining boundaries and data.
- Parameters:
- Returns:
Combined dataset.
- Raises:
RuntimeError – If boundaries cannot be retrieved.
NotImplementedError – If Census data retrieval is not yet implemented.
- Return type:
- siege_utilities.data.sample_data.get_census_county_sample(state_fips='06', county_fips='037', tract_count=5)[source]
Generate a sample county dataset with multiple tracts and synthetic data.
- Parameters:
- Returns:
DataFrame or GeoDataFrame with county data
- Return type:
- siege_utilities.data.sample_data.get_metropolitan_sample(cbsa_code='31080', county_count=3)[source]
Generate a metropolitan area sample with multiple counties.
- Parameters:
- Returns:
DataFrame or GeoDataFrame with metro data
- Return type:
- siege_utilities.data.sample_data.generate_synthetic_population(demographics=None, size=1000, geography_level='tract', tract_info=None, include_names=True, include_addresses=True, include_income=True, include_education=True)[source]
Generate synthetic population data matching real demographic patterns.
- Parameters:
demographics (Dict | None) – Dictionary of demographic percentages
size (int) – Number of people to generate
geography_level (str) – Geographic level (tract, county, etc.)
tract_info (Dict | None) – Additional tract information
include_names (bool) – Whether to include synthetic names
include_addresses (bool) – Whether to include synthetic addresses
include_income (bool) – Whether to include synthetic income
include_education (bool) – Whether to include synthetic education
- Returns:
DataFrame with synthetic population data
- Return type:
- siege_utilities.data.sample_data.generate_synthetic_businesses(business_count=500, industry_distribution=None, include_locations=True)[source]
Generate synthetic business data with realistic industry patterns.
- Parameters:
- Returns:
DataFrame with synthetic business data
- Return type:
- siege_utilities.data.sample_data.generate_synthetic_housing(housing_count=300, property_types=None, include_coordinates=True, locale='us', lat_range=None, lon_range=None, area_unit=None, area_range=None, value_range=None)[source]
Generate synthetic housing data with realistic property patterns.
- Parameters:
housing_count (int) – Number of housing units to generate
property_types (Dict | None) – Dictionary of property type percentages. If None, uses locale-appropriate defaults.
include_coordinates (bool) – Whether to include synthetic coordinates
locale (str) – Country/region preset (‘us’, ‘uk’, ‘de’, ‘fr’, ‘au’). Controls address format, coordinate bounds, units, and value ranges.
lat_range (Tuple[float, float] | None) – Override latitude bounds (min, max)
lon_range (Tuple[float, float] | None) – Override longitude bounds (min, max)
area_unit (str | None) – Override area column name (e.g. ‘square_feet’, ‘square_metres’)
area_range (Tuple[int, int] | None) – Override area range (min, max)
value_range (Tuple[int, int] | None) – Override property value range (min, max)
- Returns:
DataFrame with synthetic housing data
- Return type:
Statistics
Statistical primitives for DataFrame analysis.
cross_tabulation— contingency tables, chi-square, proportion mathmoe_propagation— ACS margin-of-error propagation through derived estimates
Submodules load on first attribute access via PEP 562 __getattr__.
- class siege_utilities.data.statistics.ChiSquareResult[source]
Bases:
objectResult of a chi-square test of independence.
- expected_freq
Expected frequency table under independence.
- Type:
- expected_freq: pandas.DataFrame
- __init__(statistic, p_value, dof, expected_freq)
- Parameters:
statistic (float)
p_value (float)
dof (int)
expected_freq (pandas.DataFrame)
- Return type:
None
- class siege_utilities.data.statistics.CrossTabSpec[source]
Bases:
objectSpecification for a cross-tabulation operation.
- siege_utilities.data.statistics.chi_square_test(ct)[source]
Run a chi-square test of independence on a contingency table.
- Parameters:
ct (DataFrame) – Contingency table (without margins). If margins are present (row/column named
'Total'), they are stripped automatically.- Return type:
- siege_utilities.data.statistics.contingency_table(df, row_var, col_var, value_var=None, weight_var=None, aggfunc='sum', margins=True)[source]
Build a contingency table (cross-tabulation) from df.
- Parameters:
df (DataFrame) – Input data.
row_var (str) – Column for rows.
col_var (str) – Column for columns.
value_var (str, optional) – Column to aggregate. If
None, counts occurrences.weight_var (str, optional) – Column of weights (multiplied into value_var before aggregation).
aggfunc (str) – Aggregation function:
'sum','mean', or'count'.margins (bool) – If
True, add row/column totals.
- Return type:
DataFrame with row_var as index, col_var as columns, and aggregated values.
- siege_utilities.data.statistics.moe_cross_tab(estimates_df, moes_df, row_var, col_var, value_var, moe_var)[source]
Propagate ACS margins of error through a cross-tabulation.
Computes the MOE of each cell sum using the Census Bureau’s root-sum-of- squares rule. Returns a DataFrame shaped like the contingency table with the propagated MOE in each cell.
- Parameters:
estimates_df (DataFrame) – Data with estimate values.
moes_df (DataFrame) – Data with corresponding MOE values (same structure as estimates_df).
row_var (str) – Row and column variables.
col_var (str) – Row and column variables.
value_var (str) – Name of the estimate column in estimates_df.
moe_var (str) – Name of the MOE column in moes_df.
- Return type:
DataFrame of propagated MOEs with shape matching the contingency table.
- siege_utilities.data.statistics.rate_table(ct, normalize='all')[source]
Normalize a contingency table to proportions.
- Parameters:
ct (DataFrame) – Contingency table (output of
contingency_table()).normalize (str) –
'all'(divide by grand total),'index'(row proportions),'columns'(column proportions).
- Return type:
DataFrame of proportions summing to 1.0 along the requested axis.
- Raises:
ZeroDivisionError – When the grand total, any row sum, or any column sum is zero (normalization is mathematically undefined).
ValueError – When normalize is not one of the accepted values.
- class siege_utilities.data.statistics.Estimate[source]
Bases:
objectAn ACS estimate with its margin of error at 90% confidence.
- property cv: float
Coefficient of variation (SE / |estimate|), as a proportion.
- siege_utilities.data.statistics.flag_unreliable(estimates, cv_threshold=0.4)[source]
Return indices and Estimates that exceed cv_threshold.
- siege_utilities.data.statistics.moe_difference(a, b)[source]
Propagate MOE through the difference a - b.
Uses the same root-sum-of-squares formula as
moe_sum().
- siege_utilities.data.statistics.moe_product(a, b)[source]
Propagate MOE through a product a * b (independent estimates).
Uses the delta-method approximation:
SE_ab = sqrt(a^2 * SE_b^2 + b^2 * SE_a^2)
- siege_utilities.data.statistics.moe_proportion(numerator, denominator)[source]
Propagate MOE for a proportion (numerator / denominator).
Uses the Census Bureau’s recommended formula for proportions where the numerator is a subset of the denominator:
SE_p = sqrt(SE_num^2 - p^2 * SE_den^2) / denominator
If the radicand is negative (which can happen with small samples), falls back to the conservative approximation:
SE_p = sqrt(SE_num^2 + p^2 * SE_den^2) / denominator
- Parameters:
- Returns:
Proportion with propagated MOE, bounded to [0, 1].
- Return type:
- Raises:
ZeroDivisionError – If the denominator estimate is zero.
- siege_utilities.data.statistics.moe_ratio(numerator, denominator)[source]
Propagate MOE for a ratio of non-nested estimates.
Uses the delta method:
SE_r = sqrt(SE_num^2 + r^2 * SE_den^2) / denominator
- Parameters:
- Return type:
- Raises:
ZeroDivisionError – If the denominator estimate is zero.
- siege_utilities.data.statistics.moe_sum(estimates)[source]
Propagate MOE through a sum of independent ACS estimates.
MOE_sum = sqrt(sum(MOE_i^2))
Cross-tabulation engine for geographic longitudinal analysis.
Provides functions to build contingency tables from joined datasets, compute rates and proportions, run chi-square independence tests, and propagate ACS margins of error through cross-tabulation operations.
Designed to consume output from:
- siege_utilities.data.redistricting_data_hub.RDHClient.to_crosstab_input()
- siege_utilities.geo.timeseries.longitudinal_data.LongitudinalAligner
Usage:
from siege_utilities.data.statistics.cross_tabulation import contingency_table, chi_square_test
ct = contingency_table(df, row_var="race", col_var="county")
result = chi_square_test(ct)
print(result.p_value)
- class siege_utilities.data.statistics.cross_tabulation.ChiSquareResult[source]
Bases:
objectResult of a chi-square test of independence.
- expected_freq
Expected frequency table under independence.
- Type:
- expected_freq: pandas.DataFrame
- __init__(statistic, p_value, dof, expected_freq)
- Parameters:
statistic (float)
p_value (float)
dof (int)
expected_freq (pandas.DataFrame)
- Return type:
None
- class siege_utilities.data.statistics.cross_tabulation.CrossTabSpec[source]
Bases:
objectSpecification for a cross-tabulation operation.
- siege_utilities.data.statistics.cross_tabulation.chi_square_test(ct)[source]
Run a chi-square test of independence on a contingency table.
- Parameters:
ct (DataFrame) – Contingency table (without margins). If margins are present (row/column named
'Total'), they are stripped automatically.- Return type:
- siege_utilities.data.statistics.cross_tabulation.contingency_table(df, row_var, col_var, value_var=None, weight_var=None, aggfunc='sum', margins=True)[source]
Build a contingency table (cross-tabulation) from df.
- Parameters:
df (DataFrame) – Input data.
row_var (str) – Column for rows.
col_var (str) – Column for columns.
value_var (str, optional) – Column to aggregate. If
None, counts occurrences.weight_var (str, optional) – Column of weights (multiplied into value_var before aggregation).
aggfunc (str) – Aggregation function:
'sum','mean', or'count'.margins (bool) – If
True, add row/column totals.
- Return type:
DataFrame with row_var as index, col_var as columns, and aggregated values.
- siege_utilities.data.statistics.cross_tabulation.moe_cross_tab(estimates_df, moes_df, row_var, col_var, value_var, moe_var)[source]
Propagate ACS margins of error through a cross-tabulation.
Computes the MOE of each cell sum using the Census Bureau’s root-sum-of- squares rule. Returns a DataFrame shaped like the contingency table with the propagated MOE in each cell.
- Parameters:
estimates_df (DataFrame) – Data with estimate values.
moes_df (DataFrame) – Data with corresponding MOE values (same structure as estimates_df).
row_var (str) – Row and column variables.
col_var (str) – Row and column variables.
value_var (str) – Name of the estimate column in estimates_df.
moe_var (str) – Name of the MOE column in moes_df.
- Return type:
DataFrame of propagated MOEs with shape matching the contingency table.
- siege_utilities.data.statistics.cross_tabulation.rate_table(ct, normalize='all')[source]
Normalize a contingency table to proportions.
- Parameters:
ct (DataFrame) – Contingency table (output of
contingency_table()).normalize (str) –
'all'(divide by grand total),'index'(row proportions),'columns'(column proportions).
- Return type:
DataFrame of proportions summing to 1.0 along the requested axis.
- Raises:
ZeroDivisionError – When the grand total, any row sum, or any column sum is zero (normalization is mathematically undefined).
ValueError – When normalize is not one of the accepted values.
ACS Margin of Error propagation through derived estimates.
Implements the Census Bureau’s recommended methods for propagating margins of error through arithmetic operations on ACS estimates:
Sum/difference: root-sum-of-squares of component MOEs
Proportion: delta method with bounded output
Ratio: delta method for ratios of non-nested estimates
CV threshold: flag unreliable estimates by coefficient of variation
- Reference:
U.S. Census Bureau, “Understanding and Using ACS Data”, Appendix A — Calculating Margins of Error.
- class siege_utilities.data.statistics.moe_propagation.Estimate[source]
Bases:
objectAn ACS estimate with its margin of error at 90% confidence.
- property cv: float
Coefficient of variation (SE / |estimate|), as a proportion.
- siege_utilities.data.statistics.moe_propagation.flag_unreliable(estimates, cv_threshold=0.4)[source]
Return indices and Estimates that exceed cv_threshold.
- siege_utilities.data.statistics.moe_propagation.moe_difference(a, b)[source]
Propagate MOE through the difference a - b.
Uses the same root-sum-of-squares formula as
moe_sum().
- siege_utilities.data.statistics.moe_propagation.moe_product(a, b)[source]
Propagate MOE through a product a * b (independent estimates).
Uses the delta-method approximation:
SE_ab = sqrt(a^2 * SE_b^2 + b^2 * SE_a^2)
- siege_utilities.data.statistics.moe_propagation.moe_proportion(numerator, denominator)[source]
Propagate MOE for a proportion (numerator / denominator).
Uses the Census Bureau’s recommended formula for proportions where the numerator is a subset of the denominator:
SE_p = sqrt(SE_num^2 - p^2 * SE_den^2) / denominator
If the radicand is negative (which can happen with small samples), falls back to the conservative approximation:
SE_p = sqrt(SE_num^2 + p^2 * SE_den^2) / denominator
- Parameters:
- Returns:
Proportion with propagated MOE, bounded to [0, 1].
- Return type:
- Raises:
ZeroDivisionError – If the denominator estimate is zero.
- siege_utilities.data.statistics.moe_propagation.moe_ratio(numerator, denominator)[source]
Propagate MOE for a ratio of non-nested estimates.
Uses the delta method:
SE_r = sqrt(SE_num^2 + r^2 * SE_den^2) / denominator
- Parameters:
- Return type:
- Raises:
ZeroDivisionError – If the denominator estimate is zero.