diff --git a/README.md b/README.md index c902cb2..d66e304 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ for Rhode Island. ** Many of maup's functions behave badly in geographic projections (i.e., lat/long coordinates), which are the default for shapefiles from the U.S. Census bureau. In order to find an appropriate CRS for a particular shapefile, consult the database -at [https://epsg.org](https://epsg.org).** +at [https://epsg.org](https://epsg.org). ** ```python @@ -54,7 +54,7 @@ at [https://epsg.org](https://epsg.org).** ``` -### Assigning precincts to districts +## Assigning precincts to districts The `assign` function in `maup` takes two sets of geometries called `sources` and `targets` and returns a pandas `Series`. The Series maps each geometry in @@ -83,7 +83,7 @@ As an aside, you can use that `precinct_to_district_assignment` object to create [gerrychain](https://gerrychain.readthedocs.io/en/latest/) `Partition` representing this districting plan. -### Aggregating block data to precincts +## Aggregating block data to precincts Precinct shapefiles usually come with election data, but not demographic data. In order to study their demographics, we need to aggregate demographic data from @@ -108,15 +108,13 @@ operation: ``` If you want to move data from one set of geometries to another but your source -and target geometries do not nest neatly (e.g., have overlaps), see +geometries do not nest cleanly into your target geometries, see [Prorating data when units do not nest neatly](#prorating-data-when-units-do-not-nest-neatly). -### Disaggregating data from precincts down to blocks +## Disaggregating data from precincts down to blocks It's common to have data at a coarser scale that you want to attach to -finer-scaled geometries. Usually this happens when vote totals for a certain -election are only reported at the county level, and we want to attach that data -to precinct geometries. +finer-scale geometries. For instance, this may happen when vote totals for a certain election are only reported at the county level, and we want to attach that data to precinct geometries. Let's say we want to prorate the vote totals in the columns `"PRES16D"`, `"PRES16R"` from our `precincts` GeoDataFrame down to our `blocks` GeoDataFrame. @@ -176,7 +174,7 @@ proration process due to the zero (or NaN) values for the weights corresponding the blocks in those precincts. If it is crucial to keep vote totals perfectly accurate, these votes will need to be assigned to the new units manually. -### Prorating data when units do not nest neatly +## Prorating data when units do not nest neatly Suppose you have a shapefile of precincts with some election results data and you want to join that data onto a different, more recent precincts shapefile. @@ -184,11 +182,10 @@ The two sets of precincts will have overlaps, and will not nest neatly like the blocks and precincts did in the above examples. (Not that blocks and precincts always nest neatly---in fact, they usually don't!) -In most cases, election data should be prorated from each old precincts to the new +In most cases, election data should be prorated from each old precinct to the new precincts with weights proportional to the population of the intersections between the old precinct and each new precinct. The most straightforward way to accomplish -this is to first disaggregate the data from the old precincts to Census blocks as in -the example above, and then reaggregate from blocks to the new precincts. +this is to first disaggregate the data from the old precincts to Census blocks as in the example above, and then reaggregate from blocks to the new precincts. ```python >>> old_precincts = precincts @@ -218,7 +215,7 @@ the example above, and then reaggregate from blocks to the new precincts. ``` As a sanity check, let's make sure that no votes were lost in either step. -Total votes in the old precincts: +Total votes in the old precincts, blocks, and new precincts: ```python >>> old_precincts[election_columns].sum() SEN18D 23401 @@ -239,9 +236,7 @@ dtype: float64 Oh no - what happened??? All votes were successfully disaggregated to blocks, but a significant percentage were lost when reaggregating to new precincts. -It turns out that when blocks were assigned to both old and new precincts, many blocks -were not assigned to any precincts. We can count how many blocks were unassigned in each -case: +It turns out that when blocks were assigned to both old and new precincts, many blocks were not assigned to any precincts. We can count how many blocks were unassigned in each case: ```python print(len(blocks)) @@ -253,7 +248,7 @@ print(blocks_to_new_precincts_assignment.isna().sum()) ``` So, out of 3,014 total Census blocks, 884 were not assigned to any old precinct and -1,227 were not assigned to any new precinct. If we plot the shapefiles, we can see why: +1,227 were not assigned to any new precinct. If we plot the GeoDataFrames, we can see why: ```python >>> blocks.plot() ``` @@ -273,8 +268,7 @@ So, out of 3,014 total Census blocks, 884 were not assigned to any old precinct ![Providence new precincts](./examples/Providence_new_precincts_plot.png) The boundaries of the regions covered by these shapefiles are substantially -different---and that doesn't even get into the possibility that the precinct shapefiles -may have gaps between precinct polygons that some blocks may fall into. +different---and that doesn't even get into the possibility that the precinct shapefiles may have gaps between precinct polygons that some blocks may fall into. Once we know to look for this issue, we can see that it affected the previous example as well: @@ -299,7 +293,7 @@ moving data around between shapefiles; see below for details about how maup can help with this. -### Progress bars +## Progress bars For long-running operations, the user might want to see a progress bar to estimate how much longer a task will take (and whether to abandon it altogether). @@ -326,72 +320,156 @@ set `maup.progress.enabled = True`: ``` -### Fixing topological issues, overlaps, and gaps +## Fixing topological issues, overlaps, and gaps Precinct shapefiles are often created by stitching together collections of precinct geometries sourced from different counties or different years. As a result, the shapefile often has gaps or overlaps between precincts where the -different sources disagree about the boundaries. These gaps and overlaps pose -problems when you are interested in working with the adjacency graph of the -precincts, and not just in mapping the precincts. This adjacency information is -especially important when studying redistricting, because districts are almost -always expected to be contiguous. +different sources disagree about the boundaries. (And by "often," we mean "for almost every shapefile that isn't produced by the U.S. Census Burueau.") +As we saw in the examples above, these issues can pose problems when moving data between shapefiles. -`maup` provides functions for closing gaps and resolving overlaps in a -collection of geometries. As an example, we'll apply both functions to these -geometries, which have both an overlap and a gap: +Even when working with a single shapefile, gaps and overlaps may cause problems if you are interested in working with the adjacency graph of the precincts. +This adjacency information is especially important when studying redistricting, because districts are almost always expected to be contiguous. -![Four polygons with a gap and some overlaps](./examples/plot.png) +Before doing anything else, it is wise to understand the current status of a shapefile with regard to topological issues. `maup` provides a `doctor` function to diagnose gaps, overlaps, and invalid geometries. If a shapefile has none of these issues, `maup.doctor` returns a value of `True`; otherwise it returns `False` along with a brief summary of the problems that it found. -Usually the gaps and overlaps in real shapefiles are tiny and easy to miss, but -this exaggerated example will help illustrate the functionality. +The blocks shapefile, like most shapefiles from the Census Bureau, is clean: +```python +>>> maup.doctor(blocks) +True +``` + +The old precincts shapefile, however, has some minor issues: +```python +>>> maup.doctor(old_precincts) +There are 2 overlaps. +There are 3 holes. +False +``` + +As of version 2.0.0, `maup` provides two repair functions with a variety of options for fixing these issues: + +1. `quick_repair` is the new name for the `autorepair` function from version 1.x (and `autorepair` still works as a synonym). This function makes fairly simplistic repairs to gaps and overlaps: + * Any polygon $Q$ created by the overlapping intersection of two geometries $P_1$ and $P_2$ is removed from both polygons and reassigned to the one with which it shares the greatest perimeter. + * Any polygon $Q$ representing a gap between geometries $P_1,\ldots, P_n$ is assigned to the one with which it shares the greatest perimeter. + + This function is probably sufficient when gaps and overlaps are all very small in area relative to the areas of the geometries, **AND** when the repaired file will only be used for operations like aggregating and prorating data. But it should **NOT** be relied upon when it is important for the repaired file to accurately represent adjacency relations between neighboring geometries, such as when a precinct shapefile is used as a basis for creating districting plans with contiguous districts. + + For instance, when a gap adjoins many geometries (which happens frequently along county boundaries in precinct shapefiles!), whichever geometry the gap is adjoined to becomes "adjacent" to **all** the other geometries adjoining the gap, which can lead to the creation of discontiguous districts in plans based on the repaired shapefile. + +2. `smart_repair` is a more sophisticated repair function designed to reproduce the "true" adjacency relations between geometries as accurately as possible. In the case of gaps that adjoin several geometries, this is accomplished by an algorithm that divides the gap into pieces to be assigned to different geometries instead of assigning the entire gap to a single geometry. -First, we'll use `shapely` to create the polygons from scratch: + In addition to repairing gaps and overlaps, `smart_repair` includes two optional features: + * In many cases, the shapefile geometries are intended to nest cleanly into some larger units; e.g., in many states, precincts should nest cleanly into counties. `smart_repair` allows the user to optionally specify a second shapefile---e.g., a shapefile of county boundaries within a state---and then performs the repair process so that the repaired geometries nest cleanly into the units in the second shapefile. + * Whether as a result of inaccurate boundaries in the original map or as an artifact of the repair algorithm, it may happen that some units share boundaries with very short perimeter but should actually be considered "queen adjacent"---i.e., intersecting at only a single point---rather than "rook adjacent"---i.e., intersecting along a boundary of positive length. `smart_repair` includes an optional step in which all rook adjacencies of length below a user-specified parameter are converted to queen adjacencies. +`smart_repair` can accept either a GeoSeries or GeoDataFrame as input, and the output type will be the same as the input type. The input must be projected to a non-geographic coordinate reference system (CRS)---i.e., **not** lat/long coordinates---in order to have sufficient precision for the repair. One option is to reproject a GeoDataFrame called `gdf` to a suitable UTM (Universal Transverse Mercator) projection via + ```python -from shapely.geometry import Polygon -geometries = geopandas.GeoSeries([ - Polygon([(0, 0), (2, 0), (2, 1), (1, 1), (1, 2), (0, 2)]), - Polygon([(2, 0), (4, 0), (4, 2), (2, 2)]), - Polygon([(0, 2), (2, 2), (2, 4), (0, 4)]), - Polygon([(2, 1), (4, 1), (4, 4), (2, 4)]), -]) +gdf = gdf.to_crs(gdf.estimate_utm_crs()) ``` -Now we'll close the gap: + +At a minimum, all overlaps will be repaired in the output. Optional arguments include: + * `snapped` (default value `True`): If `True`, all polygon vertices are snapped to a grid of size no more than $10^{-10}$ times the maximum of width/height of the entire shapefile extent. **HIGHLY RECOMMENDED** to avoid topological exceptions due to rounding errors. + * `fill_gaps` (default value `True`): If `True`, all simply connected gaps with area less than `fill_gaps_threshold` times the largest area of all geometries adjoining the gap are filled. Default threshold is $0.1$; setting `fill_gaps_threshold = None` will fill all simply connected gaps. + * `nest_within_regions` (default value `None`): If `nest_within_regions` is a secondary GeoSeries or GeoDataFrame of region boundaries (e.g., counties within a state) then the repair will be performed so that repaired geometries nest cleanly into the region boundaries; specifically, each repaired geometry will be contained in the region with which the original geometry has the largest area of intersection. Note that the CRS for the region GeoSeries/GeoDataFrame must be the same as that for the primary input. + * `min_rook_length` (default value `None`): If `min_rook_length` is given a numerical value, all rook adjacencies with length below this value will be replaced with queen adjacencies. Note that this is an absolute value and not a relative value, so make sure that the value provided is in the correct units with respect to the input GeoSeries/GeoDataFrame's CRS. + + +### Examples + +#### First, we'll use `shapely` and `geopandas` to create a GeoDataFrame of "toy precincts" from scratch. ```python -without_gaps = maup.close_gaps(geometries) +import random +import geopandas +import maup +from shapely.geometry import Polygon + +random.seed(2023) # For reproducibility + +ppolys = [] +for i in range(4): + for j in range(4): + poly = Polygon( + [(0.5*i + 0.1*k, 0.5*j + (random.random() - 0.5)/12) for k in range(6)] + + [(0.5*(i+1) + (random.random() - 0.5)/12, 0.5*j + 0.1*k) for k in range(1,6)] + + [(0.5*(i+1) - 0.1*k, 0.5*(j+1) + (random.random() - 0.5)/12) for k in range(1,6)] + + [(0.5*i + (random.random() - 0.5)/12, 0.5*(j+1) - 0.1*k) for k in range(1,5)] + ) + ppolys.append(poly) + +toy_precincts_df = geopandas.GeoDataFrame(geometry = geopandas.GeoSeries(ppolys)) +toy_precincts_df.plot(cmap = "tab20", alpha=0.7) ``` -The `without_gaps` geometries look like this: +![toy_precincts](./examples/toy_precincts.png) + +Check for gaps and overlaps: +```python +>>> maup.doctor(old_precincts) +There are 28 overlaps. +There are 23 holes. +False +``` +All the gaps between geometries in this example are below the default threshold, so a basic application of `smart_repair` will resolve all overlaps and fill all gaps: -![Four polygons with two overlapping](./examples/plot_without_gaps.png) +```python +toy_precincts_repaired_df = maup.smart_repair(toy_precincts_df) +toy_precincts_repaired_df.plot(cmap = "tab20", alpha=0.7) +``` -And then resolve the overlaps: +![toy_precincts_repaired](./examples/toy_precincts_repaired.png) +We can check that the repair succeeded: ```python -without_overlaps_or_gaps = maup.resolve_overlaps(without_gaps) +>>> maup.doctor(old_precincts) +True ``` -The `without_overlaps_or_gaps` geometries look like this: +Now suppose that the precincts are intended to nest cleanly into the following "toy counties:" -![Four squares](./examples/plot_without_gaps_or_overlaps.png) +```python +cpoly1 = Polygon([(0,0), (1,0), (1,1), (0,1)]) +cpoly2 = Polygon([(1,0), (2,0), (2,1), (1,1)]) +cpoly3 = Polygon([(0,1), (1,1), (1,2), (0,2)]) +cpoly4 = Polygon([(1,1), (2,1), (2,2), (1,2)]) -Alternatively, there is also a convenience `maup.autorepair()` function provided that -attempts to resolve topological issues as well as close gaps and resolve overlaps: +toy_counties_df = geopandas.GeoDataFrame(geometry = geopandas.GeoSeries([cpoly1, cpoly2, cpoly3, cpoly4])) + +toy_counties_df.plot(cmap='tab20') +``` +![toy_counties](./examples/toy_counties.png) +We can perform a "county-aware" repair as follows: ```python -without_overlaps_or_gaps = maup.autorepair(geometries) +toy_precincts_repaired_county_aware_df = maup.smart_repair(toy_precincts_df, nest_within_regions = toy_counties_df) +toy_precincts_repaired_county_aware_df.plot(cmap = "tab20", alpha=0.7) ``` +![toy_precincts_repaired_county_aware](./examples/toy_precincts_repaired_county_aware.png) + +Next, suppose that we'd like to get rid of small rook adjacencies at corner points where 4 precincts meet. We might reasonably estimate that these all have length less than $0.1$, so we can accomplish this as follows: +```python +toy_precincts_repaired_county_aware_rook_to_queen_df = maup.smart_repair(toy_precincts_df, nest_within_regions = toy_counties_df, min_rook_length = 0.1) +toy_precincts_repaired_county_aware_rook_to_queen_df.plot(cmap = "tab20", alpha=0.7) +``` +![toy_precincts_repaired_county_aware_rook_to_queen](./examples/toy_precincts_repaired_county_aware_rook_to_queen.png) + +The difference is hard to see, so let's zoom in on gap between the 4 original precincts in the upper left-hand corner. + +Original precincts: + +![toy_precincts_corner](./examples/toy_precincts_corner.png) + +County-aware repair: + +![toy_precincts_corner_repaired](./examples/toy_precincts_corner_repaired.png) + +County-aware repair with rook adjacency converted to queen: + +![toy_precincts_corner_repaired_rook_to_queen](./examples/toy_precincts_corner_repaired_rook_to_queen.png) -The functions `resolve_overlaps`, `close_gaps`, and `autorepair` accept a -`relative_threshold` argument. This threshold controls how large of a gap or -overlap the function will attempt to fix. The default value of -`relative_threshold` is `0.1`, which means that the functions will leave alone -any gap/overlap whose area is more than 10% of the area of the geometries that -might absorb that gap/overlap. In the above example, we set -`relative_threshold=None` to ensure that no gaps or overlaps were ignored. ## Modifiable areal unit problem @@ -401,3 +479,6 @@ the same spatial data will look different depending on how you divide up the space. Since `maup` is all about changing the way your data is aggregated and partitioned, we have named it after the MAUP to encourage users to use the toolkit thoughtfully and responsibly. + + + diff --git a/docs/conf.py b/docs/conf.py index 54f87d3..12e33d8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,7 +23,7 @@ author = 'Max Hully, Max Fan' # The full version, including alpha/beta/rc tags -release = '1.1.0' +release = '2.0.0' # -- General configuration --------------------------------------------------- diff --git a/examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.cpg b/examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.cpg new file mode 100644 index 0000000..cd89cb9 --- /dev/null +++ b/examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.cpg @@ -0,0 +1 @@ +ISO-8859-1 \ No newline at end of file diff --git a/examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.dbf b/examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.dbf new file mode 100644 index 0000000..139ebe9 Binary files /dev/null and b/examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.dbf differ diff --git a/examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.prj b/examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.prj new file mode 100644 index 0000000..471fc41 --- /dev/null +++ b/examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.prj @@ -0,0 +1 @@ +PROJCS["NAD_1983_StatePlane_Colorado_Central_FIPS_0502_Feet",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",3000000.0],PARAMETER["False_Northing",1000000.0],PARAMETER["Central_Meridian",-105.5],PARAMETER["Standard_Parallel_1",39.75],PARAMETER["Standard_Parallel_2",38.45],PARAMETER["Latitude_Of_Origin",37.8333333333333],UNIT["US survey foot",0.304800609601219]] \ No newline at end of file diff --git a/examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.shp b/examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.shp new file mode 100644 index 0000000..0578b52 Binary files /dev/null and b/examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.shp differ diff --git a/examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.shx b/examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.shx new file mode 100644 index 0000000..adac2c1 Binary files /dev/null and b/examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.shx differ diff --git a/examples/Shapefiles/DenverCo_precincts2016_orig/DenverCo_precincts2016_orig.cpg b/examples/Shapefiles/DenverCo_precincts2016_orig/DenverCo_precincts2016_orig.cpg new file mode 100644 index 0000000..cd89cb9 --- /dev/null +++ b/examples/Shapefiles/DenverCo_precincts2016_orig/DenverCo_precincts2016_orig.cpg @@ -0,0 +1 @@ +ISO-8859-1 \ No newline at end of file diff --git a/examples/Shapefiles/DenverCo_precincts2016_orig/DenverCo_precincts2016_orig.dbf b/examples/Shapefiles/DenverCo_precincts2016_orig/DenverCo_precincts2016_orig.dbf new file mode 100644 index 0000000..35ef81d Binary files /dev/null and b/examples/Shapefiles/DenverCo_precincts2016_orig/DenverCo_precincts2016_orig.dbf differ diff --git a/examples/Shapefiles/DenverCo_precincts2016_orig/DenverCo_precincts2016_orig.prj b/examples/Shapefiles/DenverCo_precincts2016_orig/DenverCo_precincts2016_orig.prj new file mode 100644 index 0000000..471fc41 --- /dev/null +++ b/examples/Shapefiles/DenverCo_precincts2016_orig/DenverCo_precincts2016_orig.prj @@ -0,0 +1 @@ +PROJCS["NAD_1983_StatePlane_Colorado_Central_FIPS_0502_Feet",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",3000000.0],PARAMETER["False_Northing",1000000.0],PARAMETER["Central_Meridian",-105.5],PARAMETER["Standard_Parallel_1",39.75],PARAMETER["Standard_Parallel_2",38.45],PARAMETER["Latitude_Of_Origin",37.8333333333333],UNIT["US survey foot",0.304800609601219]] \ No newline at end of file diff --git a/examples/Shapefiles/DenverCo_precincts2016_orig/DenverCo_precincts2016_orig.shp b/examples/Shapefiles/DenverCo_precincts2016_orig/DenverCo_precincts2016_orig.shp new file mode 100644 index 0000000..7e78829 Binary files /dev/null and b/examples/Shapefiles/DenverCo_precincts2016_orig/DenverCo_precincts2016_orig.shp differ diff --git a/examples/Shapefiles/DenverCo_precincts2016_orig/DenverCo_precincts2016_orig.shx b/examples/Shapefiles/DenverCo_precincts2016_orig/DenverCo_precincts2016_orig.shx new file mode 100644 index 0000000..7c68ace Binary files /dev/null and b/examples/Shapefiles/DenverCo_precincts2016_orig/DenverCo_precincts2016_orig.shx differ diff --git a/examples/Shapefiles/DenverCo_precincts2016_repaired/DenverCo_precincts2016_repaired.cpg b/examples/Shapefiles/DenverCo_precincts2016_repaired/DenverCo_precincts2016_repaired.cpg new file mode 100644 index 0000000..cd89cb9 --- /dev/null +++ b/examples/Shapefiles/DenverCo_precincts2016_repaired/DenverCo_precincts2016_repaired.cpg @@ -0,0 +1 @@ +ISO-8859-1 \ No newline at end of file diff --git a/examples/Shapefiles/DenverCo_precincts2016_repaired/DenverCo_precincts2016_repaired.dbf b/examples/Shapefiles/DenverCo_precincts2016_repaired/DenverCo_precincts2016_repaired.dbf new file mode 100644 index 0000000..35ef81d Binary files /dev/null and b/examples/Shapefiles/DenverCo_precincts2016_repaired/DenverCo_precincts2016_repaired.dbf differ diff --git a/examples/Shapefiles/DenverCo_precincts2016_repaired/DenverCo_precincts2016_repaired.prj b/examples/Shapefiles/DenverCo_precincts2016_repaired/DenverCo_precincts2016_repaired.prj new file mode 100644 index 0000000..471fc41 --- /dev/null +++ b/examples/Shapefiles/DenverCo_precincts2016_repaired/DenverCo_precincts2016_repaired.prj @@ -0,0 +1 @@ +PROJCS["NAD_1983_StatePlane_Colorado_Central_FIPS_0502_Feet",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",3000000.0],PARAMETER["False_Northing",1000000.0],PARAMETER["Central_Meridian",-105.5],PARAMETER["Standard_Parallel_1",39.75],PARAMETER["Standard_Parallel_2",38.45],PARAMETER["Latitude_Of_Origin",37.8333333333333],UNIT["US survey foot",0.304800609601219]] \ No newline at end of file diff --git a/examples/Shapefiles/DenverCo_precincts2016_repaired/DenverCo_precincts2016_repaired.shp b/examples/Shapefiles/DenverCo_precincts2016_repaired/DenverCo_precincts2016_repaired.shp new file mode 100644 index 0000000..42c1456 Binary files /dev/null and b/examples/Shapefiles/DenverCo_precincts2016_repaired/DenverCo_precincts2016_repaired.shp differ diff --git a/examples/Shapefiles/DenverCo_precincts2016_repaired/DenverCo_precincts2016_repaired.shx b/examples/Shapefiles/DenverCo_precincts2016_repaired/DenverCo_precincts2016_repaired.shx new file mode 100644 index 0000000..e70552b Binary files /dev/null and b/examples/Shapefiles/DenverCo_precincts2016_repaired/DenverCo_precincts2016_repaired.shx differ diff --git a/examples/Shapefiles/DenverCo_precincts2018_orig/DenverCo_precincts2018_orig.cpg b/examples/Shapefiles/DenverCo_precincts2018_orig/DenverCo_precincts2018_orig.cpg new file mode 100644 index 0000000..cd89cb9 --- /dev/null +++ b/examples/Shapefiles/DenverCo_precincts2018_orig/DenverCo_precincts2018_orig.cpg @@ -0,0 +1 @@ +ISO-8859-1 \ No newline at end of file diff --git a/examples/Shapefiles/DenverCo_precincts2018_orig/DenverCo_precincts2018_orig.dbf b/examples/Shapefiles/DenverCo_precincts2018_orig/DenverCo_precincts2018_orig.dbf new file mode 100644 index 0000000..c8cab30 Binary files /dev/null and b/examples/Shapefiles/DenverCo_precincts2018_orig/DenverCo_precincts2018_orig.dbf differ diff --git a/examples/Shapefiles/DenverCo_precincts2018_orig/DenverCo_precincts2018_orig.prj b/examples/Shapefiles/DenverCo_precincts2018_orig/DenverCo_precincts2018_orig.prj new file mode 100644 index 0000000..471fc41 --- /dev/null +++ b/examples/Shapefiles/DenverCo_precincts2018_orig/DenverCo_precincts2018_orig.prj @@ -0,0 +1 @@ +PROJCS["NAD_1983_StatePlane_Colorado_Central_FIPS_0502_Feet",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",3000000.0],PARAMETER["False_Northing",1000000.0],PARAMETER["Central_Meridian",-105.5],PARAMETER["Standard_Parallel_1",39.75],PARAMETER["Standard_Parallel_2",38.45],PARAMETER["Latitude_Of_Origin",37.8333333333333],UNIT["US survey foot",0.304800609601219]] \ No newline at end of file diff --git a/examples/Shapefiles/DenverCo_precincts2018_orig/DenverCo_precincts2018_orig.shp b/examples/Shapefiles/DenverCo_precincts2018_orig/DenverCo_precincts2018_orig.shp new file mode 100644 index 0000000..f36c7df Binary files /dev/null and b/examples/Shapefiles/DenverCo_precincts2018_orig/DenverCo_precincts2018_orig.shp differ diff --git a/examples/Shapefiles/DenverCo_precincts2018_orig/DenverCo_precincts2018_orig.shx b/examples/Shapefiles/DenverCo_precincts2018_orig/DenverCo_precincts2018_orig.shx new file mode 100644 index 0000000..d378b2d Binary files /dev/null and b/examples/Shapefiles/DenverCo_precincts2018_orig/DenverCo_precincts2018_orig.shx differ diff --git a/examples/Shapefiles/DenverCo_precincts2018_repaired/DenverCo_precincts2018_repaired.cpg b/examples/Shapefiles/DenverCo_precincts2018_repaired/DenverCo_precincts2018_repaired.cpg new file mode 100644 index 0000000..cd89cb9 --- /dev/null +++ b/examples/Shapefiles/DenverCo_precincts2018_repaired/DenverCo_precincts2018_repaired.cpg @@ -0,0 +1 @@ +ISO-8859-1 \ No newline at end of file diff --git a/examples/Shapefiles/DenverCo_precincts2018_repaired/DenverCo_precincts2018_repaired.dbf b/examples/Shapefiles/DenverCo_precincts2018_repaired/DenverCo_precincts2018_repaired.dbf new file mode 100644 index 0000000..c8cab30 Binary files /dev/null and b/examples/Shapefiles/DenverCo_precincts2018_repaired/DenverCo_precincts2018_repaired.dbf differ diff --git a/examples/Shapefiles/DenverCo_precincts2018_repaired/DenverCo_precincts2018_repaired.prj b/examples/Shapefiles/DenverCo_precincts2018_repaired/DenverCo_precincts2018_repaired.prj new file mode 100644 index 0000000..471fc41 --- /dev/null +++ b/examples/Shapefiles/DenverCo_precincts2018_repaired/DenverCo_precincts2018_repaired.prj @@ -0,0 +1 @@ +PROJCS["NAD_1983_StatePlane_Colorado_Central_FIPS_0502_Feet",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",3000000.0],PARAMETER["False_Northing",1000000.0],PARAMETER["Central_Meridian",-105.5],PARAMETER["Standard_Parallel_1",39.75],PARAMETER["Standard_Parallel_2",38.45],PARAMETER["Latitude_Of_Origin",37.8333333333333],UNIT["US survey foot",0.304800609601219]] \ No newline at end of file diff --git a/examples/Shapefiles/DenverCo_precincts2018_repaired/DenverCo_precincts2018_repaired.shp b/examples/Shapefiles/DenverCo_precincts2018_repaired/DenverCo_precincts2018_repaired.shp new file mode 100644 index 0000000..d164c4d Binary files /dev/null and b/examples/Shapefiles/DenverCo_precincts2018_repaired/DenverCo_precincts2018_repaired.shp differ diff --git a/examples/Shapefiles/DenverCo_precincts2018_repaired/DenverCo_precincts2018_repaired.shx b/examples/Shapefiles/DenverCo_precincts2018_repaired/DenverCo_precincts2018_repaired.shx new file mode 100644 index 0000000..6980186 Binary files /dev/null and b/examples/Shapefiles/DenverCo_precincts2018_repaired/DenverCo_precincts2018_repaired.shx differ diff --git a/examples/Shapefiles/DenverCo_precincts2020_orig/DenverCo_precincts2020_orig.cpg b/examples/Shapefiles/DenverCo_precincts2020_orig/DenverCo_precincts2020_orig.cpg new file mode 100644 index 0000000..cd89cb9 --- /dev/null +++ b/examples/Shapefiles/DenverCo_precincts2020_orig/DenverCo_precincts2020_orig.cpg @@ -0,0 +1 @@ +ISO-8859-1 \ No newline at end of file diff --git a/examples/Shapefiles/DenverCo_precincts2020_orig/DenverCo_precincts2020_orig.dbf b/examples/Shapefiles/DenverCo_precincts2020_orig/DenverCo_precincts2020_orig.dbf new file mode 100644 index 0000000..e2563ed Binary files /dev/null and b/examples/Shapefiles/DenverCo_precincts2020_orig/DenverCo_precincts2020_orig.dbf differ diff --git a/examples/Shapefiles/DenverCo_precincts2020_orig/DenverCo_precincts2020_orig.prj b/examples/Shapefiles/DenverCo_precincts2020_orig/DenverCo_precincts2020_orig.prj new file mode 100644 index 0000000..471fc41 --- /dev/null +++ b/examples/Shapefiles/DenverCo_precincts2020_orig/DenverCo_precincts2020_orig.prj @@ -0,0 +1 @@ +PROJCS["NAD_1983_StatePlane_Colorado_Central_FIPS_0502_Feet",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",3000000.0],PARAMETER["False_Northing",1000000.0],PARAMETER["Central_Meridian",-105.5],PARAMETER["Standard_Parallel_1",39.75],PARAMETER["Standard_Parallel_2",38.45],PARAMETER["Latitude_Of_Origin",37.8333333333333],UNIT["US survey foot",0.304800609601219]] \ No newline at end of file diff --git a/examples/Shapefiles/DenverCo_precincts2020_orig/DenverCo_precincts2020_orig.shp b/examples/Shapefiles/DenverCo_precincts2020_orig/DenverCo_precincts2020_orig.shp new file mode 100644 index 0000000..f355e45 Binary files /dev/null and b/examples/Shapefiles/DenverCo_precincts2020_orig/DenverCo_precincts2020_orig.shp differ diff --git a/examples/Shapefiles/DenverCo_precincts2020_orig/DenverCo_precincts2020_orig.shx b/examples/Shapefiles/DenverCo_precincts2020_orig/DenverCo_precincts2020_orig.shx new file mode 100644 index 0000000..98b53e3 Binary files /dev/null and b/examples/Shapefiles/DenverCo_precincts2020_orig/DenverCo_precincts2020_orig.shx differ diff --git a/examples/Shapefiles/DenverCo_precincts2020_repaired/DenverCo_precincts2020_repaired.cpg b/examples/Shapefiles/DenverCo_precincts2020_repaired/DenverCo_precincts2020_repaired.cpg new file mode 100644 index 0000000..cd89cb9 --- /dev/null +++ b/examples/Shapefiles/DenverCo_precincts2020_repaired/DenverCo_precincts2020_repaired.cpg @@ -0,0 +1 @@ +ISO-8859-1 \ No newline at end of file diff --git a/examples/Shapefiles/DenverCo_precincts2020_repaired/DenverCo_precincts2020_repaired.dbf b/examples/Shapefiles/DenverCo_precincts2020_repaired/DenverCo_precincts2020_repaired.dbf new file mode 100644 index 0000000..e2563ed Binary files /dev/null and b/examples/Shapefiles/DenverCo_precincts2020_repaired/DenverCo_precincts2020_repaired.dbf differ diff --git a/examples/Shapefiles/DenverCo_precincts2020_repaired/DenverCo_precincts2020_repaired.prj b/examples/Shapefiles/DenverCo_precincts2020_repaired/DenverCo_precincts2020_repaired.prj new file mode 100644 index 0000000..471fc41 --- /dev/null +++ b/examples/Shapefiles/DenverCo_precincts2020_repaired/DenverCo_precincts2020_repaired.prj @@ -0,0 +1 @@ +PROJCS["NAD_1983_StatePlane_Colorado_Central_FIPS_0502_Feet",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",3000000.0],PARAMETER["False_Northing",1000000.0],PARAMETER["Central_Meridian",-105.5],PARAMETER["Standard_Parallel_1",39.75],PARAMETER["Standard_Parallel_2",38.45],PARAMETER["Latitude_Of_Origin",37.8333333333333],UNIT["US survey foot",0.304800609601219]] \ No newline at end of file diff --git a/examples/Shapefiles/DenverCo_precincts2020_repaired/DenverCo_precincts2020_repaired.shp b/examples/Shapefiles/DenverCo_precincts2020_repaired/DenverCo_precincts2020_repaired.shp new file mode 100644 index 0000000..4651131 Binary files /dev/null and b/examples/Shapefiles/DenverCo_precincts2020_repaired/DenverCo_precincts2020_repaired.shp differ diff --git a/examples/Shapefiles/DenverCo_precincts2020_repaired/DenverCo_precincts2020_repaired.shx b/examples/Shapefiles/DenverCo_precincts2020_repaired/DenverCo_precincts2020_repaired.shx new file mode 100644 index 0000000..0b7a57a Binary files /dev/null and b/examples/Shapefiles/DenverCo_precincts2020_repaired/DenverCo_precincts2020_repaired.shx differ diff --git a/examples/Shapefiles/bad_gap_region/bad_gap_region.cpg b/examples/Shapefiles/bad_gap_region/bad_gap_region.cpg new file mode 100644 index 0000000..cd89cb9 --- /dev/null +++ b/examples/Shapefiles/bad_gap_region/bad_gap_region.cpg @@ -0,0 +1 @@ +ISO-8859-1 \ No newline at end of file diff --git a/examples/Shapefiles/bad_gap_region/bad_gap_region.dbf b/examples/Shapefiles/bad_gap_region/bad_gap_region.dbf new file mode 100644 index 0000000..b6d6650 Binary files /dev/null and b/examples/Shapefiles/bad_gap_region/bad_gap_region.dbf differ diff --git a/examples/Shapefiles/bad_gap_region/bad_gap_region.prj b/examples/Shapefiles/bad_gap_region/bad_gap_region.prj new file mode 100644 index 0000000..471fc41 --- /dev/null +++ b/examples/Shapefiles/bad_gap_region/bad_gap_region.prj @@ -0,0 +1 @@ +PROJCS["NAD_1983_StatePlane_Colorado_Central_FIPS_0502_Feet",GEOGCS["GCS_North_American_1983",DATUM["D_North_American_1983",SPHEROID["GRS_1980",6378137.0,298.257222101]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Lambert_Conformal_Conic"],PARAMETER["False_Easting",3000000.0],PARAMETER["False_Northing",1000000.0],PARAMETER["Central_Meridian",-105.5],PARAMETER["Standard_Parallel_1",39.75],PARAMETER["Standard_Parallel_2",38.45],PARAMETER["Latitude_Of_Origin",37.8333333333333],UNIT["US survey foot",0.304800609601219]] \ No newline at end of file diff --git a/examples/Shapefiles/bad_gap_region/bad_gap_region.shp b/examples/Shapefiles/bad_gap_region/bad_gap_region.shp new file mode 100644 index 0000000..d5cc0c4 Binary files /dev/null and b/examples/Shapefiles/bad_gap_region/bad_gap_region.shp differ diff --git a/examples/Shapefiles/bad_gap_region/bad_gap_region.shx b/examples/Shapefiles/bad_gap_region/bad_gap_region.shx new file mode 100644 index 0000000..a772190 Binary files /dev/null and b/examples/Shapefiles/bad_gap_region/bad_gap_region.shx differ diff --git a/examples/toy_counties.png b/examples/toy_counties.png new file mode 100644 index 0000000..c5d83a8 Binary files /dev/null and b/examples/toy_counties.png differ diff --git a/examples/toy_precincts.png b/examples/toy_precincts.png new file mode 100644 index 0000000..7e01ba8 Binary files /dev/null and b/examples/toy_precincts.png differ diff --git a/examples/toy_precincts_corner.png b/examples/toy_precincts_corner.png new file mode 100644 index 0000000..f5f8183 Binary files /dev/null and b/examples/toy_precincts_corner.png differ diff --git a/examples/toy_precincts_corner_repaired.png b/examples/toy_precincts_corner_repaired.png new file mode 100644 index 0000000..6e44c6d Binary files /dev/null and b/examples/toy_precincts_corner_repaired.png differ diff --git a/examples/toy_precincts_corner_repaired_rook_to_queen.png b/examples/toy_precincts_corner_repaired_rook_to_queen.png new file mode 100644 index 0000000..8738286 Binary files /dev/null and b/examples/toy_precincts_corner_repaired_rook_to_queen.png differ diff --git a/examples/toy_precincts_repaired.png b/examples/toy_precincts_repaired.png new file mode 100644 index 0000000..c41f8f3 Binary files /dev/null and b/examples/toy_precincts_repaired.png differ diff --git a/examples/toy_precincts_repaired_county_aware.png b/examples/toy_precincts_repaired_county_aware.png new file mode 100644 index 0000000..f1e7f17 Binary files /dev/null and b/examples/toy_precincts_repaired_county_aware.png differ diff --git a/examples/toy_precincts_repaired_county_aware_rook_to_queen.png b/examples/toy_precincts_repaired_county_aware_rook_to_queen.png new file mode 100644 index 0000000..3789848 Binary files /dev/null and b/examples/toy_precincts_repaired_county_aware_rook_to_queen.png differ diff --git a/maup/__init__.py b/maup/__init__.py index 626cdea..5daa866 100644 --- a/maup/__init__.py +++ b/maup/__init__.py @@ -3,11 +3,11 @@ from .assign import assign from .indexed_geometries import IndexedGeometries from .intersections import intersections, prorate -from .repair import close_gaps, autorepair, snap_to_grid, crop_to, doctor, resolve_overlaps +from .repair import close_gaps, resolve_overlaps, quick_repair, snap_to_grid, crop_to, expand_to, doctor +from .smart_repair import smart_repair from .normalize import normalize from .progress_bar import progress - # warn about https://github.com/geopandas/geopandas/issues/2199 if geopandas.options.use_pygeos: raise ImportError( @@ -16,7 +16,7 @@ "`geopandas.options.use_pygeos = False` before importing your shapefile." ) -__version__ = "1.1.3" +__version__ = "2.0.0" __all__ = [ "adjacencies", "assign", @@ -24,11 +24,13 @@ "intersections", "prorate", "close_gaps", - "autorepair", "resolve_overlaps", + "quick_repair", "snap_to_grid", "crop_to", + "expand_to", "doctor", + "smart_repair", "normalize", "progress" -] \ No newline at end of file +] diff --git a/maup/adjacencies.py b/maup/adjacencies.py index 3836eb0..60ba85e 100644 --- a/maup/adjacencies.py +++ b/maup/adjacencies.py @@ -1,8 +1,7 @@ import warnings from geopandas import GeoSeries, GeoDataFrame - -from shapely import make_valid +from shapely import make_valid from .indexed_geometries import IndexedGeometries, get_geometries from .progress_bar import progress @@ -26,12 +25,13 @@ def iter_adjacencies(geometries): for j, inter in inters.items(): yield (i, j), inter + def adjacencies( geometries, adjacency_type="rook", output_type="geoseries", *, warn_for_overlaps=True, warn_for_islands=True ): - """Returns adjacencies between geometries. + """Returns adjacencies between geometries. The default return type is a `GeoSeries` with a `MultiIndex`, whose (i, j)th entry is the pairwise intersection between geometry `i` and geometry `j`. We ensure that @@ -48,20 +48,20 @@ def adjacencies( orig_crs = geometries.crs geometries = get_geometries(geometries) geometries = make_valid(geometries) - + adjs = list(iter_adjacencies(geometries)) if adjs: index, geoms = zip(*adjs) else: - index, geoms = [[],[]] - + index, geoms = [[], []] + if output_type == "geodataframe": - inters = GeoDataFrame({"neighbors" : index, "geometry" : geoms}) + inters = GeoDataFrame({"neighbors" : index, "geometry" : geoms}, crs = geometries.crs) else: - inters = GeoSeries(geoms, index=index) + inters = GeoSeries(geoms, index=index, crs=geometries.crs) if adjacency_type == "rook": - inters = inters[inters.length > 0].copy() + inters = inters[inters.length > 0] if warn_for_overlaps: overlaps = inters[inters.area > 0] diff --git a/maup/assign.py b/maup/assign.py index 3a63c76..968fae8 100644 --- a/maup/assign.py +++ b/maup/assign.py @@ -24,7 +24,7 @@ def assign(sources, targets): dtype="float" ) assignment.update(assignments_by_area) - + # TODO: add a warning here if there are still unassigned source geometries. return assignment.astype(targets.index.dtype, errors="ignore") diff --git a/maup/indexed_geometries.py b/maup/indexed_geometries.py index 8070076..9f02abc 100644 --- a/maup/indexed_geometries.py +++ b/maup/indexed_geometries.py @@ -1,8 +1,6 @@ import pandas import geopandas -# Added numpy import to handle output of STRtree query import numpy -import warnings from shapely.prepared import prep from shapely.strtree import STRtree @@ -21,8 +19,7 @@ def __init__(self, geometries): self.spatial_index = STRtree(self.geometries) self.index = self.geometries.index - - def query(self, geometry): + def query(self, geometry): # IMPORTANT: When "geometry" is multi-part, this query will return a # (2 x n) array instead of a (1 x n) array, so it's safest to flatten the query # output before proceeding. @@ -31,12 +28,12 @@ def query(self, geometry): relevant_geometries = self.geometries.iloc[relevant_indices] return relevant_geometries - def intersections(self, geometry): - relevant_geometries = self.query(geometry) + def intersections(self, geometry): + relevant_geometries = self.query(geometry) intersections = relevant_geometries.intersection(geometry) return intersections[-(intersections.is_empty | intersections.isna())] - def covered_by(self, container): + def covered_by(self, container): relevant_geometries = self.query(container) prepared_container = prep(container) @@ -46,7 +43,7 @@ def covered_by(self, container): selected_geometries = relevant_geometries.apply(prepared_container.covers) return relevant_geometries[selected_geometries] - def assign(self, targets): + def assign(self, targets): target_geometries = get_geometries(targets) groups = [ self.covered_by(container).apply(lambda x: container_index) @@ -54,29 +51,25 @@ def assign(self, targets): target_geometries.items(), len(target_geometries) ) ] + # New in pandas 2.1.2: Only concatenate Series of positive length + groups = [group for group in groups if len(group) > 0] if groups: - # New in pandas 2.1.2: Only concatenate Series of positive length - groups = [group for group in groups if len(group) > 0] - if groups: - groups_concat = pandas.concat(groups) - # New in pandas 2.1.2: No reindexing allowed with a non-unique Index, - # so we need to find and remove repetitions. (This only happens when the - # targets have overlaps and some source is completely covered by more - # than one target.) - # Any that get removed here will be randomly assigned to one of the - # covering units at the assign_by_area step ub maup.assign. - groups_concat_index_list = list(groups_concat.index) - seen = set() - bad_indices = list(set([x for x in groups_concat_index_list if x in seen or seen.add(x)])) - if len(bad_indices)>0: - groups_concat = groups_concat.drop(bad_indices) - return groups_concat.reindex(self.index) - else: - return geopandas.GeoSeries().reindex(self.index) + groups_concat = pandas.concat(groups) + # New in pandas 2.1.2: No reindexing allowed with a non-unique Index, + # so we need to find and remove repetitions. (This only happens when the + # targets have overlaps and some source is completely covered by more + # than one target.) + # Any that get removed here will be randomly assigned to one of the + # covering units at the assign_by_area step ub maup.assign. + groups_concat_index_list = list(groups_concat.index) + seen = set() + bad_indices = list(set([x for x in groups_concat_index_list if x in seen or seen.add(x)])) + if len(bad_indices) > 0: + groups_concat = groups_concat.drop(bad_indices) + return groups_concat.reindex(self.index) else: return geopandas.GeoSeries().reindex(self.index) - def enumerate_intersections(self, targets): target_geometries = get_geometries(targets) for i, target in progress(target_geometries.items(), len(target_geometries)): diff --git a/maup/intersections.py b/maup/intersections.py index 40fd87a..a9ec53f 100644 --- a/maup/intersections.py +++ b/maup/intersections.py @@ -7,35 +7,26 @@ @require_same_crs - def intersections(sources, targets, output_type="geoseries", area_cutoff=None): - """ - Computes all of the nonempty intersections between two sets of geometries. - - By default, the returned `~geopandas.GeoSeries` will have a MultiIndex, where the - geometry at index *(i, j)* is the intersection of ``sources[i]`` and ``targets[j]`` - (if it is not empty). - + """Computes all of the nonempty intersections between two sets of geometries. + By default, the returned `~geopandas.GeoSeries` will have a MultiIndex, where the + geometry at index *(i, j)* is the intersection of ``sources[i]`` and ``targets[j]`` + (if it is not empty). If output_type == "geodataframe", the return type is a range-indexed GeoDataFrame - with "source" and "target" columns containing the indices i,j, respectively, for the - intersection of ``sources[i]`` and ``targets[j]``. - + with "source" and "target" columns containing the indices i,j, respectively, for the + intersection of ``sources[i]`` and ``targets[j]`` :param sources: geometries :type sources: :class:`~geopandas.GeoSeries` or :class:`~geopandas.GeoDataFrame` :param targets: geometries :type targets: :class:`~geopandas.GeoSeries` or :class:`~geopandas.GeoDataFrame` - :param output_type: type of output, "geoseries" or "geodataframe" - :type output_type: str + :rtype: :class:`~geopandas.GeoSeries` :param area_cutoff: (optional) if provided, only return intersections with area greater than ``area_cutoff`` :type area_cutoff: Number or None - :rtype: :class:`~geopandas.GeoSeries` or :class:`~geopandas.GeoDataFrame` """ - reindexed_sources = get_geometries_with_range_index(sources) reindexed_targets = get_geometries_with_range_index(targets) - spatially_indexed_sources = IndexedGeometries(reindexed_sources) records = [ @@ -45,11 +36,11 @@ def intersections(sources, targets, output_type="geoseries", area_cutoff=None): reindexed_targets ) ] - + df = GeoDataFrame.from_records(records, columns=["source", "target", "geometry"]) df = df.sort_values(by=["source", "target"]).reset_index(drop=True) df.crs = sources.crs - + geometries = df.set_index(["source", "target"]).geometry geometries.sort_index(inplace=True) geometries.crs = sources.crs diff --git a/maup/repair.py b/maup/repair.py index c5aa2d0..712514e 100644 --- a/maup/repair.py +++ b/maup/repair.py @@ -1,350 +1,392 @@ -import math -import pandas -import functools -import warnings - -from geopandas import GeoSeries, GeoDataFrame -from shapely.geometry import MultiPolygon, Polygon -from shapely.ops import unary_union -from shapely import make_valid - -from .adjacencies import adjacencies -from .assign import assign_to_max -from .crs import require_same_crs -from .indexed_geometries import get_geometries -from .intersections import intersections - - -""" -Some of these functions are based on the functions in Mary Barker's -check_shapefile_connectivity.py script in @gerrymandr/Preprocessing. -""" - -# IMPORTANT TO BE AWARE OF FOR FUTURE UPDATES: -# The old version of this file used buffer(0) to simplify geometries, but this only -# works properly for polygons. For 1-D objects such as LineStrings, it kills them off -# completely - and this resulted in some pretty disastrous choices in the -# absorb_by_shared_perimeter function when ALL the perimeters simplified to zero and -# the choice of which geometry to absorb into was essentially random! -# In this version, buffer(0) has been replaced by Shapely 2.0's make_valid function, -# which is MUCH better behaved - EXCEPT that when applied to a GeoSeries it apparently -# removes the CRS - which then creates problems for functions that use @require_same_crs. -# So here we need to be careful throughout to reassign the correct CRS to a GeoSeries -# after applying the make_valid function. - - -class AreaCroppingWarning(UserWarning): - pass - - -def holes_of_union(geometries): - """Returns any holes in the union of the given geometries.""" - geometries = get_geometries(geometries) - if not all( - isinstance(geometry, (Polygon, MultiPolygon)) for geometry in geometries - ): - raise TypeError(f"Must be a Polygon or MultiPolygon (got types {set([x.geom_type for x in geometries])})!") - - union = unary_union(geometries) - series = holes(union) - series.crs = geometries.crs - return series - - -def holes(geometry): - if isinstance(geometry, MultiPolygon): - return GeoSeries( - [ - Polygon(list(hole.coords)) - for polygon in geometry.geoms - for hole in polygon.interiors - ] - ) - elif isinstance(geometry, Polygon): - return GeoSeries([Polygon(list(hole.coords)) for hole in geometry.interiors]) - else: - raise TypeError("geometry must be a Polygon or MultiPolygon to have holes") - - - - -def close_gaps(geometries, relative_threshold=0.1): - """Closes gaps between geometries by assigning the hole to the polygon - that shares the most perimeter with the hole. - - If the area of the gap is greater than `relative_threshold` times the - area of the polygon, then the gap is left alone. The default value - of `relative_threshold` is 0.1. This is intended to preserve intentional - gaps while closing the tiny gaps that can occur as artifacts of - geospatial operations. Set `relative_threshold=None` to attempt close all - gaps. Due to floating point precision issues, all gaps may not be closed. - """ - geometries = get_geometries(geometries) - gaps = holes_of_union(geometries) - return absorb_by_shared_perimeter( - gaps, geometries, relative_threshold=relative_threshold - ) - - -def resolve_overlaps(geometries, relative_threshold=0.1): - """For any pair of overlapping geometries, assigns the overlapping area to the - geometry that shares the most perimeter with the overlap. Returns the GeoSeries - of geometries, which will have no overlaps. - - If the ratio of the overlap's area to either of the overlapping geometries' - areas is greater than `relative_threshold`, then the overlap is ignored. - The default `relative_threshold` is `0.1`. This default is chosen to include - tiny overlaps that can be safely auto-fixed while preserving major overlaps - that might indicate deeper issues and should be handled on a case-by-case - basis. Set `relative_threshold=None` to attempt to resolve all overlaps. Due - to floating point precision issues, all overlaps may not be resolved. - """ - geometries = get_geometries(geometries) - inters = adjacencies(geometries, warn_for_islands=False, warn_for_overlaps=False) - overlaps = inters[inters.area > 0].make_valid() - - if relative_threshold is not None: - left_areas, right_areas = split_by_level(geometries.area, overlaps.index) - under_threshold = ((overlaps.area / left_areas) < relative_threshold) & ( - (overlaps.area / right_areas) < relative_threshold - ) - overlaps = overlaps[under_threshold] - - if len(overlaps) == 0: - return geometries - - to_remove = GeoSeries( - pandas.concat([overlaps.droplevel(1), overlaps.droplevel(0)]), crs=overlaps.crs - ) - with_overlaps_removed = geometries.apply(lambda x: x.difference(unary_union(to_remove))) - - return absorb_by_shared_perimeter( - overlaps, with_overlaps_removed, relative_threshold=None - ) - -def autorepair(geometries, relative_threshold=0.1): - """ - Applies all the tricks in `maup.repair` with default args. Should work by default. - The default `relative_threshold` is `0.1`. This default is chosen to include - tiny overlaps that can be safely auto-fixed while preserving major overlaps - that might indicate deeper issues and should be handled on a case-by-case - basis. Set `relative_threshold=None` to attempt to resolve all overlaps. See - `resolve_overlaps()` and `close_gaps()` for more. - """ - orig_crs = geometries.crs - geometries = get_geometries(geometries) - - geometries = remove_repeated_vertices(geometries).make_valid() - geometries = resolve_overlaps(geometries, relative_threshold=relative_threshold).make_valid() - geometries = close_gaps(geometries, relative_threshold=relative_threshold).make_valid() - - return geometries - - -def remove_repeated_vertices(geometries): - """ - Removes repeated vertices. Vertices are considered to be repeated if they - appear consecutively, excluding the start and end points. - """ - return geometries.geometry.apply(lambda x: apply_func_to_polygon_parts(x, dedup_vertices)) - - -def snap_to_grid(geometries, n=-7): - """ - Snap the geometries to a grid by rounding to the nearest 10^n. Helps to - resolve floating point precision issues in shapefiles. - """ - func = functools.partial(snap_polygon_to_grid, n=n) - return geometries.geometry.apply(lambda x: apply_func_to_polygon_parts(x, func)) - - -@require_same_crs -def crop_to(source, target): - """ - Crops the source geometries to the target geometries. - """ - target_union = unary_union(get_geometries(target)) - cropped_geometries = get_geometries(source).apply(lambda x: x.intersection(target_union)) - - if (cropped_geometries.area == 0).any(): - warnings.warn("Some cropped geometries have zero area, likely due to\n"+ - "large differences in the union of the geometries in your\n"+ - "source and target shapefiles. This may become an issue\n"+ - "when maupping.\n", - AreaCroppingWarning - ) - - return cropped_geometries - -@require_same_crs -def expand_to(source, target): - """ - Expands the source geometries to the target geometries. - """ - geometries = get_geometries(source).make_valid() - - source_union = unary_union(geometries) - - leftover_geometries = get_geometries(target).apply(lambda x: x - source_union) - leftover_geometries = leftover_geometries[~leftover_geometries.is_empty].explode(index_parts=False) - - geometries = absorb_by_shared_perimeter( - leftover_geometries, get_geometries(source), relative_threshold=None - ) - - return geometries - - - -def doctor(source, target=None): - """ - Detects quality issues in a given set of source and target geometries. Quality - issues include overlaps, gaps, invalid geometries, repeated verticies, non-perfect - tiling, and not entirely overlapping source and targets. If `maup.doctor()` returns - `True`, votes should not be lost when prorating or assigning (beyond a few due to - rounding, etc.). Passing a target to doctor is optional. - """ - shapefiles = [source] - source_union = unary_union(get_geometries(source)) - - # Adding "health_check" variable to return instead of using assertions. - health_check = True - - if target is not None: - shapefiles.append(target) - - target_union = unary_union(get_geometries(target)) - sym_area = target_union.symmetric_difference(source_union).area - - if sym_area != 0: - print("The unions of target and source differ!") - health_check = False - - for shp in shapefiles: - if not shp.geometry.apply(lambda x: isinstance(x, (Polygon, MultiPolygon))).all(): - print("Some rows do not have geometries.") - health_check = False - - overlaps = count_overlaps(shp) - holes = len(holes_of_union(shp)) - - if overlaps != 0: - print("There are", overlaps, "overlaps.") - health_check = False - if holes != 0: - print("There are", holes, "holes.") - health_check = False - if not shp.is_valid.all(): - print("There are some invalid geometries.") - health_check = False - - return health_check - - -def count_overlaps(shp): - """ - Counts overlaps. Code is taken directly from the resolve_overlaps function in maup. - """ - inters = adjacencies(shp.geometry, warn_for_islands=False, warn_for_overlaps=False) - overlaps = inters[inters.area > 0].make_valid() - return len(overlaps) - - -def count_holes(shp): - gaps = holes_of_union(shp.geometry) - return(len(gaps)) - - -def apply_func_to_polygon_parts(shape, func): - if isinstance(shape, Polygon): - return func(shape) - elif isinstance(shape, MultiPolygon): - return MultiPolygon([func(poly) for poly in shape.geoms]) - else: - raise TypeError(f"Can only apply {func} to a Polygon or MultiPolygon (got {shape} with type {type(shape)})!") - - -def dedup_vertices(polygon): - if len(polygon.interiors) == 0: - deduped_vertices = [] - for c, p in enumerate(list(polygon.exterior.coords)): - if c == 0: - deduped_vertices.append(p) - elif p != deduped_vertices[-1]: - deduped_vertices.append(p) - return Polygon(deduped_vertices) - - else: - deduped_vertices_exterior = [] - for c, p in enumerate(list(polygon.exterior.coords)): - if c == 0: - deduped_vertices_exterior.append(p) - elif p != deduped_vertices_exterior[-1]: - deduped_vertices_exterior.append(p) - - deduped_vertices_interiors = [] - for interior_ring in polygon.interiors: - deduped_vertices_this_ring = [] - for c, p in enumerate(list(interior_ring.coords)): - if c == 0: - deduped_vertices_this_ring.append(p) - elif p != deduped_vertices_this_ring[-1]: - deduped_vertices_this_ring.append(p) - deduped_vertices_interiors.append(deduped_vertices_this_ring) - return Polygon(deduped_vertices_exterior, holes = deduped_vertices_interiors) - - -def snap_polygon_to_grid(polygon, n=-7): - if len(polygon.interiors) == 0: - return Polygon([(round(x, -n), round(y, -n)) for x, y in polygon.exterior.coords]) - else: - return Polygon([(round(x, -n), round(y, -n)) for x, y in polygon.exterior.coords], holes = [[(round(x, -n), round(y, -n)) for x, y in interior_ring.coords] for interior_ring in polygon.interiors]) - - -def split_by_level(series, multiindex): - return tuple( - multiindex.get_level_values(i).to_series(index=multiindex).map(series) - for i in range(multiindex.nlevels) - ) - - -@require_same_crs -def absorb_by_shared_perimeter(sources, targets, relative_threshold=None): - if len(sources) == 0: - return targets - - if len(targets) == 0: - raise IndexError("targets must be nonempty") - - inters = intersections(sources, targets, area_cutoff=None).make_valid() - - assignment = assign_to_max(inters.length) - - if relative_threshold is not None: - under_threshold = ( - sources.area / assignment.map(targets.area) - ) < relative_threshold - assignment = assignment[under_threshold] - - sources_to_absorb = GeoSeries( - sources.groupby(assignment).apply(unary_union), crs=sources.crs, - ) - - # Note that the following line produces a warning message when sources_to_absorb - # and targets have different indices: - - # "lib/python3.11/site-packages/geopandas/base.py:31: UserWarning: The indices of - # the two GeoSeries are different. - # warn("The indices of the two GeoSeries are different.") - - # This difference in indices is expected since not all target geometries may have sources - # to absorb, so it would be nice to remove this warning. - result = targets.union(sources_to_absorb) - - # The .union call only returns the targets who had a corresponding - # source to absorb. Now we fill in all of the unchanged targets. - result = result.reindex(targets.index) - did_not_absorb = result.isna() | result.is_empty - result.loc[did_not_absorb] = targets[did_not_absorb] - - return result +import functools +import warnings + +import pandas + +from geopandas import GeoSeries +from shapely.geometry import Polygon, MultiPolygon, LineString, MultiLineString +from shapely.ops import unary_union + +from .adjacencies import adjacencies +from .assign import assign_to_max +from .crs import require_same_crs +from .indexed_geometries import get_geometries +from .intersections import intersections + + +""" +Some of these functions are based on the functions in Mary Barker's +check_shapefile_connectivity.py script in @gerrymandr/Preprocessing. +""" + +# IMPORTANT TO BE AWARE OF FOR FUTURE UPDATES: +# A previous version of this file used buffer(0) to simplify geometries, but this only +# works properly for polygons. For 1-D objects such as LineStrings, it kills them off +# completely - and this resulted in some pretty disastrous choices in the +# absorb_by_shared_perimeter function when ALL the perimeters simplified to zero and +# the choice of which geometry to absorb into was essentially random! +# In this version, buffer(0) has been replaced by Shapely 2.0's make_valid function, +# which is MUCH better behaved - EXCEPT that when applied to a GeoSeries via +# geoseries = make_valid(geoseries) +# it apparently removes the CRS - which then creates problems for functions that use +# @require_same_crs. +# Note that it appears to work correctly if we use the format +# geoseries = geoseries.make_valid() + + +class AreaCroppingWarning(UserWarning): + pass + + +def holes_of_union(geometries): + """Returns any holes in the union of the given geometries.""" + geometries = get_geometries(geometries) + if not all( + isinstance(geometry, (Polygon, MultiPolygon)) for geometry in geometries + ): + raise TypeError(f"Must be a Polygon or MultiPolygon (got types {set([x.geom_type for x in geometries])})!") + + union = unary_union(geometries) + series = holes(union) + series.crs = geometries.crs + return series + + +def holes(geometry): + """Returns any holes in a Polygon or MultiPolygon.""" + if isinstance(geometry, MultiPolygon): + return GeoSeries( + [ + Polygon(list(hole.coords)) + for polygon in geometry.geoms + for hole in polygon.interiors + ] + ) + elif isinstance(geometry, Polygon): + return GeoSeries([Polygon(list(hole.coords)) for hole in geometry.interiors]) + else: + raise TypeError("geometry must be a Polygon or MultiPolygon to have holes") + + +def close_gaps(geometries, relative_threshold=0.1): + """Closes gaps between geometries by assigning the hole to the polygon + that shares the greatest perimeter with the hole. + + If the area of the gap is greater than `relative_threshold` times the + area of the polygon, then the gap is left alone. The default value + of `relative_threshold` is 0.1. This is intended to preserve intentional + gaps while closing the tiny gaps that can occur as artifacts of + geospatial operations. Set `relative_threshold=None` to attempt close all + gaps. Due to floating point precision issues, all gaps may not be closed. + """ + geometries = get_geometries(geometries) + gaps = holes_of_union(geometries) + return absorb_by_shared_perimeter( + gaps, geometries, relative_threshold=relative_threshold + ) + + +def resolve_overlaps(geometries, relative_threshold=0.1): + """For any pair of overlapping geometries, assigns the overlapping area to the + geometry that shares the greatest perimeter with the overlap. Returns the GeoSeries + of geometries, which will have no overlaps. + + If the ratio of the overlap's area to either of the overlapping geometries' + areas is greater than `relative_threshold`, then the overlap is ignored. + The default `relative_threshold` is `0.1`. This default is chosen to include + tiny overlaps that can be safely auto-fixed while preserving major overlaps + that might indicate deeper issues and should be handled on a case-by-case + basis. Set `relative_threshold=None` to attempt to resolve all overlaps. Due + to floating point precision issues, all overlaps may not be resolved. + """ + geometries = get_geometries(geometries) + inters = adjacencies(geometries, warn_for_islands=False, warn_for_overlaps=False) + overlaps = inters[inters.area > 0].make_valid() + + if relative_threshold is not None: + left_areas, right_areas = split_by_level(geometries.area, overlaps.index) + under_threshold = ((overlaps.area / left_areas) < relative_threshold) & ( + (overlaps.area / right_areas) < relative_threshold + ) + overlaps = overlaps[under_threshold] + + if len(overlaps) == 0: + return geometries + + to_remove = GeoSeries( + pandas.concat([overlaps.droplevel(1), overlaps.droplevel(0)]), crs=overlaps.crs + ) + with_overlaps_removed = geometries.apply(lambda x: x.difference(unary_union(to_remove))) + + return absorb_by_shared_perimeter( + overlaps, with_overlaps_removed, relative_threshold=None + ) + + +def quick_repair(geometries, relative_threshold=0.1): + """ + New name for autorepair function from Maup 1.x. + Uses simplistic algorithms to repair most gaps and overlaps. + + The default `relative_threshold` is `0.1`. This default is chosen to include + tiny overlaps that can be safely auto-fixed while preserving major overlaps + that might indicate deeper issues and should be handled on a case-by-case + basis. Set `relative_threshold=None` to attempt to resolve all overlaps. See + `resolve_overlaps()` and `close_gaps()` for more. + + For a more careful repair that takes adjacencies and higher-order overlaps + between geometries into account, consider using smart_repair instead. + """ + return autorepair(geometries, relative_threshold=relative_threshold) + + +def autorepair(geometries, relative_threshold=0.1): + """ + Uses simplistic algorithms to repair most gaps and overlaps. + + The default `relative_threshold` is `0.1`. This default is chosen to include + tiny overlaps that can be safely auto-fixed while preserving major overlaps + that might indicate deeper issues and should be handled on a case-by-case + basis. Set `relative_threshold=None` to attempt to resolve all overlaps. See + `resolve_overlaps()` and `close_gaps()` for more. + + For a more careful repair that takes adjacencies and higher-order overlaps + between geometries into account, consider using smart_repair instead. + """ + geometries = get_geometries(geometries) + + geometries = remove_repeated_vertices(geometries).make_valid() + geometries = resolve_overlaps(geometries, relative_threshold=relative_threshold).make_valid() + geometries = close_gaps(geometries, relative_threshold=relative_threshold).make_valid() + + return geometries + + +def remove_repeated_vertices(geometries): + """ + Removes repeated vertices. Vertices are considered to be repeated if they + appear consecutively, excluding the start and end points. + """ + return geometries.geometry.apply(lambda x: apply_func_to_polygon_parts(x, dedup_vertices)) + + +def snap_to_grid(geometries, n=-7): + """ + Snap the geometries to a grid by rounding to the nearest 10^n. Helps to + resolve floating point precision issues in shapefiles. + """ + func = functools.partial(snap_polygon_to_grid, n=n) + return geometries.geometry.apply(lambda x: apply_func_to_polygon_parts(x, func)) + + +@require_same_crs +def crop_to(source, target): + """ + Crops the source geometries to the target geometries. + """ + target_union = unary_union(get_geometries(target)) + cropped_geometries = get_geometries(source).apply(lambda x: x.intersection(target_union)) + + if (cropped_geometries.area == 0).any(): + warnings.warn("Some cropped geometries have zero area, likely due to\n" + + "large differences in the union of the geometries in your\n" + + "source and target shapefiles. This may become an issue\n" + + "when maupping.\n", + AreaCroppingWarning + ) + + return cropped_geometries + + +@require_same_crs +def expand_to(source, target): + """ + Expands the source geometries to the target geometries. + """ + geometries = get_geometries(source).make_valid() + + source_union = unary_union(geometries) + + leftover_geometries = get_geometries(target).apply(lambda x: x - source_union) + leftover_geometries = leftover_geometries[~leftover_geometries.is_empty].explode(index_parts=False) + + geometries = absorb_by_shared_perimeter( + leftover_geometries, get_geometries(source), relative_threshold=None + ) + + return geometries + + +def doctor(source, target=None, silent=False, accept_holes=False): + """ + Detects quality issues in a given set of source and target geometries. Quality + issues include overlaps, gaps, invalid geometries, non-perfect + tiling, and not entirely overlapping source and targets. If `maup.doctor()` returns + `True`, votes should not be lost when prorating or assigning (beyond a few due to + rounding, etc.). Passing a target to doctor is optional. + + If silent is True, then print outputs are suppressed. (Default is silent = False.) + + If accept_holes is True, then holes alone do not cause doctor to return a value of + False. (Default is accept_holes = False.) + """ + shapefiles = [source] + source_union = unary_union(get_geometries(source)) + + health_check = True + + if target is not None: + shapefiles.append(target) + + target_union = unary_union(get_geometries(target)) + sym_area = target_union.symmetric_difference(source_union).area + + if sym_area != 0: + if silent is False: + print("The unions of target and source differ!") + health_check = False + + for shp in shapefiles: + if not shp.geometry.apply(lambda x: isinstance(x, (Polygon, MultiPolygon))).all(): + if silent is False: + print("Some rows do not have geometries.") + health_check = False + + overlaps = count_overlaps(shp) + num_holes = len(holes_of_union(shp)) + + if overlaps != 0: + if silent is False: + print("There are", overlaps, "overlaps.") + health_check = False + if num_holes != 0: + if silent is False: + print("There are", num_holes, "holes.") + if accept_holes is False: + health_check = False + if not shp.is_valid.all(): + if silent is False: + print("There are some invalid geometries.") + health_check = False + + return health_check + + +def count_overlaps(shp): + """ + Counts overlaps between geometries. + Code is taken directly from the resolve_overlaps function in maup. + """ + inters = adjacencies(shp.geometry, warn_for_islands=False, warn_for_overlaps=False) + overlaps = inters[inters.area > 0].make_valid() + return len(overlaps) + + +def count_holes(shp): + """ + Counts gaps between geometries. + """ + gaps = holes_of_union(shp.geometry) + return len(gaps) + + +def apply_func_to_polygon_parts(shape, func): + if isinstance(shape, Polygon): + return func(shape) + elif isinstance(shape, MultiPolygon): + return MultiPolygon([func(poly) for poly in shape.geoms]) + else: + raise TypeError(f"Can only apply {func} to a Polygon or MultiPolygon (got {shape} with type {type(shape)})!") + + +def dedup_vertices(polygon): + if len(polygon.interiors) == 0: + deduped_vertices = [] + for c, p in enumerate(list(polygon.exterior.coords)): + if c == 0: + deduped_vertices.append(p) + elif p != deduped_vertices[-1]: + deduped_vertices.append(p) + return Polygon(deduped_vertices) + + else: + deduped_vertices_exterior = [] + for c, p in enumerate(list(polygon.exterior.coords)): + if c == 0: + deduped_vertices_exterior.append(p) + elif p != deduped_vertices_exterior[-1]: + deduped_vertices_exterior.append(p) + + deduped_vertices_interiors = [] + for interior_ring in polygon.interiors: + deduped_vertices_this_ring = [] + for c, p in enumerate(list(interior_ring.coords)): + if c == 0: + deduped_vertices_this_ring.append(p) + elif p != deduped_vertices_this_ring[-1]: + deduped_vertices_this_ring.append(p) + deduped_vertices_interiors.append(deduped_vertices_this_ring) + return Polygon(deduped_vertices_exterior, holes=deduped_vertices_interiors) + + +def snap_polygon_to_grid(polygon, n=-7): + if len(polygon.interiors) == 0: + return Polygon([(round(x, -n), round(y, -n)) for x, y in polygon.exterior.coords]) + else: + return Polygon([(round(x, -n), round(y, -n)) for x, y in polygon.exterior.coords], holes=[[(round(x, -n), round(y, -n)) for x, y in interior_ring.coords] for interior_ring in polygon.interiors]) + + +def snap_multilinestring_to_grid(multilinestring, n=-7): + if multilinestring.geom_type == "LineString": + return LineString([(round(x, -n), round(y, -n)) for x, y in multilinestring.coords]) + elif multilinestring.geom_type == "MultiLineString": + return MultiLineString([LineString([(round(x, -n), round(y, -n)) for x, y in linestring.coords]) for linestring in multilinestring.geoms]) + + +def split_by_level(series, multiindex): + return tuple( + multiindex.get_level_values(i).to_series(index=multiindex).map(series) + for i in range(multiindex.nlevels) + ) + + +@require_same_crs +def absorb_by_shared_perimeter(sources, targets, relative_threshold=None): + if len(sources) == 0: + return targets + + if len(targets) == 0: + raise IndexError("targets must be nonempty") + + inters = intersections(sources, targets, area_cutoff=None).make_valid() + + assignment = assign_to_max(inters.length) + + if relative_threshold is not None: + under_threshold = ( + sources.area / assignment.map(targets.area) + ) < relative_threshold + assignment = assignment[under_threshold] + + sources_to_absorb = GeoSeries( + sources.groupby(assignment).apply(unary_union), crs=sources.crs, + ) + + # Note that the following line produces a warning message when sources_to_absorb + # and targets have different indices: + + # "lib/python3.11/site-packages/geopandas/base.py:31: UserWarning: The indices of + # the two GeoSeries are different. + # warn("The indices of the two GeoSeries are different.") + + # This difference in indices is expected since not all target geometries may have sources + # to absorb, so it would be nice to remove this warning. + result = targets.union(sources_to_absorb) + + # The .union call only returns the targets who had a corresponding + # source to absorb. Now we fill in all of the unchanged targets. + result = result.reindex(targets.index) + did_not_absorb = result.isna() | result.is_empty + result.loc[did_not_absorb] = targets[did_not_absorb] + + return result diff --git a/maup/smart_repair.py b/maup/smart_repair.py new file mode 100644 index 0000000..6d06900 --- /dev/null +++ b/maup/smart_repair.py @@ -0,0 +1,1591 @@ +import math +import warnings +from collections import deque + +import numpy +import pandas +import shapely + +from geopandas import GeoSeries, GeoDataFrame +from shapely import make_valid, extract_unique_points +from shapely.strtree import STRtree +from shapely.ops import unary_union, polygonize, linemerge, nearest_points +from shapely.geometry import Polygon, MultiPolygon, Point, MultiPoint, LineString, MultiLineString +from shapely.geometry.polygon import orient +from tqdm import tqdm, TqdmWarning + +from .adjacencies import adjacencies +from .assign import assign +from .indexed_geometries import get_geometries +from .intersections import intersections +from .progress_bar import progress +from .repair import doctor, snap_to_grid + +warnings.filterwarnings('ignore', 'GeoSeries.isna', UserWarning) +warnings.filterwarnings("ignore", category=TqdmWarning) + +pandas.options.mode.chained_assignment = None + + +""" +Some of these functions are based on the functions in Mary Barker's +check_shapefile_connectivity.py script in @gerrymandr/Preprocessing. + +Updated functions for maup 2.0.0 were written by Jeanne Clelland. +""" + +######### +# MAIN REPAIR FUNCTION +######### + + +def smart_repair(geometries_df, snapped=True, fill_gaps=True, fill_gaps_threshold=0.1, + disconnection_threshold=0.0001, nest_within_regions=None, + min_rook_length=None): + """ + Repairs topology issues (overlaps, gaps, invalid polygons) in a geopandas + GeoDataFrame or GeoSeries, with an emphasis on preserving intended adjacency + relations between geometries as closely as possible. + + Specifically, the algorithm + (1) Applies shapely.make_valid to all polygon geometries. + (2) If snapped = True (default), snaps all polygon vertices to a grid of size no + more than 10^(-10) times the max of width/height of the entire shapefile extent. + HIGHLY RECOMMENDED to avoid topological exceptions due to rounding errors. + (3) Resolves all overlaps. + (4) If fill_gaps = True (default), closes all simply connected gaps with area + less than fill_gaps_threshold times the largest area of all geometries adjoining + the gap. Default threshold is 10%; if fill_gaps_threshold = None then all + simply connected gaps will be filled. + (5) If nest_within_regions is a secondary shapefile of region boundaries (e.g., + counties in a state) then all of the above will be performed so that repaired + geometries nest cleanly into the region boundaries; each repaired geometry + will be contained in the region with which the original geometry has the largest + area of intersection. Default value is None. + (6) If min_rook_length is given a numerical value, replaces all rook adjacencies + with length below this value with queen adjacencies. Note that this is an + absolute value and not a relative value, so make sure that the value provided + is in the correct units with respect to the shapefile's CRS. + Default value is None. + (7) Sometimes the repair process creates tiny fragments that are disconnected from + the district that they are assigned to. A final cleanup step assigns any such + fragments to a neighboring geometry if their area is less than + disconnection_threshold times the area of the largest connected component of + their assigned geometry. Default threshold is 0.01%, and this seems to work + well in practice. + """ + + # Keep a copy of the original input for comparisons later! + if isinstance(geometries_df, GeoSeries): + orig_input_type = "geoseries" + geometries_df = GeoDataFrame(geometry=geometries_df) + geometries0_df = geometries_df.copy() + elif isinstance(geometries_df, GeoDataFrame): + orig_input_type = "geodataframe" + geometries_df = geometries_df.copy() + geometries0_df = geometries_df.copy() + else: + raise TypeError("Input geometries must be in the form of a geopandas GeoSeries or GeoDataFrame.") + + # Ensure that geometries are 2-D and not 3-D: + for i in geometries_df.index: + geometries_df["geometry"][i] = shapely.wkb.loads( + shapely.wkb.dumps(geometries_df["geometry"][i], output_dimension=2)) + + # Ensure that crs is not geographic: + if geometries_df.crs is not None: + if geometries_df.crs.is_geographic: + raise Exception("Input geometries must be in a projected, non-geographic CRS. To project a GeoDataFrame 'gdf' to UTM, use 'gdf = gdf.to_crs(gdf.estimate_utm_crs())' ") + + # If nest_within_regions is not None, require it to have the same CRS as the main shapefile + # and set regions_df equal to a GeoDataFrame version. + # nest_within_regions is None, set regions_df equal to None so we can use it as a parameter later. + if nest_within_regions is None: + regions_df = None + else: + if isinstance(nest_within_regions, GeoSeries): + regions_df = GeoDataFrame(geometry=nest_within_regions) + elif isinstance(nest_within_regions, GeoDataFrame): + regions_df = nest_within_regions.copy() + else: + raise TypeError("nest_within_regions must be a geopandas GeoSeries or GeoDataFrame.") + + if nest_within_regions.crs != geometries_df.crs: + raise Exception("nest_within_regions must be in the same CRS as the geometries being repaired.") + if doctor(nest_within_regions, silent=True, accept_holes=True) is False: + raise Exception("nest_within_regions must be topologically clean---i.e., all geometries must be valid and there must be no overlaps between geometries. Generally the best source for region shapefiles is the U.S. Census Burueau.") + + # Before doing anything else, make sure all polygons are valid and remove any + # LineStrings and MultiLineStrings. + for i in geometries_df.index: + geometries_df["geometry"][i] = make_valid(geometries_df["geometry"][i]) + if geometries_df["geometry"][i].geom_type == "GeometryCollection": + geometries_df["geometry"][i] = unary_union([x for x in geometries_df["geometry"][i].geoms if x.geom_type in ("Polygon", "MultiPolygon")]) + + # If snapped is True, snap all polygon vertices to a grid of size no more than + # 10^(-10) times the max of width/height of the entire shapefile extent. + # (For instance, in Texas this would be less than 1/100th of an inch.) + # This avoids a rare "non-noded intersection" error due to a GEOS bug and leaves + # several orders of magnitude for additional intersection operations before hitting + # python's precision limit of about 10^(-15). + if snapped: + # These bounds are in the form (xmin, ymin, xmax, ymax) + geometries_total_bounds = geometries_df.total_bounds + largest_bound = max(geometries_total_bounds[2] - geometries_total_bounds[0], geometries_total_bounds[3] - geometries_total_bounds[1]) + snap_magnitude = int(math.log10(largest_bound)) - 10 + geometries_df["geometry"] = snap_to_grid(geometries_df["geometry"], n=snap_magnitude) + if nest_within_regions is not None: + regions_df["geometry"] = snap_to_grid(regions_df["geometry"], n=snap_magnitude) + + # Snapping could possibly have created some invalid polygons, so do another round + # of validity checks - and do a validity check for regions as well, if applicable. + for i in geometries_df.index: + geometries_df["geometry"][i] = make_valid(geometries_df["geometry"][i]) + if geometries_df["geometry"][i].geom_type == "GeometryCollection": + geometries_df["geometry"][i] = unary_union([x for x in geometries_df["geometry"][i].geoms if x.geom_type in ("Polygon", "MultiPolygon")]) + if nest_within_regions is not None: + for i in regions_df.index: + regions_df["geometry"][i] = make_valid(regions_df["geometry"][i]) + if regions_df["geometry"][i].geom_type == "GeometryCollection": + regions_df["geometry"][i] = unary_union([x for x in regions_df["geometry"][i].geoms if x.geom_type in ("Polygon", "MultiPolygon")]) + print("Snapping all geometries to a grid with precision 10^(", snap_magnitude, ") to avoid GEOS errors.") + + # Construct data about overlaps of all orders, plus holes. + overlap_tower, holes_df = building_blocks(geometries_df, nest_within_regions=regions_df) + + # Use data from the overlap tower to rebuild geometries with no overlaps. + # If nest_within_regions is not None, resolve overlaps and fill holes (if applicable) + # for each region separately. + + if nest_within_regions is None: + print("Resolving overlaps...") + reconstructed_df = reconstruct_from_overlap_tower(geometries_df, overlap_tower) + + # Use data about the holes to fill holes if applicable. + if fill_gaps: + # First remove any holes above the relative area threshold (if any). + # Also remove any non-simply connected holes since our algorithm breaks + # down in that case, regardless of whether or not a relative area + # threshold has been set. + holes_df, num_holes_dropped = drop_bad_holes(reconstructed_df, holes_df, fill_gaps_threshold=fill_gaps_threshold) + if num_holes_dropped > 0: + print(num_holes_dropped, "gaps will remain unfilled, because they either are not simply connected or exceed the area threshold.") + + print("Filling gaps...") + reconstructed_df = smart_close_gaps(reconstructed_df, holes_df) + + else: + if fill_gaps: + print("Resolving overlaps and filling gaps...") + else: + print("Resolving overlaps...") + + reconstructed_df = geometries_df.copy() + geometries_to_regions_assignment = assign(geometries_df.geometry, regions_df.geometry) + + for r_ind in nest_within_regions.index: + geometries_this_region_indices = [g_ind for g_ind in geometries_df.index if geometries_to_regions_assignment[g_ind] == r_ind] + geometries_this_region_df = geometries_df.loc[geometries_this_region_indices] + + overlap_tower_this_region = [] + for i in range(len(overlap_tower)): + overlap_tower_this_region.append(overlap_tower[i][overlap_tower[i]["region"] == r_ind]) + + reconstructed_this_region_df = reconstruct_from_overlap_tower(geometries_this_region_df, overlap_tower_this_region, nested=True) + + if fill_gaps: + holes_this_region_df = holes_df[holes_df["region"] == r_ind] + # First remove any holes above the relative area threshold (if any). + # Also remove any non-simply connected holes since our algorithm breaks + # down in that case, regardless of whether or not a relative area + # threshold has been set. + holes_this_region_df, num_holes_dropped_this_region = drop_bad_holes(reconstructed_this_region_df, holes_this_region_df, fill_gaps_threshold=fill_gaps_threshold) + if num_holes_dropped_this_region > 0: + print(num_holes_dropped_this_region, "gaps in region", r_ind, "will remain unfilled, because they either are not simply connected or exceed the area threshold.") + + reconstructed_this_region_df = smart_close_gaps(reconstructed_this_region_df, holes_this_region_df) + + reconstructed_df["geometry"].loc[list(reconstructed_this_region_df.index)] = reconstructed_this_region_df["geometry"] + + # Check for geometries that have become (more) disconnected, generally with an extra + # component of negligible area. If any are found and the area is negligible, + # reassign to an adjacent geometry by shared perimeter. + # If the area is not negligible, leave it alone and report it so that the user + # can decide what to do about it. + + disconnected_df = reconstructed_df[reconstructed_df["geometry"].apply(lambda x: x.geom_type != "Polygon")] + + # This will include geometries that were disconnected in the original; need to + # filter by whether they got worse. + + if len(disconnected_df) > 0: + disconnected_poly_indices = [] + for ind in disconnected_df.index: + if num_components(reconstructed_df["geometry"][ind]) > num_components(geometries0_df["geometry"][ind]): + disconnected_poly_indices.append(ind) + + if len(disconnected_poly_indices) > 0: + # These are the ones (if any) that got worse. + geometries = get_geometries(reconstructed_df) + spatial_index = STRtree(geometries) + index_by_iloc = dict((i, list(geometries.index)[i]) for i in range(len(geometries.index))) + + for g_ind in disconnected_poly_indices: + excess = num_components(reconstructed_df["geometry"][g_ind]) - num_components(geometries0_df["geometry"][g_ind]) + component_num_list = list(range(len(reconstructed_df["geometry"][g_ind].geoms))) + component_areas = [] + + for c_ind in range(len(reconstructed_df["geometry"][g_ind].geoms)): + component_areas.append((c_ind, reconstructed_df["geometry"][g_ind].geoms[c_ind].area)) + + component_areas_sorted = sorted(component_areas, key=lambda tup: tup[1]) + big_area = max([reconstructed_df["geometry"][g_ind].area, geometries0_df["geometry"][g_ind].area]) + + for i in range(excess): + # Check whether the ith smallest component has small enough area, and if + # so find a better polygon to add it to. + c_ind = component_areas_sorted[i][0] + this_fragment = reconstructed_df["geometry"][g_ind].geoms[c_ind] + if component_areas_sorted[i][1] < disconnection_threshold*big_area: + possible_intersect_integer_indices = [*set(numpy.ndarray.flatten(spatial_index.query(this_fragment)))] + possible_intersect_indices = [(index_by_iloc[k]) for k in possible_intersect_integer_indices] + + if nest_within_regions is not None: + # Restrict to geometries in the same region as this geometry + possible_intersect_indices = [ind for ind in possible_intersect_indices if geometries_to_regions_assignment[ind] == geometries_to_regions_assignment[g_ind]] + + shared_perimeters = [] + for g_ind2 in possible_intersect_indices: + if g_ind2 != g_ind and not (this_fragment.boundary).intersection(reconstructed_df["geometry"][g_ind2].boundary).is_empty: + shared_perimeters.append((g_ind2, (this_fragment.boundary).intersection(reconstructed_df["geometry"][g_ind2].boundary).length)) + + # If this is an isolated fragment and doesn't touch any other + # geometries, leave it alone; otherwise, choose a geometry to + # adjoin it to by largest shared perimeter. + if len(shared_perimeters) > 0: + component_num_list.remove(c_ind) # Tells us to take out this component later + max_shared_perim = sorted(shared_perimeters, key=lambda tup: tup[1])[-1] + poly_to_add_to = max_shared_perim[0] + reconstructed_df["geometry"][poly_to_add_to] = unary_union( + [reconstructed_df["geometry"][poly_to_add_to], this_fragment]) + + if len(component_num_list) == 1: + reconstructed_df["geometry"][g_ind] = reconstructed_df["geometry"][g_ind].geoms[component_num_list[0]] + elif len(component_num_list) > 1: + reconstructed_df["geometry"][g_ind] = MultiPolygon( + [reconstructed_df["geometry"][g_ind].geoms[c_ind] for c_ind in component_num_list]) + else: + print("WARNING: A component of the geometry at index", g_ind, "was badly disconnected and redistributed to other geometries!") + + # We should usually now be back to the correct number of components everywhere, but + # there may occasionally be exceptions, so check again and alert the user if not. + + disconnected_df_2 = reconstructed_df[reconstructed_df["geometry"].apply(lambda x: x.geom_type != "Polygon")] + if len(disconnected_df_2) > 0: + for ind in disconnected_df_2.index: + if num_components(reconstructed_df["geometry"][ind]) > num_components(geometries0_df["geometry"][ind]): + print("WARNING: A component of the geometry at index", ind, "may have been disconnected!") + + if min_rook_length is not None: + # Find all inter-polygon boundaries shorter than min_rook_length and replace them + # with queen adjacencies by manipulating coordinates of all surrounding polygon. + print("Converting small rook adjacencies to queen...") + reconstructed_df = small_rook_to_queen(reconstructed_df, min_rook_length) + + if orig_input_type == "geoseries": + return reconstructed_df.geometry + else: + return reconstructed_df + + +######### +# SUPPORTING FUNCTIONS +######### + +def num_components(geom): + """Counts the number of connected components of a shapely object.""" + if geom.is_empty: + return 0 + elif geom.geom_type in ("Polygon", "Point", "LineString"): + return 1 + elif geom.geom_type in ("MultiPolygon", "MultiLineString", "GeometryCollection"): + return len(geom.geoms) + + +def segments(curve): + """Extracts a list of the individual line segments from a LineString""" + return list(map(LineString, zip(curve.coords[:-1], curve.coords[1:]))) + + +def building_blocks(geometries_df, nest_within_regions=None): + """ + Partitions the extent of the input via all boundaries of all geometries + (and regions, if nest_within_regions is a shapefile of region boundaries); + associates to each polygon in the partition the set of polygons in the original + shapefile whose intersection created it, and organizes this data according to + order of the overlaps. (Order zero = hole) + """ + if isinstance(geometries_df, GeoDataFrame) is False: + raise TypeError("Primary input to building_blocks must be a GeoDataFrame.") + + geometries_df = geometries_df.copy() + if nest_within_regions is not None: + if isinstance(nest_within_regions, GeoDataFrame) is False: + raise TypeError("nest_within_regions must be either None or a GeoDataFrame.") + else: + regions_df = nest_within_regions.copy() + + # Make a list of all the boundaries of all the polygons. + # This won't work properly with MultiPolygons, so explode first: + boundaries = [] + geometries_exploded_df = geometries_df.explode(index_parts=False).reset_index(drop=True) + for i in geometries_exploded_df.index: + boundaries.append(shapely.boundary(geometries_exploded_df["geometry"][i])) + + # Include region boundaries if applicable: + if nest_within_regions is not None: + regions_exploded_df = regions_df.explode(index_parts=False).reset_index(drop=True) + for i in regions_exploded_df.index: + boundaries.append(shapely.boundary(regions_exploded_df["geometry"][i])) + + boundaries_exploded = [] + for geom in boundaries: + if geom.geom_type == "LineString": + boundaries_exploded.append(geom) + elif geom.geom_type == "MultiLineString": + boundaries_exploded += list(geom.geoms) + boundaries_union = shapely.node(MultiLineString(boundaries_exploded)) + + # Create a geodataframe with all the pieces created by overlaps of all orders, + # together with a set for each piece consisting of the polygons that created the overlap. + pieces_df = GeoDataFrame(columns=["polygon indices"], + geometry=GeoSeries(list(polygonize(boundaries_union))), + crs=geometries_df.crs) + + for i in pieces_df.index: + pieces_df["polygon indices"][i] = set() + + # Add a column to indicate the region for each piece; if there are no regions the + # entries will remain as None. + pieces_df["region"] = None + + g_spatial_index = STRtree(geometries_df["geometry"]) + g_index_by_iloc = dict((i, list(geometries_df.index)[i]) for i in range(len(geometries_df))) + + # If region boundaries are included, also create an STRtree for the regions + # and assign the main geometries to regions by largest area overlap. + if nest_within_regions is not None: + r_spatial_index = STRtree(regions_df["geometry"]) + r_index_by_iloc = dict((i, list(regions_df.index)[i]) for i in range(len(regions_df))) + geometries_to_regions_assignment = assign(geometries_df.geometry, regions_df.geometry) + + print("Identifying overlaps...") + for i in progress(pieces_df.index, len(pieces_df.index)): + # If region boundaries are included, identify the region for each piece. + # Note that "None" is a possibility, and that each piece will belong to a unique + # region because the regions shapefile MUST be clean. + if nest_within_regions is not None: + possible_region_integer_indices = [*set(numpy.ndarray.flatten(r_spatial_index.query(pieces_df["geometry"][i])))] + possible_region_indices = [r_index_by_iloc[k] for k in possible_region_integer_indices] + + for j in possible_region_indices: + if pieces_df["geometry"][i].representative_point().intersects(regions_df["geometry"][j]): + pieces_df["region"][i] = j + + # Now identify the set of geometries in the main geometry that each piece is + # contained in. If region boundaries are included, then while determining which + # geometries each piece is contained in, omit any geometries that are + # assigned to a region other than the one the piece is contained in. + possible_geom_integer_indices = [*set(numpy.ndarray.flatten(g_spatial_index.query(pieces_df["geometry"][i])))] + possible_geom_indices = [g_index_by_iloc[k] for k in possible_geom_integer_indices] + + for j in possible_geom_indices: + if nest_within_regions is not None: + if pieces_df["geometry"][i].representative_point().intersects(geometries_df["geometry"][j]): + if geometries_to_regions_assignment[j] == pieces_df["region"][i]: + pieces_df["polygon indices"][i] = pieces_df["polygon indices"][i].union({j}) + else: + if pieces_df["geometry"][i].representative_point().intersects(geometries_df["geometry"][j]): + pieces_df["polygon indices"][i] = pieces_df["polygon indices"][i].union({j}) + + # Organize this info into separate GeoDataFrames for overlaps of all orders - including + # order zero, which corresponds to gaps. + # This will be easier if we temporarily add a column for overlap degree. + overlap_degree_list = [len(x) for x in pieces_df["polygon indices"]] + pieces_df["overlap degree"] = overlap_degree_list + + # Here are the gaps: + holes_df = (pieces_df[pieces_df["overlap degree"] == 0]).reset_index(drop=True) + + # If region boundaries are included, drop all the polygons that didn't fall into any + # region, and also take the (exploded) unary unions of all the gaps in each region, + # since some pieces of geometries from other regions may now be gaps that are adjacent + # to other gaps. + if nest_within_regions is not None: + pieces_df = pieces_df[~pieces_df["region"].isna()].reset_index(drop=True) + holes_df = holes_df[~holes_df["region"].isna()].reset_index(drop=True) + + consolidated_holes_df = GeoDataFrame(columns=["polygon indices", "geometry", "region", "overlap degree"], + geometry="geometry", crs=holes_df.crs) + for r_ind in regions_df.index: + this_region_holes_df = holes_df[holes_df["region"] == r_ind] + this_region_consolidated_holes = GeoSeries([unary_union(this_region_holes_df["geometry"])]).explode(index_parts=False).reset_index(drop=True) + this_region_consolidated_holes_df = GeoDataFrame(geometry=this_region_consolidated_holes, crs=holes_df.crs) + + this_region_consolidated_holes_df.insert(0, "polygon indices", None) + for i in this_region_consolidated_holes_df.index: + this_region_consolidated_holes_df["polygon indices"][i] = set() + this_region_consolidated_holes_df.insert(2, "region", r_ind) + this_region_consolidated_holes_df.insert(2, "overlap degree", 0) + + consolidated_holes_df = pandas.concat([consolidated_holes_df, this_region_consolidated_holes_df]).reset_index(drop=True) + + holes_df = consolidated_holes_df + + # Here is a list of GeoDataFrames, one consisting of all overlaps of each order: + overlap_tower = [] + + for i in range(max(pieces_df["overlap degree"])): + overlap_tower.append(pieces_df[pieces_df["overlap degree"] == i+1]) + + # Drop unnecessary "overlap degree" column and reindex each GeoDataFrame: + for i in range(len(overlap_tower)): + del overlap_tower[i]["overlap degree"] + overlap_tower[i] = overlap_tower[i].reset_index(drop=True) + del holes_df["overlap degree"] + + # Drop the "polygon indices" column in the holes GeoDataFrame: + del holes_df["polygon indices"] + + return overlap_tower, holes_df + + +def reconstruct_from_overlap_tower(geometries_df, overlap_tower, nested=False): + """ + Rebuild the shapefile polygons with overlaps removed. + """ + # Keep a copy of the original input for comparisons later! + geometries0_df = geometries_df.copy() + + geometries_df = geometries_df.copy() + overlap_tower = [df.copy() for df in overlap_tower] + + geometries_df["geometry"] = Polygon() + + max_overlap_level = len(overlap_tower) + + # Start by assigning all order 1 pieces to the polygon they came from: + for ind in overlap_tower[0].index: + this_poly_ind = list(overlap_tower[0]["polygon indices"][ind])[0] + this_piece = overlap_tower[0]["geometry"][ind] + geometries_df["geometry"][this_poly_ind] = unary_union([geometries_df["geometry"][this_poly_ind], this_piece]) + + # We will need to know which geometries were disconnected by removing + # overlaps, so add columns for numbers of components in the original and refined + # geometries to each dataframe for future use. + geometries_df["num components orig"] = 0 + geometries_df["num components refined"] = 0 + + for ind in geometries_df.index: + geometries_df["num components orig"][ind] = num_components(geometries0_df["geometry"][ind]) + geometries_df["num components refined"][ind] = num_components(geometries_df["geometry"][ind]) + + # Now, start with the order 2 overlaps and gradually add overlaps at successively + # higher orders until done. + + # First look for geometries at the top level that were disconnected + # by the refinement process, and give them first dibs at grabbing overlaps + # until they are reconnected or run out of overlaps to grab. + # Note that this doesn't always completely work; in rare cases a single overlap + # can disconnect more than one polygon, and only one of them gets to grab it back. + # This will be addressed at the end of the reconstruction process. + + geometries_disconnected_df = geometries_df[geometries_df["num components refined"] > geometries_df["num components orig"]] + + for i in range(1, max_overlap_level): + overlaps_df = overlap_tower[i] + overlaps_df_unused_indices = overlaps_df.index.tolist() + + o_spatial_index = STRtree(overlaps_df["geometry"]) + o_index_by_iloc = dict((i, list(overlaps_df.index)[i]) for i in range(len(overlaps_df))) + + for g_ind in geometries_disconnected_df.index: + possible_overlap_integer_indices = [*set(numpy.ndarray.flatten(o_spatial_index.query(geometries_disconnected_df["geometry"][g_ind])))] + possible_overlap_indices_0 = [o_index_by_iloc[k] for k in possible_overlap_integer_indices] + possible_overlap_indices = list(set(possible_overlap_indices_0) & set(overlaps_df_unused_indices)) + + geom_finished = False + + for o_ind in possible_overlap_indices: + # If the corresponding overlap intersects this geometry (and was + # contained in it originally!), grab it. + if (geom_finished is False) and (g_ind in list(overlaps_df["polygon indices"][o_ind])) and (not geometries_disconnected_df["geometry"][g_ind].intersection(overlaps_df["geometry"][o_ind]).is_empty): + + if (geometries_disconnected_df["geometry"][g_ind].intersection(overlaps_df["geometry"][o_ind])).length > 0: + geometries_disconnected_df["geometry"][g_ind] = unary_union([ + geometries_disconnected_df["geometry"][g_ind], overlaps_df["geometry"][o_ind] + ]) + overlaps_df_unused_indices.remove(o_ind) + if num_components(geometries_disconnected_df["geometry"][g_ind]) == geometries_df["num components orig"][g_ind]: + geom_finished = True + + geometries_df["geometry"][g_ind] = geometries_disconnected_df["geometry"][g_ind] + + if geom_finished: + geometries_disconnected_df = geometries_disconnected_df.drop(g_ind) + + # That's all we can do for the disconnected geometries at this level. + # Go on to filling in the rest of the overlaps by greatest perimeter. + g_spatial_index = STRtree(geometries_df["geometry"]) + g_index_by_iloc = dict((i, list(geometries_df.index)[i]) for i in range(len(geometries_df))) + + if nested is False: + print("Assigning order", i+1, "pieces...") + for o_ind in overlaps_df_unused_indices: + this_overlap = overlaps_df["geometry"][o_ind] + shared_perimeters = [] + possible_geom_integer_indices = [*set(numpy.ndarray.flatten(g_spatial_index.query(overlaps_df["geometry"][o_ind])))] + possible_geom_indices = [g_index_by_iloc[k] for k in possible_geom_integer_indices] + + for g_ind in possible_geom_indices: + if (g_ind in list(overlaps_df["polygon indices"][o_ind])) and not (this_overlap.boundary).intersection(geometries_df["geometry"][g_ind].boundary).is_empty: + shared_perimeters.append((g_ind, (this_overlap.boundary).intersection(geometries_df["geometry"][g_ind].boundary).length)) + + if len(shared_perimeters) > 0: + max_shared_perim = sorted(shared_perimeters, key=lambda tup: tup[1])[-1] + poly_to_add_to = max_shared_perim[0] + geometries_df["geometry"][poly_to_add_to] = unary_union( + [geometries_df["geometry"][poly_to_add_to], this_overlap]) + else: + # It seems like this should never happen, but it still seems to on + # very rare occasions. + if nested is False: + print("Couldn't find a polygon to glue a component in the intersection of geometries", overlaps_df["polygon indices"][o_ind], "to") + + reconstructed_df = geometries_df + del reconstructed_df["num components orig"] + del reconstructed_df["num components refined"] + + return reconstructed_df + + +def drop_bad_holes(reconstructed_df, holes_df, fill_gaps_threshold): + """ Identify holes that won't be filled and drop them from holes_df """ + + holes_df = holes_df.copy() + + if fill_gaps_threshold is not None: + spatial_index = STRtree(reconstructed_df.geometry) + index_by_iloc = dict((i, list(reconstructed_df.index)[i]) for i in range(len(reconstructed_df.index))) + hole_indices_to_drop = [] + for h_ind in holes_df.index: + this_hole = holes_df["geometry"][h_ind] + if shapely.get_num_interior_rings(holes_df["geometry"][h_ind]) > 0: + hole_indices_to_drop.append(h_ind) + else: + possible_intersect_integer_indices = [*set(numpy.ndarray.flatten(spatial_index.query(this_hole)))] + possible_intersect_indices = [(index_by_iloc[k]) for k in possible_intersect_integer_indices] + actual_intersect_indices = [g_ind for g_ind in possible_intersect_indices if not this_hole.intersection(reconstructed_df["geometry"][g_ind]).is_empty] + if len(actual_intersect_indices) > 0: + max_geom_area = max(reconstructed_df["geometry"][g_ind].area for g_ind in actual_intersect_indices) + hole_area_ratio = this_hole.area/max_geom_area + if hole_area_ratio > fill_gaps_threshold: + hole_indices_to_drop.append(h_ind) + + else: + hole_indices_to_drop = [] + for h_ind in holes_df.index: + if shapely.get_num_interior_rings(holes_df["geometry"][h_ind]) > 0: + hole_indices_to_drop.append(h_ind) + + if len(hole_indices_to_drop) > 0: + holes_df = holes_df.drop(hole_indices_to_drop).reset_index(drop=True) + + return holes_df, len(hole_indices_to_drop) + + +def smart_close_gaps(geometries_df, holes_df): + """ + Fill simply connected gaps; general procedure is roughly as follows: + (1) Fill in gaps that only intersect one non-exterior geometry in the + obvious way. + (2) For remaining gaps, partially fill by "convexifying" boundaries with each + non-exterior geometry. This will have the effect of completely filling + gaps that only intersect 2 geometries and no exterior boundaries. + (3) For any gap that intersects 4 or more geometries nontrivially (including + exterior boundaries), find the non-adjacent pair with the shortest distance + between them and try to connect the pair by adding a "triangle" to each of the + non-exterior geometries in the pair. (Keep trying until this succeeds for + some pair.) This reduces the gap to 1 or 2 smaller gaps, each intersecting + strictly fewer geometries than the original. Put the smaller gaps back in the + queue for the next round. + (4) For any gap that intersects exactly 3 geometries (including exterior boundaries) + nontrivially, fill by a process that gives a portion of the gap to each of + the non-exterior geometries that it intersects. + """ + geometries_df = geometries_df.copy() + holes_df = holes_df.copy() + + # First step is to simplify gaps by convexifying the geometry boundaries: + geometries_df, holes_df = convexify_hole_boundaries(geometries_df, holes_df) + + # Now proceed with filling simplified gaps. + if len(holes_df) > 0: + holes_to_process = deque(list(holes_df["geometry"])) + this_region = list(holes_df["region"])[0] # All holes in this dataframe should be from the same region + if this_region is None: + pbar = tqdm(desc="Gaps to fill", total=len(holes_to_process)) + else: + pbar = tqdm(desc=f"Gaps to fill in region {this_region}", total=len(holes_to_process)) + else: + holes_to_process = deque([]) + pbar = tqdm(desc="Gaps to fill", total=len(holes_to_process)) + + while len(holes_to_process) > 0: + pbar_increment = 1 + this_hole = holes_to_process.popleft() + this_hole_df = GeoDataFrame(geometry=GeoSeries([this_hole]), crs=holes_df.crs) + this_hole_boundaries_df = construct_hole_boundaries(geometries_df, this_hole_df) + + # Break into cases depending on how many target geometries intersect this gap + # and how many line segments the gap boundary consists of. + # After convexification, all gaps must have at least 3 boundaries (possibly + # including an exterior boundary). + + if len(set(this_hole_boundaries_df["target"]).difference({-1})) == 1: + # Attach the gap to the unique non-exterior geometry that it intersects: + poly_to_add_to = list(set(this_hole_boundaries_df["target"]).difference({-1}))[0] + geometries_df["geometry"][poly_to_add_to] = unary_union([geometries_df["geometry"][poly_to_add_to], this_hole]) + + elif len(segments(this_hole.boundary)) == 3: # If the hole is a simple triangle + if len(set(this_hole_boundaries_df["target"]).difference({-1})) == 3: + # Find the incenter of the triangle and use it to divide the triangle into + # 3 smaller triangles. (The incenter is more natural for this purpose than + # the centroid, especially for long skinny triangles.) + this_hole_incenter = incenter(this_hole) + for thb_ind in this_hole_boundaries_df.index: + g_ind = this_hole_boundaries_df["target"][thb_ind] + this_segment = this_hole_boundaries_df["geometry"][thb_ind] + this_segment_poly_to_add = make_valid(Polygon([this_segment.boundary.geoms[0], this_segment.boundary.geoms[1], this_hole_incenter])) + geometries_df["geometry"][g_ind] = unary_union([geometries_df["geometry"][g_ind], this_segment_poly_to_add]) + + else: + # There are either 2 sides intersecting a common geometry or 1 + # side intersecting an exterior boundary. In this case join the entire + # triangle to the geometry that it shares the largest perimeter with. + touching_geoms = list(set(this_hole_boundaries_df["target"]).difference({-1})) + perim_1 = this_hole.intersection(geometries_df["geometry"][touching_geoms[0]]).length + perim_2 = this_hole.intersection(geometries_df["geometry"][touching_geoms[1]]).length + if perim_1 > perim_2: + poly_to_add_to = touching_geoms[0] + else: + poly_to_add_to = touching_geoms[1] + geometries_df["geometry"][poly_to_add_to] = unary_union([geometries_df["geometry"][poly_to_add_to], this_hole]) + + else: + this_hole_df = GeoDataFrame(geometry=GeoSeries([this_hole]), crs=holes_df.crs) + this_hole_boundaries_df = construct_hole_boundaries(geometries_df, this_hole_df) + + # If this_hole falls into one of the simple cases above, put it back + # in the queue. (Note that after convexification, + # this_hole_boundaries_df can only have length 2 if one of the + # boundaries is exterior and didn't get convexified.) + if len(this_hole_boundaries_df) == 3: + # Put the gap boundaries and target geometries into oriented order: + this_hole_boundaries = [this_hole_boundaries_df["geometry"][0]] + target_geometries = [this_hole_boundaries_df["target"][0]] + + if this_hole_boundaries_df["geometry"][1].coords[0] == this_hole_boundaries_df["geometry"][0].coords[-1]: + this_hole_boundaries.append(this_hole_boundaries_df["geometry"][1]) + target_geometries.append(this_hole_boundaries_df["target"][1]) + this_hole_boundaries.append(this_hole_boundaries_df["geometry"][2]) + target_geometries.append(this_hole_boundaries_df["target"][2]) + elif this_hole_boundaries_df["geometry"][2].coords[0] == this_hole_boundaries_df["geometry"][0].coords[-1]: + this_hole_boundaries.append(this_hole_boundaries_df["geometry"][2]) + target_geometries.append(this_hole_boundaries_df["target"][2]) + this_hole_boundaries.append(this_hole_boundaries_df["geometry"][1]) + target_geometries.append(this_hole_boundaries_df["target"][1]) + + # If one of the boundaries is an exterior region boundary, find + # the shortest path between the vertex that isn't one of its + # endpoints and the nearest point in this boundary, and divide + # the hole between the other two adjacent geometries along this path. + # Otherwise, for each of the three boundary endpoints, construct + # the angle bisector of the two adjacent line segments and extend + # this line beyond the extent of the hole. Intersections of these + # 3 line segments will determine the endpoints of the new boundaries. + + if -1 in target_geometries: + ext_boundary_position = target_geometries.index(-1) + # Cyclically permute so that the exterior boundary is in the + # 1st position: + this_hole_boundaries = this_hole_boundaries[ext_boundary_position:] + this_hole_boundaries[0:ext_boundary_position] + target_geometries = target_geometries[ext_boundary_position:] + target_geometries[0:ext_boundary_position] + + main_vertex = Point(this_hole_boundaries[2].coords[0]) + nearest_ext_boundary_point = nearest_points(main_vertex, extract_unique_points(this_hole_boundaries[0]))[1] + + ext_boundary_points = list(extract_unique_points(this_hole_boundaries[0]).geoms) + nearest_point_position = ext_boundary_points.index(nearest_ext_boundary_point) + + if nearest_point_position == 0: + # Add the entire hole to target_geometries[1]. + geometries_df["geometry"][target_geometries[1]] = unary_union([geometries_df["geometry"][target_geometries[1]], this_hole]) + + elif nearest_point_position == len(ext_boundary_points) - 1: + # Add the entire hole to target_geometries[2]. + geometries_df["geometry"][target_geometries[2]] = unary_union([geometries_df["geometry"][target_geometries[2]], this_hole]) + + else: + this_hole_triangulation = triangulate_polygon(this_hole) + sp = LineString(shortest_path_in_polygon(this_hole, main_vertex, nearest_ext_boundary_point, full_triangulation=this_hole_triangulation)) + + poly1_to_add_boundary = unary_union([this_hole_boundaries[1], sp, LineString(ext_boundary_points[nearest_point_position:])]) + poly1_to_add = polygonize(poly1_to_add_boundary)[0] + geometries_df["geometry"][target_geometries[1]] = unary_union([geometries_df["geometry"][target_geometries[1]], poly1_to_add]) + + poly2_to_add_boundary = unary_union([this_hole_boundaries[2], sp, LineString(ext_boundary_points[0:nearest_point_position+1])]) + poly2_to_add = polygonize(poly2_to_add_boundary)[0] + geometries_df["geometry"][target_geometries[2]] = unary_union([geometries_df["geometry"][target_geometries[2]], poly2_to_add]) + + else: + max_line_length = this_hole.boundary.length/2 + vertices = [] + bisectors = [] + + for i in range(3): + this_vertex = numpy.array(this_hole_boundaries[i].coords[0]) + vertices.append(Point(this_hole_boundaries[i].coords[0])) + this_vec_1_raw = numpy.array(this_hole_boundaries[i].coords[1]) - this_vertex + this_vec_2_raw = numpy.array(this_hole_boundaries[i-1].coords[-2]) - this_vertex + this_unit_vec_1 = this_vec_1_raw/math.sqrt(this_vec_1_raw[0]**2 + this_vec_1_raw[1]**2) + this_unit_vec_2 = this_vec_2_raw/math.sqrt(this_vec_2_raw[0]**2 + this_vec_2_raw[1]**2) + this_bisector_vec_raw = this_unit_vec_1 + this_unit_vec_2 + this_bisector_unit_vec = this_bisector_vec_raw/math.sqrt(this_bisector_vec_raw[0]**2 + this_bisector_vec_raw[1]**2) + this_bisector = LineString([tuple(this_vertex), tuple(this_vertex + max_line_length*this_bisector_unit_vec)]) + bisectors.append(this_bisector) + + # Points of intersection of the bisectors: + i_points = [bisectors[0].intersection(bisectors[1]), bisectors[1].intersection(bisectors[2]), bisectors[2].intersection(bisectors[0])] + + # Note that these points could coincide - e.g., if the convexified + # hole is a triangle - and the rest of the construction would be very + # simple. + # Also - even though this is geometrically impossible(!), + # rounding errors can create a situation in which two + # of these points are equal but different from the 3rd. + # In this case, assume that the one that appears twice + # is actually the common value for all three. + + if i_points[0] == i_points[1] or i_points[0] == i_points[2]: + # Construct pieces to append to geometries and append them. + middle_point = i_points[0] + for i in range(3): + poly_to_add_boundary = unary_union([this_hole_boundaries[i], LineString([this_hole_boundaries[i].coords[-1], middle_point, this_hole_boundaries[i].coords[0]])]) + poly_to_add = polygonize(poly_to_add_boundary)[0] + geometries_df["geometry"][target_geometries[i]] = unary_union([geometries_df["geometry"][target_geometries[i]], poly_to_add]) + + elif i_points[1] == i_points[2]: + # Construct pieces to append to geometries and append them. + middle_point = i_points[1] + for i in range(3): + poly_to_add_boundary = unary_union([this_hole_boundaries[i], LineString([this_hole_boundaries[i].coords[-1], middle_point, this_hole_boundaries[i].coords[0]])]) + poly_to_add = polygonize(poly_to_add_boundary)[0] + geometries_df["geometry"][target_geometries[i]] = unary_union([geometries_df["geometry"][target_geometries[i]], poly_to_add]) + + else: + # In general, each bisector intersects the other two + # bisectors in distinct points. To accurately construct + # the path to the more distant one, we need to include + # the nearer one as an intermediate point. + # And we might as well go ahead and find the incenter of the + # triangle formed by the intersection points, and include it + # on the path to the more distant one so we can completely + # fill the hole without a separate step. + middle_point = incenter(Polygon(i_points)) + + # The first bisector contains the 1st and 3rd intersection points. + if vertices[0].distance(i_points[0]) > vertices[0].distance(i_points[2]): + v0_to_i01_path = LineString([vertices[0], i_points[2], middle_point, i_points[0]]) + v0_to_i02_path = LineString([vertices[0], i_points[2]]) + else: + v0_to_i01_path = LineString([vertices[0], i_points[0]]) + v0_to_i02_path = LineString([vertices[0], i_points[0], middle_point, i_points[2]]) + + # The second bisector contains the 1st and 2nd intersection points. + if vertices[1].distance(i_points[0]) > vertices[1].distance(i_points[1]): + v1_to_i01_path = LineString([vertices[1], i_points[1], middle_point, i_points[0]]) + v1_to_i12_path = LineString([vertices[1], i_points[1]]) + else: + v1_to_i01_path = LineString([vertices[1], i_points[0]]) + v1_to_i12_path = LineString([vertices[1], i_points[0], middle_point, i_points[1]]) + + # The third bisector contains the 2nd and 3rd intersection points. + if vertices[2].distance(i_points[1]) > vertices[2].distance(i_points[2]): + v2_to_i12_path = LineString([vertices[2], i_points[2], middle_point, i_points[1]]) + v2_to_i02_path = LineString([vertices[2], i_points[2]]) + else: + v2_to_i12_path = LineString([vertices[2], i_points[1]]) + v2_to_i02_path = LineString([vertices[2], i_points[1], middle_point, i_points[2]]) + + # Construct and adjoin new polygon pieces one at a time. + poly0_to_add_boundary = unary_union([this_hole_boundaries[0], v0_to_i01_path, v1_to_i01_path]) + poly0_to_add = polygonize(poly0_to_add_boundary)[0] + geometries_df["geometry"][target_geometries[0]] = unary_union([geometries_df["geometry"][target_geometries[0]], poly0_to_add]) + + poly1_to_add_boundary = unary_union([this_hole_boundaries[1], v1_to_i12_path, v2_to_i12_path]) + poly1_to_add = polygonize(poly1_to_add_boundary)[0] + geometries_df["geometry"][target_geometries[1]] = unary_union([geometries_df["geometry"][target_geometries[1]], poly1_to_add]) + + poly2_to_add_boundary = unary_union([this_hole_boundaries[2], v2_to_i02_path, v0_to_i02_path]) + poly2_to_add = polygonize(poly2_to_add_boundary)[0] + geometries_df["geometry"][target_geometries[2]] = unary_union([geometries_df["geometry"][target_geometries[2]], poly2_to_add]) + + else: # If len(this_hole_boundaries_df) >= 4 + this_hole_triangulation = triangulate_polygon(this_hole) + thb_distances = [] + + for i in this_hole_boundaries_df.index: + for j in this_hole_boundaries_df.index: + if j > i: + this_distance = this_hole_boundaries_df["geometry"][i].distance(this_hole_boundaries_df["geometry"][j]) + if this_distance != 0: + thb_distances.append((i, j, this_distance)) + + thb_distance_data_sorted = deque(sorted(thb_distances, key=lambda tup: tup[2])) + + found_triangles = False + while found_triangles is False and len(thb_distance_data_sorted) > 0: + boundary_distance_data = thb_distance_data_sorted.popleft() + boundaries_to_connect = (boundary_distance_data[0], boundary_distance_data[1]) + + nhb1 = this_hole_boundaries_df["geometry"][boundaries_to_connect[0]] + nhb2 = this_hole_boundaries_df["geometry"][boundaries_to_connect[1]] + geom1 = this_hole_boundaries_df["target"][boundaries_to_connect[0]] + geom2 = this_hole_boundaries_df["target"][boundaries_to_connect[1]] + + # Construct the shortest paths between + # (1) initial points of both boundaries; + # (2) terminal points of both boundaries. + # These paths will intersect, possibly at a vertex or along entire + # hole boundary segments, but generically---and provably for at + # at leat one non-adjacent pair---at a single interior point of + # the hole. + # In the generic case, these paths together with the two + # hole boundaries will form a pair of "triangles" that each share a + # boundary of positive length with one of the two hole boundaries. + # Find the closest pair that satisfy this generic intersection + # condition and adjoin each of the triangles formed in this way + # to its adjacent geometry. This will create + # two smaller holes - which already have convexified geometry + # boundaries by construction! - and we put these back on the queue + # for the next round. + + if not (geom1 == -1 and geom2 == -1): + # If one of the boundaries is exterior (and in the rare case that BOTH + # boundaries are exterior, skip this pair and go on to the next one): + # Find the closest point on the exterior boundary to the non-exterior + # geometry, construct a "triangle" by taking the shortest paths from + # the endpoints of the non-exterior geometry to this point, and + # (assuming it has positive area) adjoining it to the non-exterior + # geometry. + if geom1 == -1 or geom2 == -1: + if geom1 == -1: + nhb_ext = nhb1 + nhb_int = nhb2 + geom_int = geom2 + elif geom2 == -1: + nhb_ext = nhb2 + nhb_int = nhb1 + geom_int = geom1 + point1 = nhb_int.boundary.geoms[0] + point2 = nhb_int.boundary.geoms[1] + nearest_ext_boundary_point = nearest_points(nhb_int, extract_unique_points(nhb_ext))[1] + path1 = LineString(shortest_path_in_polygon(this_hole, point1, nearest_ext_boundary_point, full_triangulation=this_hole_triangulation)) + path2 = LineString(shortest_path_in_polygon(this_hole, point2, nearest_ext_boundary_point, full_triangulation=this_hole_triangulation)) + polys_to_add_boundary = shapely.node(MultiLineString([nhb_int, path1, path2])) + polys_to_add = polygonize(polys_to_add_boundary) + if len(polys_to_add) > 0: + for poly_to_add in polys_to_add: + if poly_to_add.area > 0: + found_triangles = True + geometries_df["geometry"][geom_int] = unary_union([geometries_df["geometry"][geom_int], poly_to_add]) + this_hole = this_hole.difference(poly_to_add) + + else: + # Start by constructing the shortest paths between the initial point + # of each boundary and the terminal point of the other. If these + # paths are disjoint, then this pair of boundaries is strongly + # mutually visible and we want to connect them. Otherwise, + # skip this pair and move on to the next one. + point11 = nhb1.boundary.geoms[0] + point12 = nhb1.boundary.geoms[1] + point21 = nhb2.boundary.geoms[0] + point22 = nhb2.boundary.geoms[1] + + test_path1_vertices = shortest_path_in_polygon(this_hole, point11, point22, full_triangulation=this_hole_triangulation) + test_path2_vertices = shortest_path_in_polygon(this_hole, point12, point21, full_triangulation=this_hole_triangulation) + if len(set(test_path1_vertices).intersection(set(test_path2_vertices))) == 0: + # In this case we should be good to add triangles formed + # by crossing paths between the initial and terminal + # points between the two boundaries! + # A minor exception is when both boundaries target the same + # geometry, in which case we want the paths to NOT cross in + # order to preserve convexity of the geometry boundaries in the + # new holes, and we'll add a single polygon instead of a + # pair of triangles. + + found_triangles = True + + if geom1 == geom2: + path1 = LineString(shortest_path_in_polygon(this_hole, point11, point22, full_triangulation=this_hole_triangulation)) + path2 = LineString(shortest_path_in_polygon(this_hole, point12, point21, full_triangulation=this_hole_triangulation)) + else: + path1 = LineString(shortest_path_in_polygon(this_hole, point11, point21, full_triangulation=this_hole_triangulation)) + path2 = LineString(shortest_path_in_polygon(this_hole, point12, point22, full_triangulation=this_hole_triangulation)) + + polys_to_add_boundary = shapely.node(MultiLineString([nhb1, nhb2, path1, path2])) + polys_to_add = polygonize(polys_to_add_boundary) + # polys_to_add will consist of either 1 or 2 polygons, + # each sharing a positive-length boundary witha unique geometry. + # Add each polygon to the geometry that it shares a boundary with. + nhb1_segments = segments(nhb1) + nhb2_segments = segments(nhb2) + for poly_to_add in polys_to_add: + poly_to_add = orient(poly_to_add) + # Cover all bases with both possible orientations for + # boundary segments, even though the proper orientation + # SHOULD always be correct. + poly_segments_oriented = segments(poly_to_add.boundary) + poly_segments_reverse = [shapely.reverse(segment) for segment in poly_segments_oriented] + poly_segments_all = set(poly_segments_oriented + poly_segments_reverse) + if (len(set(nhb1_segments).intersection(poly_segments_all)) > 0) and (len(set(nhb2_segments).intersection(poly_segments_all)) == 0): + geometries_df["geometry"][geom1] = unary_union([geometries_df["geometry"][geom1], poly_to_add]) + this_hole = this_hole.difference(poly_to_add) + + elif (len(set(nhb1_segments).intersection(poly_segments_all)) == 0) and (len(set(nhb2_segments).intersection(poly_segments_all)) > 0): + geometries_df["geometry"][geom2] = unary_union([geometries_df["geometry"][geom2], poly_to_add]) + this_hole = this_hole.difference(poly_to_add) + + elif geom1 == geom2: + geometries_df["geometry"][geom1] = unary_union([geometries_df["geometry"][geom1], poly_to_add]) + this_hole = this_hole.difference(poly_to_add) + + else: + print("Internal triangle construction went weird!") + print("Hole boundaries:") + for i in this_hole_boundaries_df.index: + print("Target:", this_hole_boundaries_df["target"][i]) + print(list(this_hole_boundaries_df["geometry"][i].coords)) + print("poly_to_add boundaries:") + print(list(poly_to_add.boundary.coords)) + + # Now put the new hole(s) created by removing triangles back in the queue: + if found_triangles and not this_hole.is_empty: + if this_hole.geom_type == "MultiPolygon": # 2 holes to add + holes_to_add = [orient(geom) for geom in this_hole.geoms] + elif this_hole.geom_type == "Polygon": # 1 hole to add + holes_to_add = [orient(this_hole)] + holes_to_process.extend(holes_to_add) + pbar_increment -= len(holes_to_add) + + elif found_triangles is False: + # This is rare, but it does happen occasionally in the scenario where + # there's a large external boundary that, if it weren't external, + # would grab most (or maybe even all) of the hole in the + # convexification process. + # In this case, just assign the entire hole to the geometry with which + # it shares the largest perimeter. (This is fairly close to what + # tends to happen with large, non-convexified external boundaries + # anyway!) + shared_perimeters = [] + for i in this_hole_boundaries_df.index: + if this_hole_boundaries_df["target"][i] != -1: + shared_perimeters.append((this_hole_boundaries_df["target"][i], this_hole_boundaries_df["geometry"][i].length)) + if len(shared_perimeters) > 0: + max_shared_perim = sorted(shared_perimeters, key=lambda tup: tup[1])[-1] + poly_to_add_to = max_shared_perim[0] + geometries_df["geometry"][poly_to_add_to] = unary_union( + [geometries_df["geometry"][poly_to_add_to], this_hole]) + + pbar.update(pbar_increment) + + pbar.close() + + return geometries_df + + +def small_rook_to_queen(geometries_df, min_rook_length): + """ + Convert all rook adjacencies between geometries with total adjacency length less + than min_rook_length to queen adjacencies. + """ + + geometries_df = geometries_df.copy() + + # The input should be clean, so these should all be 1-D or less: + adj_df = adjacencies(geometries_df, output_type="geodataframe") + adj_df.crs = geometries_df.crs + + # Identify the adjacencies whose TOTAL length is less than the threshold; + # since this is all about adjacency relations, there's no need to fix + # a small component when there's also a large component that will create + # an adjacency regardless. + # Add column for boundary length and select the small ones: + adj_df["boundary length"] = adj_df["geometry"].length + small_adj_df = adj_df[adj_df["boundary length"] < min_rook_length] + + # Get rid of point geometries, linemerge the MultiLineStrings, and then + # explode into components. (Then get rid of points again.) + for ind in small_adj_df.index: + if small_adj_df["geometry"][ind].geom_type == "GeometryCollection": + small_adj_list = list(small_adj_df["geometry"][ind].geoms) + small_adj_list_no_point = [x for x in small_adj_list if x.geom_type != "Point"] + small_adj_df["geometry"][ind] = MultiLineString(small_adj_list_no_point) + + if small_adj_df["geometry"][ind].geom_type == "MultiLineString": + small_adj_df["geometry"][ind] = linemerge(small_adj_df["geometry"][ind]) + + small_adj_df = small_adj_df.explode(index_parts=False).reset_index(drop=True) + + small_adj_df_indices_to_drop = [] + for ind in small_adj_df.index: + if small_adj_df["geometry"][ind].geom_type == "Point": + small_adj_df_indices_to_drop.append(ind) + + if len(small_adj_df_indices_to_drop) > 0: + small_adj_df = small_adj_df.drop(small_adj_df_indices_to_drop) + + # Next, construct small disks around each adjacency, and add them to a list. + # We'll take their unary union later in case any of them overlap. + disks_to_remove_list = [] + for a_ind in small_adj_df.index: + this_adj = small_adj_df["geometry"][a_ind] + adj_diam = this_adj.length + fat_point_radius = 0.6*adj_diam # slightly more than the radius from the midpoint to the endpoints + endpoint1 = this_adj.coords[0] + endpoint2 = this_adj.coords[-1] + midpoint = LineString([endpoint1, endpoint2]).centroid + disk_to_remove = midpoint.buffer(fat_point_radius) + disks_to_remove_list.append(disk_to_remove) + + if len(disks_to_remove_list) > 0: + # Make a list of the convex hulls of all the components of the unary union, + # and make sure none of THOSE intersect. (Note that is is only necessary if there + # is more than 1 disk to remove.) + polys_to_remove_list = disks_to_remove_list + polys_to_remove_complete = False + while polys_to_remove_complete is False: + all_polys_to_remove = unary_union(polys_to_remove_list) + if all_polys_to_remove.geom_type == "Polygon": # if it's all one big polygon now + merged_polys_to_remove_list = [all_polys_to_remove] + else: + merged_polys_to_remove_list = list(all_polys_to_remove.geoms) + + convex_polys_to_remove_list = [shapely.convex_hull(x) for x in merged_polys_to_remove_list] + + if len(convex_polys_to_remove_list) == 1: + polys_to_remove_complete = True + elif unary_union(convex_polys_to_remove_list).geom_type == "MultiPolygon": + # Note that if the unary union is a Polygon, then this next condition + # below can't hold anyway and we want polys_to_remove_complete to remain + # False. + if len(unary_union(convex_polys_to_remove_list).geoms) == len(convex_polys_to_remove_list): + polys_to_remove_complete = True + + polys_to_remove_list = convex_polys_to_remove_list + + # Build an STRtree to use for finding intersecting geometries. + g_spatial_index = STRtree(geometries_df["geometry"]) + g_index_by_iloc = dict((i, list(geometries_df.index)[i]) for i in range(len(geometries_df))) + + for a_ind in range(len(polys_to_remove_list)): + poly_to_remove = polys_to_remove_list[a_ind] + + # Identify geometries that might intersect this polygon. + possible_geom_integer_indices = [*set(numpy.ndarray.flatten(g_spatial_index.query(poly_to_remove)))] + possible_geom_indices = [g_index_by_iloc[k] for k in possible_geom_integer_indices] + + # Use the boundaries of these geometries together with the boundary of the disk to + # polygonize and divide geometries into pieces inside and outside the disk. + boundaries = [geometries_df["geometry"][i].boundary for i in possible_geom_indices] + boundaries.append(LineString(list(poly_to_remove.exterior.coords))) + + boundaries_exploded = [] + for geom in boundaries: + if geom.geom_type == "LineString": + boundaries_exploded.append(geom) + elif geom.geom_type == "MultiLineString": + boundaries_exploded += list(geom.geoms) + boundaries_union = shapely.node(MultiLineString(boundaries_exploded)) + + pieces_df = GeoDataFrame(columns=["polygon indices"], + geometry=GeoSeries(list(polygonize(boundaries_union))), + crs=geometries_df.crs) + + # Associate the pieces to the main geometries. (Note that if there are + # gaps, some pieces may be unassigned.) + for i in pieces_df.index: + pieces_df["polygon indices"][i] = set() + + for i in pieces_df.index: + temp_possible_geom_integer_indices = [*set(numpy.ndarray.flatten(g_spatial_index.query(pieces_df["geometry"][i])))] + temp_possible_geom_indices = [g_index_by_iloc[k] for k in temp_possible_geom_integer_indices] + + for j in temp_possible_geom_indices: + if pieces_df["geometry"][i].representative_point().intersects(geometries_df["geometry"][j]): + pieces_df["polygon indices"][i] = pieces_df["polygon indices"][i].union({j}) + + # Now rebuild the disk from the pieces that are inside the circle, and drop them from + # pieces_df. Then we'll give the pieces outside the circle back to the geometries that they came from. + + poly_to_remove_refined = Polygon() + + pieces_df_indices_to_drop = [] + for p_ind in pieces_df.index: + if pieces_df["geometry"][p_ind].representative_point().intersects(poly_to_remove): + poly_to_remove_refined = unary_union([poly_to_remove_refined, pieces_df["geometry"][p_ind]]) + pieces_df_indices_to_drop.append(p_ind) + if len(pieces_df_indices_to_drop) > 0: + pieces_df = pieces_df.drop(pieces_df_indices_to_drop) + + for g_ind in possible_geom_indices: + geometries_df["geometry"][g_ind] = Polygon() + + for p_ind in pieces_df.index: + if len(pieces_df["polygon indices"][p_ind]) == 1: # Note that it won't be >1 if the file is clean! + this_poly_ind = list(pieces_df["polygon indices"][p_ind])[0] + this_piece = pieces_df["geometry"][p_ind] + if this_poly_ind in possible_geom_indices: + # This check is needed because the geometries in possible_geom_incides can form a + # non-simply-connected region, in which case the interior holes - which may consist + # of multiple geometries each - may be assigned someplace they shouldn't be! + geometries_df["geometry"][this_poly_ind] = unary_union([geometries_df["geometry"][this_poly_ind], this_piece]) + + # Find the boundary arcs between geometries and poly_to_remove_refined (and make sure each arc is a connected piece): + possible_geoms = geometries_df.loc[possible_geom_indices] + poly_to_remove_boundaries_df = intersections(GeoDataFrame(geometry=GeoSeries([poly_to_remove_refined], crs=geometries_df.crs)), possible_geoms, output_type="geodataframe") + + for b_ind in poly_to_remove_boundaries_df.index: + if poly_to_remove_boundaries_df["geometry"][b_ind].geom_type == "MultiLineString": + poly_to_remove_boundaries_df["geometry"][b_ind] = linemerge(poly_to_remove_boundaries_df["geometry"][b_ind]) + + poly_to_remove_boundaries_df = poly_to_remove_boundaries_df.explode(index_parts=False).reset_index(drop=True) + poly_to_remove_centroid_coords = poly_to_remove_refined.centroid.coords[0] + + # For each boundary arc, create a "pie wedge" from the center of poly_to_remove_refined + # subtending this arc. (Since the polygon is convex, these are guaranteed to piece + # together nicely.) + for b_ind in poly_to_remove_boundaries_df.index: + boundary_arc_coords = list(poly_to_remove_boundaries_df["geometry"][b_ind].coords) + boundary_wedge_coords = boundary_arc_coords + [poly_to_remove_centroid_coords] + + g_ind = poly_to_remove_boundaries_df["target"][b_ind] + + geometries_df["geometry"][g_ind] = unary_union([geometries_df["geometry"][g_ind], Polygon(boundary_wedge_coords)]) + + return geometries_df + + +def construct_hole_boundaries(geometries_df, holes_df): + """ + Construct a GeoDataFrame with all positive-length intersections between hole + and geometry boundaries, including intersections between hole boundaries and + exterior boundaries, if applicable. + """ + geometries_df = geometries_df.copy() + holes_df = holes_df.copy() + + # Be sure gaps are correctly oriented: + for h_ind in holes_df.index: + holes_df.geometry[h_ind] = orient(holes_df.geometry[h_ind]) + + # Do this WITHOUT using geometric intersection operations, which seem to be prone to + # inexplicable rounding errors (GEOS bugs?) + # Start by constructing an STRtree to find geometries that may intersect gaps. + + g_spatial_index = STRtree(geometries_df["geometry"]) + g_index_by_iloc = dict((i, list(geometries_df.index)[i]) for i in range(len(geometries_df))) + + # Initialize the geodataframe for the gap boundaries + hole_boundaries_df = GeoDataFrame(columns=["source", "target"], geometry=GeoSeries([]), crs=geometries_df.crs) + + # For each gap and each geometry that it might possibly intersect, find all + # common LineStrings in their boundaries (if any) and take their unary union to + # construct the appropriate boundary between them. (Note that this requires paying + # VERY careful attention to orientations!) + for h_ind in holes_df.index: + this_hole = holes_df["geometry"][h_ind] + this_hole_segments = segments(this_hole.boundary) + this_hole_segments_used = [] + + possible_geom_integer_indices = [*set(numpy.ndarray.flatten(g_spatial_index.query(holes_df["geometry"][h_ind])))] + possible_geom_indices = [g_index_by_iloc[k] for k in possible_geom_integer_indices] + + for g_ind in possible_geom_indices: + + this_geom = geometries_df["geometry"][g_ind] + if this_geom.geom_type == "Polygon": + this_geom_geoms = [orient(this_geom)] + elif this_geom.geom_type == "MultiPolygon": + this_geom_geoms = [orient(geom) for geom in this_geom.geoms] + + this_geom_boundary_components = [] + for geom in this_geom_geoms: + this_geom_boundary = geom.boundary + if this_geom_boundary.geom_type == "LineString": + this_geom_boundary_components += [this_geom_boundary] + elif this_geom_boundary.geom_type == "MultiLineString": + this_geom_boundary_components += list(this_geom_boundary.geoms) + + this_geom_segments = set() + for component in this_geom_boundary_components: + this_geom_segments = this_geom_segments.union(set(segments(component))) + + this_hole_this_geom_segments = [segment for segment in this_hole_segments if (segment in this_geom_segments or shapely.reverse(segment) in this_geom_segments)] + + if len(this_hole_this_geom_segments) > 0: + + this_hole_segments_used += this_hole_this_geom_segments + this_hole_boundary_df = GeoDataFrame(geometry=GeoSeries([linemerge(this_hole_this_geom_segments)]), crs=geometries_df.crs) + this_hole_boundary_df.insert(0, "source", h_ind) + this_hole_boundary_df.insert(1, "target", g_ind) + + hole_boundaries_df = pandas.concat([hole_boundaries_df, this_hole_boundary_df]).reset_index(drop=True) + + # Finally, check for any exterior boundary: + if len(this_hole_segments) > len(this_hole_segments_used): + exterior_segments = [segment for segment in this_hole_segments if segment not in this_hole_segments_used] + this_hole_exterior_boundary_df = GeoDataFrame(geometry=GeoSeries([linemerge(exterior_segments)]), crs=geometries_df.crs) + this_hole_exterior_boundary_df.insert(0, "source", h_ind) + this_hole_exterior_boundary_df.insert(1, "target", -1) + hole_boundaries_df = pandas.concat([hole_boundaries_df, this_hole_exterior_boundary_df]).reset_index(drop=True) + + hole_boundaries_df = hole_boundaries_df.explode(index_parts=False).reset_index(drop=True) + + return hole_boundaries_df + + +def incenter(triangle): + """ + Find the incenter (intersection point of the angle bisectors) of a triangle. + """ + triangle_vertices = triangle.boundary.coords + triangle_segments = segments(triangle.boundary) + + if len(triangle_segments) != 3: + raise TypeError("Input must be a triangle!") + + x_a = triangle_vertices[0][0] + y_a = triangle_vertices[0][1] + x_b = triangle_vertices[1][0] + y_b = triangle_vertices[1][1] + x_c = triangle_vertices[2][0] + y_c = triangle_vertices[2][1] + a = triangle_segments[1].length + b = triangle_segments[2].length + c = triangle_segments[0].length + + # The incenter will be a weighted average of the coordinates of the vertices, + # with coefficients proportional to a,b,c. + + alpha = a/(a + b + c) + beta = b/(a + b + c) + gamma = c/(a + b + c) + + x_i = alpha*x_a + beta*x_b + gamma*x_c + y_i = alpha*y_a + beta*y_b + gamma*y_c + + # Occasionally for very tiny triangles, rounding errors produce a point not + # contained in the triangle. In this case, replace the computed point with + # the nearest vertex of the triangle. + if not triangle.contains(Point(x_i, y_i)): + point_to_return = nearest_points(Point(x_i, y_i), MultiPoint([Point(x_a, y_a), Point(x_b, y_b), Point(x_c, y_c)]))[1] + else: + point_to_return = Point(x_i, y_i) + + return point_to_return + + +def triangulate_polygon(polygon): + """ + Triangulate a not-necessarily-convex simple polygon, based on the ear clipping + method. + """ + + triangles = [] + poly = polygon + + while len(segments(poly.boundary)) > 3: + poly_vertices = list(extract_unique_points(poly).geoms) + + # Find an ear to cut from the polygon and add it to the list of triangles. + for i in range(len(poly_vertices)): + triangle_to_check = Polygon([poly_vertices[i-1], poly_vertices[i], poly_vertices[i+1]]) + if poly.contains(triangle_to_check) and LineString([poly_vertices[i-1], poly_vertices[i+1]]).intersection(poly.boundary).difference(MultiPoint([poly_vertices[i-1], poly_vertices[i+1]])).is_empty: + triangles.append(triangle_to_check) + poly = poly.difference(triangle_to_check) + break + + # Remaining polygon is now a triangle, so add it to the list. + triangles.append(poly) + + return triangles + + +def shortest_path_in_polygon(polygon, start, end, full_triangulation=None): + """ + Finds the shortest path between any two vertices in a not-necessarily-convex + simple polygon. The polygon must be valid and simply connected, + and "start" and "end" must be vertices of the polygon. + + Optional input full_triangulation allows triangulation to be computed in + advance to avoid repetition when multiple paths need to be computed within + the same polygon. + """ + if not (polygon.is_valid and polygon.geom_type == "Polygon"): + raise TypeError("shortest_path_in_polygon: Input polygon must be a valid Polygon.") + if not extract_unique_points(polygon).contains(MultiPoint([start, end])): + raise TypeError("shortest_path_in_polygon: Start and end points must be vertices of the polygon.") + + # First check for the easy case: If the line segment between the start and end points is + # contained in the polygon, then that's the shortest path. (And the rest of the algorithm + # won't work correctly because the simplified polygon will degenerate.) + + if polygon.contains(LineString([start, end])) or polygon.boundary.contains(LineString([start, end])): + return [start, end] + + else: + # First make sure the polygon is oriented so that we're clear on what + # "left" and "right" mean. + polygon = orient(polygon) + + # Create two paths around polygon.boundary to the start and end points, oriented + # appropriately. + boundary_points = list(extract_unique_points(polygon.boundary).geoms) + start_index = boundary_points.index(start) + end_index = boundary_points.index(end) + if start_index < end_index: + path_1 = LineString(boundary_points[start_index:end_index+1]) + path_2 = LineString(boundary_points[end_index:] + boundary_points[0:start_index+1]) + else: + path_1 = LineString(boundary_points[start_index:] + boundary_points[0:end_index+1]) + path_2 = LineString(boundary_points[end_index:start_index+1]) + + if (extract_unique_points(path_1).geoms[0] == start) and (extract_unique_points(path_2).geoms[0] == end): + right_path = path_1 + left_path = shapely.reverse(path_2) + + elif (extract_unique_points(path_2).geoms[0] == start) and (extract_unique_points(path_1).geoms[0] == end): + right_path = path_2 + left_path = shapely.reverse(path_1) + + right_path_points = list(extract_unique_points(right_path).geoms) + left_path_points = list(extract_unique_points(left_path).geoms) + + # Now triangulate the polygon, but only keep the triangles that have one vertex + # in each of the right and left paths (not counting the starting and ending points) + # in order to create a "sleeve" for the shortest path. + if full_triangulation is None: + full_triangulation = triangulate_polygon(polygon) + + triangulation = [] + for triangle in full_triangulation: + if not triangle.boundary.intersection(MultiPoint(right_path_points[1:-1])).is_empty and not triangle.boundary.intersection(MultiPoint(left_path_points[1:-1])).is_empty: + triangulation.append(triangle) + + # Put the triangles for the sleeve in the correct order: + initial_triangle = [triangle for triangle in triangulation if start in extract_unique_points(triangle.boundary).geoms][0] + + ordered_triangulation = [initial_triangle] + triangulation.remove(initial_triangle) + + while len(triangulation) > 0: + leading_triangle = ordered_triangulation[-1] + next_triangle = [triangle for triangle in triangulation if leading_triangle.intersection(triangle).geom_type == "LineString"][0] + ordered_triangulation.append(next_triangle) + triangulation.remove(next_triangle) + + # Regard the sleeve given by the union of these triangles as the "simplified" + # polygon; the shortest path must be contained in this simplfied polygon. + polygon_simplified = unary_union(ordered_triangulation) + + # Now use the ordered triangulation to order the vertices of the simplified polygon, + # as well as the left and right paths restricted to the simplified polygon. + ordered_path_vertices = [start] + right_path_simplified_points = [start] + left_path_simplified_points = [start] + + for triangle in ordered_triangulation: + this_triangle_vertices = set(extract_unique_points(triangle.boundary).geoms) + this_triangle_new_vertices = this_triangle_vertices.difference(set(ordered_path_vertices)) + ordered_path_vertices = ordered_path_vertices + list(this_triangle_new_vertices) + for vertex in this_triangle_new_vertices: + if vertex in right_path_points: + right_path_simplified_points.append(vertex) + if vertex in left_path_points: + left_path_simplified_points.append(vertex) + + # Initialize the found_shortest_path and the left and right funnel edges: + found_shortest_path = [start] + + left_funnel = [left_path_simplified_points[0], left_path_simplified_points[1]] + right_funnel = [right_path_simplified_points[0], right_path_simplified_points[1]] + + # We've already used the first 3 points on this list, so take them out. + ordered_path_vertices = ordered_path_vertices[3:] + + # Now find the shortest path! + for point in ordered_path_vertices: + apex = found_shortest_path[-1] + + if point in left_path_simplified_points: + this_funnel = left_funnel + other_funnel = right_funnel + reflex_sign = 1 + elif point in right_path_simplified_points: + this_funnel = right_funnel + other_funnel = left_funnel + reflex_sign = -1 + + # Start by checking whether this point can see the apex, and if so, the + # apex becomes the new predecessor on this funnel. + # Otherwise, find the first point on this funnel that can see point, + # and check whether this is a *reflex* vertex. If so, cut off the rest + # of this funnel and add point to it. If not, find the first seen + # vertex on the *other* funnel; it's guaranteed to be reflex. Make it + # the new apex, and add the other funnel up to this point to + # found_shortest_path. + if polygon_simplified.contains(LineString([apex, point])) or polygon_simplified.boundary.contains(LineString([apex, point])): + this_funnel = [apex, point] + + else: + for i in range(1, len(this_funnel)): + if polygon_simplified.contains(LineString([this_funnel[i], point])) or polygon_simplified.boundary.contains(LineString([this_funnel[i], point])): + first_seen = i + break + + seg1 = list(LineString([this_funnel[first_seen-1], this_funnel[first_seen]]).coords) + seg2 = list(LineString([this_funnel[first_seen], point]).coords) + + vec1 = (seg1[1][0] - seg1[0][0], seg1[1][1] - seg1[0][1]) + vec2 = (seg2[1][0] - seg2[0][0], seg2[1][1] - seg2[0][1]) + + cross_prod = vec1[0]*vec2[1] - vec1[1]*vec2[0] + + if cross_prod*reflex_sign >= 0: + # If this vertex is reflex: + this_funnel = this_funnel[0:first_seen+1] + [point] + else: + first_seen = min(i for i in range(1, len(other_funnel)) if polygon_simplified.contains(LineString([other_funnel[i], point])) or polygon_simplified.boundary.contains(LineString([other_funnel[i], point]))) + found_shortest_path += other_funnel[1: first_seen+1] + apex = other_funnel[first_seen] + this_funnel = [apex, point] + other_funnel = other_funnel[first_seen:] + + # Reassign this_funnel and other_funnel to left_funnel and right_funnel: + if point in left_path_simplified_points: + left_funnel = this_funnel + right_funnel = other_funnel + elif point in right_path_simplified_points: + right_funnel = this_funnel + left_funnel = other_funnel + + # Still need to complete the path by adding the portion from the current apex to + # the endpoint: + found_shortest_path += left_funnel[1:] + + return found_shortest_path + + +def convexify_hole_boundaries(geometries_df, holes_df): + """ + Partially fill gaps as follows: + (1) Assign any gap that only adjoins 1 geometry to that geometry. + (2) For each gap that adjoins at least 2 geometries, "convexify" the geometries + surrounding the gap by replacing the gap's boundary with each geometry by the + shortest path within the gap between its endpoints and "filling in" the + geometry up to the new boundary. (Exterior boundaries, if any, are left alone.) + If there are only 2 non-exterior (and no exterior) geometries intersecting + the gap, this will fill the gap completely; otherwise it will usually leave one or + more smaller gaps remaining. The convexity of the geometry boundaries will simplify + the process of filling the remaining gap(s). + """ + geometries_df = geometries_df.copy() + holes_df = holes_df.copy() + + completed_holes_df = GeoDataFrame(columns=["region"], geometry=GeoSeries([]), crs=holes_df.crs) + + if len(holes_df) > 0: + holes_to_process = deque(list(holes_df["geometry"])) + this_region = list(holes_df["region"])[0] # All holes in this dataframe should be from the same region + if this_region is None: + pbar = tqdm(desc="Gaps to simplify", total=len(holes_to_process)) + else: + pbar = tqdm(desc=f"Gaps to simplify in region {this_region}", total=len(holes_to_process)) + else: + holes_to_process = deque([]) + pbar = tqdm(desc="Gaps to simplify", total=len(holes_to_process)) + + while len(holes_to_process) > 0: + pbar_increment = 1 + this_hole = holes_to_process.popleft() + this_hole_df = GeoDataFrame(geometry=GeoSeries([this_hole]), crs=holes_df.crs) + this_hole_boundaries_df = construct_hole_boundaries(geometries_df, this_hole_df) + + # Take care of some trivial cases: + if len(set(this_hole_boundaries_df["target"]).difference({-1})) == 0: + # This is probably a small component of a region that isn't assigned to + # any geometry in that region. Just leave it alone and let it be a hole. + if this_region is not None: + print("Found a component of the region at index", this_region, "that does not intersect any geometry assigned to that region.") + + elif len(set(this_hole_boundaries_df["target"]).difference({-1})) == 1: + # Attach the hole to the unique non-exterior geometry that it intersects: + poly_to_add_to = list(set(this_hole_boundaries_df["target"]).difference({-1}))[0] + geometries_df["geometry"][poly_to_add_to] = unary_union([geometries_df["geometry"][poly_to_add_to], this_hole]) + + else: + # Each remaining hole intersects at least 2 geometries nontrivially. + # "Convexify" the geometries surrounding this hole by replacing the + # boundary with each geometry by the shortest path within the hole + # between its endpoints and "filling in" the geometry up to the + # new boundary. (Exterior boundaries, if any, should be left alone.) + new_hole_in_progress = this_hole + this_hole_triangulation = triangulate_polygon(new_hole_in_progress) + + for thb_ind in this_hole_boundaries_df.index: + thb = this_hole_boundaries_df["geometry"][thb_ind] + this_geom = this_hole_boundaries_df["target"][thb_ind] + + if this_geom != -1: + start = list(extract_unique_points(thb).geoms)[0] + end = list(extract_unique_points(thb).geoms)[-1] + + sp = LineString(shortest_path_in_polygon(this_hole, start, end, full_triangulation=this_hole_triangulation)) + + piece_to_add_boundary = unary_union([thb, sp]) + if piece_to_add_boundary.geom_type == "MultiLineString": + piece_to_add_boundary = linemerge(piece_to_add_boundary) + + piece_to_add = unary_union(polygonize(piece_to_add_boundary)) + geometries_df["geometry"][this_geom] = unary_union([geometries_df["geometry"][this_geom], piece_to_add]) + new_hole_in_progress = new_hole_in_progress.difference(piece_to_add) + + if not new_hole_in_progress.is_empty: + if new_hole_in_progress.geom_type == "Polygon": + new_holes = [new_hole_in_progress] + elif new_hole_in_progress.geom_type == "MultiPolygon": + new_holes = list(new_hole_in_progress.geoms) + + for new_hole in new_holes: + new_hole = orient(new_hole) + new_hole_df = GeoDataFrame(geometry=GeoSeries([new_hole]), crs=holes_df.crs) + new_hole_df.insert(0, "region", this_region) + completed_holes_df = pandas.concat([completed_holes_df, new_hole_df]).reset_index(drop=True) + + pbar.update(pbar_increment) + + pbar.close() + + return geometries_df, completed_holes_df diff --git a/notebooks/.ipynb_checkpoints/Maup data management demo-checkpoint.ipynb b/notebooks/.ipynb_checkpoints/Maup data management demo-checkpoint.ipynb new file mode 100644 index 0000000..63cd062 --- /dev/null +++ b/notebooks/.ipynb_checkpoints/Maup data management demo-checkpoint.ipynb @@ -0,0 +1,5636 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1372272b", + "metadata": {}, + "source": [ + "### Demo notebook for data management using Maup, based on Denver County, CO" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "44231122", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import geopandas as gpd\n", + "import maup\n", + "\n", + "maup.progress.enabled = True\n", + "\n", + "pd.options.mode.chained_assignment = None\n", + "pd.set_option('display.max_columns', None)" + ] + }, + { + "cell_type": "markdown", + "id": "ccae0af8", + "metadata": {}, + "source": [ + "### Goal: Add population data and election data from 2016 and 2018 to 2020 precincts." + ] + }, + { + "cell_type": "markdown", + "id": "bc693ada", + "metadata": {}, + "source": [ + "### Here are the shapefiles that we'll need:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "2a4e791a", + "metadata": {}, + "outputs": [], + "source": [ + "blocks_df = gpd.read_file(\"../examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.shp\")\n", + "precincts2016_df = gpd.read_file(\"../examples/Shapefiles/DenverCo_precincts2016_repaired/DenverCo_precincts2016_repaired.shp\")\n", + "precincts2018_df = gpd.read_file(\"../examples/Shapefiles/DenverCo_precincts2018_repaired/DenverCo_precincts2018_repaired.shp\")\n", + "precincts2020_df = gpd.read_file(\"../examples/Shapefiles/DenverCo_precincts2020_repaired/DenverCo_precincts2020_repaired.shp\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "b614fcef", + "metadata": {}, + "source": [ + "### Take a look at what information each of these shapefiles contains:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "9d840231", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
STATEFP20COUNTYFP20TRACTCE20BLOCKCE20GEOID20NAME20CD116SLDL20SLDU20CD118SLDL22SLDU22MTFCC20FUNCSTAT20ALAND20AWATER20INTPTLAT20INTPTLON20TOTPOP20WHITE20BLACK20AMIN20ASIAN20NHPI20OTHER202MORE20HISP20NH_WHITE20NH_BLACK20NH_AMIN20NH_ASIAN20NH_NHPI20NH_OTHER20NH_2MORE20VAP20HVAP20WVAP20BVAP20AMINVAP20ASIANVAP20NHPIVAP20OTHERVAP202MOREVAP20H_WHITE20H_BLACK20H_AMIN20H_ASIAN20H_NHPI20H_OTHER20H_2MORE20TOTPOP20RHISP20RNHWHITE20RNHBLACK20RNHAMIN20RNHASIAN20RNH_NHPI20RNHOTHER20RNH2MORE20RWHITE20RBLACK20RAMIN20RASIAN20RNHPI20ROTHER20R2MORE20Rgeometry
0080310005013004080310005013004Block 300414341434G5040S113600+39.7445040-105.0362730000000000000000000000000000000000000000000000000POLYGON ((3130205.522 1696255.187, 3130218.309...
1080310043034019080310043034019Block 401916311231G5040S173060+39.7138779-104.932222733290110027250010002422100100240100023372500100029011002POLYGON ((3159569.782 1685233.398, 3159568.175...
2080310055031008080310055031008Block 100811161126G5040S171200+39.6308773-105.029607138250140176220140143152101401430000033862201401425014017POLYGON ((3132214.313 1655090.209, 3132219.592...
3080310032041004080310032041004Block 100418311631G5040S156480+39.7376302-104.96899931088810007121685100006101168510007830000761081685100006881000712POLYGON ((3149182.154 1694282.223, 3149242.061...
4080310031011022080310031011022Block 102218331831G5040S147590+39.7475700-104.960387338370000011360000012912800000110000003813600000137000001POLYGON ((3151562.306 1697493.116, 3151561.779...
............................................................................................................................................................................................................
10144080310069032005080310069032005Block 200519311932G5040S186260+39.6706226-104.911557251300110613113000001945112601106110011054511130000019300110613POLYGON ((3165300.448 1669539.900, 3165299.396...
10145080319800011033080319800011033Block 103317331733G5040S118600+39.8255061-104.7465304000000000000000000000000000000000000000000000000POLYGON ((3211448.603 1727256.205, 3211526.127...
10146080319800011024080319800011024Block 102417331733G5040S1696250+39.8342487-104.7591599000000000000000000000000000000000000000000000000POLYGON ((3204485.897 1729532.389, 3204502.996...
10147080310004013008080310004013008Block 300814341434G5040S176610+39.7711189-105.019748372690000123680000015825600000210000117236800000169000012POLYGON ((3134776.811 1706335.477, 3134790.024...
10148080310009031012080310009031012Block 101214341434G5040S185850+39.7189546-105.02940608320034033235418034013563414032018192000032208455180340132013403323POLYGON ((3132206.908 1687391.415, 3132550.655...
\n", + "

10149 rows × 67 columns

\n", + "
" + ], + "text/plain": [ + " STATEFP20 COUNTYFP20 TRACTCE20 BLOCKCE20 GEOID20 NAME20 \\\n", + "0 08 031 000501 3004 080310005013004 Block 3004 \n", + "1 08 031 004303 4019 080310043034019 Block 4019 \n", + "2 08 031 005503 1008 080310055031008 Block 1008 \n", + "3 08 031 003204 1004 080310032041004 Block 1004 \n", + "4 08 031 003101 1022 080310031011022 Block 1022 \n", + "... ... ... ... ... ... ... \n", + "10144 08 031 006903 2005 080310069032005 Block 2005 \n", + "10145 08 031 980001 1033 080319800011033 Block 1033 \n", + "10146 08 031 980001 1024 080319800011024 Block 1024 \n", + "10147 08 031 000401 3008 080310004013008 Block 3008 \n", + "10148 08 031 000903 1012 080310009031012 Block 1012 \n", + "\n", + " CD116 SLDL20 SLDU20 CD118 SLDL22 SLDU22 MTFCC20 FUNCSTAT20 \\\n", + "0 1 4 34 1 4 34 G5040 S \n", + "1 1 6 31 1 2 31 G5040 S \n", + "2 1 1 16 1 1 26 G5040 S \n", + "3 1 8 31 1 6 31 G5040 S \n", + "4 1 8 33 1 8 31 G5040 S \n", + "... ... ... ... ... ... ... ... ... \n", + "10144 1 9 31 1 9 32 G5040 S \n", + "10145 1 7 33 1 7 33 G5040 S \n", + "10146 1 7 33 1 7 33 G5040 S \n", + "10147 1 4 34 1 4 34 G5040 S \n", + "10148 1 4 34 1 4 34 G5040 S \n", + "\n", + " ALAND20 AWATER20 INTPTLAT20 INTPTLON20 TOTPOP20 WHITE20 \\\n", + "0 11360 0 +39.7445040 -105.0362730 0 0 \n", + "1 17306 0 +39.7138779 -104.9322227 33 29 \n", + "2 17120 0 +39.6308773 -105.0296071 38 25 \n", + "3 15648 0 +39.7376302 -104.9689993 108 88 \n", + "4 14759 0 +39.7475700 -104.9603873 38 37 \n", + "... ... ... ... ... ... ... \n", + "10144 18626 0 +39.6706226 -104.9115572 51 30 \n", + "10145 11860 0 +39.8255061 -104.7465304 0 0 \n", + "10146 169625 0 +39.8342487 -104.7591599 0 0 \n", + "10147 17661 0 +39.7711189 -105.0197483 72 69 \n", + "10148 18585 0 +39.7189546 -105.0294060 83 20 \n", + "\n", + " BLACK20 AMIN20 ASIAN20 NHPI20 OTHER20 2MORE20 HISP20 NH_WHITE20 \\\n", + "0 0 0 0 0 0 0 0 0 \n", + "1 0 1 1 0 0 2 7 25 \n", + "2 0 1 4 0 1 7 6 22 \n", + "3 1 0 0 0 7 12 16 85 \n", + "4 0 0 0 0 0 1 1 36 \n", + "... ... ... ... ... ... ... ... ... \n", + "10144 0 1 1 0 6 13 11 30 \n", + "10145 0 0 0 0 0 0 0 0 \n", + "10146 0 0 0 0 0 0 0 0 \n", + "10147 0 0 0 0 1 2 3 68 \n", + "10148 0 3 4 0 33 23 54 18 \n", + "\n", + " NH_BLACK20 NH_AMIN20 NH_ASIAN20 NH_NHPI20 NH_OTHER20 NH_2MORE20 \\\n", + "0 0 0 0 0 0 0 \n", + "1 0 0 1 0 0 0 \n", + "2 0 1 4 0 1 4 \n", + "3 1 0 0 0 0 6 \n", + "4 0 0 0 0 0 1 \n", + "... ... ... ... ... ... ... \n", + "10144 0 0 0 0 1 9 \n", + "10145 0 0 0 0 0 0 \n", + "10146 0 0 0 0 0 0 \n", + "10147 0 0 0 0 0 1 \n", + "10148 0 3 4 0 1 3 \n", + "\n", + " VAP20 HVAP20 WVAP20 BVAP20 AMINVAP20 ASIANVAP20 NHPIVAP20 \\\n", + "0 0 0 0 0 0 0 0 \n", + "1 24 2 21 0 0 1 0 \n", + "2 31 5 21 0 1 4 0 \n", + "3 101 16 85 1 0 0 0 \n", + "4 29 1 28 0 0 0 0 \n", + "... ... ... ... ... ... ... ... \n", + "10144 45 11 26 0 1 1 0 \n", + "10145 0 0 0 0 0 0 0 \n", + "10146 0 0 0 0 0 0 0 \n", + "10147 58 2 56 0 0 0 0 \n", + "10148 56 34 14 0 3 2 0 \n", + "\n", + " OTHERVAP20 2MOREVAP20 H_WHITE20 H_BLACK20 H_AMIN20 H_ASIAN20 \\\n", + "0 0 0 0 0 0 0 \n", + "1 0 2 4 0 1 0 \n", + "2 1 4 3 0 0 0 \n", + "3 7 8 3 0 0 0 \n", + "4 0 1 1 0 0 0 \n", + "... ... ... ... ... ... ... \n", + "10144 6 11 0 0 1 1 \n", + "10145 0 0 0 0 0 0 \n", + "10146 0 0 0 0 0 0 \n", + "10147 0 2 1 0 0 0 \n", + "10148 18 19 2 0 0 0 \n", + "\n", + " H_NHPI20 H_OTHER20 H_2MORE20 TOTPOP20R HISP20R NHWHITE20R \\\n", + "0 0 0 0 0 0 0 \n", + "1 0 0 2 33 7 25 \n", + "2 0 0 3 38 6 22 \n", + "3 0 7 6 108 16 85 \n", + "4 0 0 0 38 1 36 \n", + "... ... ... ... ... ... ... \n", + "10144 0 5 4 51 11 30 \n", + "10145 0 0 0 0 0 0 \n", + "10146 0 0 0 0 0 0 \n", + "10147 0 1 1 72 3 68 \n", + "10148 0 32 20 84 55 18 \n", + "\n", + " NHBLACK20R NHAMIN20R NHASIAN20R NH_NHPI20R NHOTHER20R NH2MORE20R \\\n", + "0 0 0 0 0 0 0 \n", + "1 0 0 1 0 0 0 \n", + "2 0 1 4 0 1 4 \n", + "3 1 0 0 0 0 6 \n", + "4 0 0 0 0 0 1 \n", + "... ... ... ... ... ... ... \n", + "10144 0 0 0 0 1 9 \n", + "10145 0 0 0 0 0 0 \n", + "10146 0 0 0 0 0 0 \n", + "10147 0 0 0 0 0 1 \n", + "10148 0 3 4 0 1 3 \n", + "\n", + " WHITE20R BLACK20R AMIN20R ASIAN20R NHPI20R OTHER20R 2MORE20R \\\n", + "0 0 0 0 0 0 0 0 \n", + "1 29 0 1 1 0 0 2 \n", + "2 25 0 1 4 0 1 7 \n", + "3 88 1 0 0 0 7 12 \n", + "4 37 0 0 0 0 0 1 \n", + "... ... ... ... ... ... ... ... \n", + "10144 30 0 1 1 0 6 13 \n", + "10145 0 0 0 0 0 0 0 \n", + "10146 0 0 0 0 0 0 0 \n", + "10147 69 0 0 0 0 1 2 \n", + "10148 20 1 3 4 0 33 23 \n", + "\n", + " geometry \n", + "0 POLYGON ((3130205.522 1696255.187, 3130218.309... \n", + "1 POLYGON ((3159569.782 1685233.398, 3159568.175... \n", + "2 POLYGON ((3132214.313 1655090.209, 3132219.592... \n", + "3 POLYGON ((3149182.154 1694282.223, 3149242.061... \n", + "4 POLYGON ((3151562.306 1697493.116, 3151561.779... \n", + "... ... \n", + "10144 POLYGON ((3165300.448 1669539.900, 3165299.396... \n", + "10145 POLYGON ((3211448.603 1727256.205, 3211526.127... \n", + "10146 POLYGON ((3204485.897 1729532.389, 3204502.996... \n", + "10147 POLYGON ((3134776.811 1706335.477, 3134790.024... \n", + "10148 POLYGON ((3132206.908 1687391.415, 3132550.655... \n", + "\n", + "[10149 rows x 67 columns]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "22521abd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
COUNTYFPNAMELSADG16PREDCliG16PRERTruG16PRELJohG16PREGSteG16PREIMcMG16PREOthG16USSDBenG16USSRGleG16USSLWilG16USSGMenG16USSOthgeometry
0031Denver 10172316334211973016426179POLYGON ((3125703.139 1681147.799, 3125702.944...
1031Denver 10275716623257773817428149POLYGON ((3129675.455 1682118.384, 3129674.269...
2031Denver 103678241442241470322734189POLYGON ((3129642.865 1679001.901, 3129708.711...
3031Denver 20367711433102565114320152POLYGON ((3146588.324 1694971.352, 3146587.373...
4031Denver 2089868859384292210640545POLYGON ((3146274.972 1693705.886, 3146340.216...
.............................................
341031Denver 916929449722381092648349167POLYGON ((3164780.350 1664424.144, 3165010.513...
342031Denver 92751628534912652130620121POLYGON ((3163781.804 1663194.808, 3163746.863...
343031Denver 9237393365525316732348561412POLYGON ((3173239.768 1665060.235, 3173239.714...
344031Denver 9375523354587355335519102POLYGON ((3173156.086 1655193.513, 3173161.344...
345031Denver 9334032222811834082311280POLYGON ((3169603.158 1661791.346, 3169558.084...
\n", + "

346 rows × 14 columns

\n", + "
" + ], + "text/plain": [ + " COUNTYFP NAMELSAD G16PREDCli G16PRERTru G16PRELJoh G16PREGSte \\\n", + "0 031 Denver 101 723 163 34 21 \n", + "1 031 Denver 102 757 166 23 25 \n", + "2 031 Denver 103 678 241 44 22 \n", + "3 031 Denver 203 677 114 33 10 \n", + "4 031 Denver 208 986 88 59 38 \n", + ".. ... ... ... ... ... ... \n", + "341 031 Denver 916 929 449 72 23 \n", + "342 031 Denver 927 516 285 34 9 \n", + "343 031 Denver 923 739 336 55 25 \n", + "344 031 Denver 937 552 335 45 8 \n", + "345 031 Denver 933 403 222 28 11 \n", + "\n", + " G16PREIMcM G16PREOth G16USSDBen G16USSRGle G16USSLWil G16USSGMen \\\n", + "0 1 9 730 164 26 17 \n", + "1 7 7 738 174 28 14 \n", + "2 4 14 703 227 34 18 \n", + "3 2 5 651 143 20 15 \n", + "4 4 2 922 106 40 54 \n", + ".. ... ... ... ... ... ... \n", + "341 8 10 926 483 49 16 \n", + "342 12 6 521 306 20 12 \n", + "343 3 16 732 348 56 14 \n", + "344 7 3 553 355 19 10 \n", + "345 8 3 408 231 12 8 \n", + "\n", + " G16USSOth geometry \n", + "0 9 POLYGON ((3125703.139 1681147.799, 3125702.944... \n", + "1 9 POLYGON ((3129675.455 1682118.384, 3129674.269... \n", + "2 9 POLYGON ((3129642.865 1679001.901, 3129708.711... \n", + "3 2 POLYGON ((3146588.324 1694971.352, 3146587.373... \n", + "4 5 POLYGON ((3146274.972 1693705.886, 3146340.216... \n", + ".. ... ... \n", + "341 7 POLYGON ((3164780.350 1664424.144, 3165010.513... \n", + "342 1 POLYGON ((3163781.804 1663194.808, 3163746.863... \n", + "343 12 POLYGON ((3173239.768 1665060.235, 3173239.714... \n", + "344 2 POLYGON ((3173156.086 1655193.513, 3173161.344... \n", + "345 0 POLYGON ((3169603.158 1661791.346, 3169558.084... \n", + "\n", + "[346 rows x 14 columns]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2016_df" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "8849c8cf", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
COUNTYFPVTDSTNAMECD116FPSLDUSTSLDLSTPRECIDAG18DAG18RSOS18DSOS18RTRE18DTRE18RGOV18DGOV18RREG18DREG18RUSH18DUSH18RTOTPOPNH_WHITENH_BLACKNH_AMINNH_ASIANNH_NHPINH_OTHERNH_2MOREHISPH_WHITEH_BLACKH_AMINH_ASIANH_NHPIH_OTHERH_2MOREVAPHVAPWVAPBVAPAMINVAPASIANVAPNHPIVAPOTHERVAP2MOREVAPgeometry
0031031745Denver 7450103300713307167451084303107331610463341137272105025411212640000000000000000000000000POLYGON ((3167607.595 1714575.543, 3167607.566...
1031031540Denver 5400103400513405165407601937512157152567681967131767711790000000000000000000000000POLYGON ((3139202.439 1699577.829, 3139240.848...
2031031744Denver 74401033007133071674410003091003312994307105128097126710442619131301100028001002529128313011000POLYGON ((3180920.093 1705405.138, 3180920.088...
3031031530Denver 53001031005131051653064562864867462566517210729330082211000091171221062933008POLYGON ((3144153.912 1694345.882, 3144155.827...
4031031940Denver 940010310091310916940395267400273386271422254375247417244274258108002420000202684252108002POLYGON ((3165244.173 1654452.896, 3165241.340...
..........................................................................................................................................
351031031102Denver 102010340011340116102577134581129572135556130559126585123416943411856422053031011553307913013101152653186435355393210117POLYGON ((3133625.259 1681579.939, 3133626.217...
352031031101Denver 101010340011340116101615122619123615125613127599122634115441457866341092734357717802598441509154278521134993128792521POLYGON ((3125719.959 1678986.315, 3125719.959...
353031031924Denver 92401031009131091692446384476874738546894458864688744631586746196853620412096371555048659364184114296401856826133POLYGON ((3180600.942 1663222.850, 3180600.864...
354031031604Denver 604010330061330616604717133734126718130745121708114737116457889314081899046164108941326267050810632206877959911361944101POLYGON ((3173051.077 1693417.146, 3173051.078...
355031031746Denver 7460103300713307167464621464691464461604951234451274901270000000000000000000000000POLYGON ((3178132.638 1716671.381, 3178133.440...
\n", + "

356 rows × 45 columns

\n", + "
" + ], + "text/plain": [ + " COUNTYFP VTDST NAME CD116FP SLDUST SLDLST PRECID AG18D \\\n", + "0 031 031745 Denver 745 01 033 007 1330716745 1084 \n", + "1 031 031540 Denver 540 01 034 005 1340516540 760 \n", + "2 031 031744 Denver 744 01 033 007 1330716744 1000 \n", + "3 031 031530 Denver 530 01 031 005 1310516530 64 \n", + "4 031 031940 Denver 940 01 031 009 1310916940 395 \n", + ".. ... ... ... ... ... ... ... ... \n", + "351 031 031102 Denver 102 01 034 001 1340116102 577 \n", + "352 031 031101 Denver 101 01 034 001 1340116101 615 \n", + "353 031 031924 Denver 924 01 031 009 1310916924 463 \n", + "354 031 031604 Denver 604 01 033 006 1330616604 717 \n", + "355 031 031746 Denver 746 01 033 007 1330716746 462 \n", + "\n", + " AG18R SOS18D SOS18R TRE18D TRE18R GOV18D GOV18R REG18D REG18R \\\n", + "0 303 1073 316 1046 334 1137 272 1050 254 \n", + "1 193 751 215 715 256 768 196 713 176 \n", + "2 309 1003 312 994 307 1051 280 971 267 \n", + "3 5 62 8 64 8 67 4 62 5 \n", + "4 267 400 273 386 271 422 254 375 247 \n", + ".. ... ... ... ... ... ... ... ... ... \n", + "351 134 581 129 572 135 556 130 559 126 \n", + "352 122 619 123 615 125 613 127 599 122 \n", + "353 84 476 87 473 85 468 94 458 86 \n", + "354 133 734 126 718 130 745 121 708 114 \n", + "355 146 469 146 446 160 495 123 445 127 \n", + "\n", + " USH18D USH18R TOTPOP NH_WHITE NH_BLACK NH_AMIN NH_ASIAN NH_NHPI \\\n", + "0 1121 264 0 0 0 0 0 0 \n", + "1 771 179 0 0 0 0 0 0 \n", + "2 1044 261 91 31 30 1 1 0 \n", + "3 66 5 172 107 29 3 3 0 \n", + "4 417 244 274 258 1 0 8 0 \n", + ".. ... ... ... ... ... ... ... ... \n", + "351 585 123 4169 434 118 56 422 0 \n", + "352 634 115 4414 578 66 34 109 2 \n", + "353 468 87 4463 1586 746 19 685 3 \n", + "354 737 116 4578 893 1408 18 990 4 \n", + "355 490 127 0 0 0 0 0 0 \n", + "\n", + " NH_OTHER NH_2MORE HISP H_WHITE H_BLACK H_AMIN H_ASIAN H_NHPI \\\n", + "0 0 0 0 0 0 0 0 0 \n", + "1 0 0 0 0 0 0 0 0 \n", + "2 0 0 28 0 0 1 0 0 \n", + "3 0 8 22 11 0 0 0 0 \n", + "4 0 2 4 2 0 0 0 0 \n", + ".. ... ... ... ... ... ... ... ... \n", + "351 5 30 3101 1553 30 79 13 0 \n", + "352 7 34 3577 1780 25 98 4 4 \n", + "353 6 204 1209 637 15 5 5 0 \n", + "354 6 164 1089 413 26 26 7 0 \n", + "355 0 0 0 0 0 0 0 0 \n", + "\n", + " H_OTHER H_2MORE VAP HVAP WVAP BVAP AMINVAP ASIANVAP NHPIVAP \\\n", + "0 0 0 0 0 0 0 0 0 0 \n", + "1 0 0 0 0 0 0 0 0 0 \n", + "2 25 2 91 28 31 30 1 1 0 \n", + "3 9 1 171 22 106 29 3 3 0 \n", + "4 2 0 268 4 252 1 0 8 0 \n", + ".. ... ... ... ... ... ... ... ... ... \n", + "351 1310 115 2653 1864 353 55 39 321 0 \n", + "352 1509 154 2785 2113 499 31 28 79 2 \n", + "353 486 59 3641 841 1429 640 18 568 2 \n", + "354 508 106 3220 687 795 991 13 619 4 \n", + "355 0 0 0 0 0 0 0 0 0 \n", + "\n", + " OTHERVAP 2MOREVAP geometry \n", + "0 0 0 POLYGON ((3167607.595 1714575.543, 3167607.566... \n", + "1 0 0 POLYGON ((3139202.439 1699577.829, 3139240.848... \n", + "2 0 0 POLYGON ((3180920.093 1705405.138, 3180920.088... \n", + "3 0 8 POLYGON ((3144153.912 1694345.882, 3144155.827... \n", + "4 0 2 POLYGON ((3165244.173 1654452.896, 3165241.340... \n", + ".. ... ... ... \n", + "351 1 17 POLYGON ((3133625.259 1681579.939, 3133626.217... \n", + "352 5 21 POLYGON ((3125719.959 1678986.315, 3125719.959... \n", + "353 6 133 POLYGON ((3180600.942 1663222.850, 3180600.864... \n", + "354 4 101 POLYGON ((3173051.077 1693417.146, 3173051.078... \n", + "355 0 0 POLYGON ((3178132.638 1716671.381, 3178133.440... \n", + "\n", + "[356 rows x 45 columns]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2018_df" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "f6af11f7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PRECIDSTATEFP20COUNTYFP20NAMECD18SUD18SLD18VTDST20NOTESPRES20DSEN20DPRES20RSEN20Rgeometry
0131051653008031Denver 53013105031530None73751612POLYGON ((3144474.619 1694874.799, 3144509.314...
1131061660508031Denver 60513106031605None720702110137POLYGON ((3162200.074 1691024.061, 3162164.358...
2131061660608031Denver 60613106031606None832819107121POLYGON ((3162459.795 1693616.802, 3162459.063...
3131061660708031Denver 60713106031607None597595102113POLYGON ((3165142.137 1693683.542, 3165141.880...
4131061660908031Denver 60913106031609None2412335971POLYGON ((3152968.867 1690916.131, 3153065.627...
.............................................
351133071674608031Denver 74613307031746None17681699386469POLYGON ((3172960.862 1716278.085, 3172953.814...
352134051654008031Denver 54013405031540None15581477416489POLYGON ((3139192.261 1699587.973, 3139181.643...
353134051654108031Denver 54113405031541None15541504246297POLYGON ((3148046.593 1705187.168, 3148047.303...
354134051654208031Denver 54213405031542None881828200247POLYGON ((3133494.258 1698947.396, 3133494.426...
355134051654308031Denver 54313405031543None8378369390POLYGON ((3140186.339 1689621.761, 3140186.210...
\n", + "

356 rows × 14 columns

\n", + "
" + ], + "text/plain": [ + " PRECID STATEFP20 COUNTYFP20 NAME CD18 SUD18 SLD18 VTDST20 \\\n", + "0 1310516530 08 031 Denver 530 1 31 05 031530 \n", + "1 1310616605 08 031 Denver 605 1 31 06 031605 \n", + "2 1310616606 08 031 Denver 606 1 31 06 031606 \n", + "3 1310616607 08 031 Denver 607 1 31 06 031607 \n", + "4 1310616609 08 031 Denver 609 1 31 06 031609 \n", + ".. ... ... ... ... ... ... ... ... \n", + "351 1330716746 08 031 Denver 746 1 33 07 031746 \n", + "352 1340516540 08 031 Denver 540 1 34 05 031540 \n", + "353 1340516541 08 031 Denver 541 1 34 05 031541 \n", + "354 1340516542 08 031 Denver 542 1 34 05 031542 \n", + "355 1340516543 08 031 Denver 543 1 34 05 031543 \n", + "\n", + " NOTES PRES20D SEN20D PRES20R SEN20R \\\n", + "0 None 73 75 16 12 \n", + "1 None 720 702 110 137 \n", + "2 None 832 819 107 121 \n", + "3 None 597 595 102 113 \n", + "4 None 241 233 59 71 \n", + ".. ... ... ... ... ... \n", + "351 None 1768 1699 386 469 \n", + "352 None 1558 1477 416 489 \n", + "353 None 1554 1504 246 297 \n", + "354 None 881 828 200 247 \n", + "355 None 837 836 93 90 \n", + "\n", + " geometry \n", + "0 POLYGON ((3144474.619 1694874.799, 3144509.314... \n", + "1 POLYGON ((3162200.074 1691024.061, 3162164.358... \n", + "2 POLYGON ((3162459.795 1693616.802, 3162459.063... \n", + "3 POLYGON ((3165142.137 1693683.542, 3165141.880... \n", + "4 POLYGON ((3152968.867 1690916.131, 3153065.627... \n", + ".. ... \n", + "351 POLYGON ((3172960.862 1716278.085, 3172953.814... \n", + "352 POLYGON ((3139192.261 1699587.973, 3139181.643... \n", + "353 POLYGON ((3148046.593 1705187.168, 3148047.303... \n", + "354 POLYGON ((3133494.258 1698947.396, 3133494.426... \n", + "355 POLYGON ((3140186.339 1689621.761, 3140186.210... \n", + "\n", + "[356 rows x 14 columns]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2020_df" + ] + }, + { + "cell_type": "markdown", + "id": "b93f583a", + "metadata": {}, + "source": [ + "### So the blocks file has lots of population data and the precinct files each have election data for one year.\n", + "### It might be convenient to rename some of the election columns in the 2016 file so that they have the same format as the other years." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "ad1aa151", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Index(['COUNTYFP', 'NAMELSAD', 'G16PREDCli', 'G16PRERTru', 'G16PRELJoh',\n", + " 'G16PREGSte', 'G16PREIMcM', 'G16PREOth', 'G16USSDBen', 'G16USSRGle',\n", + " 'G16USSLWil', 'G16USSGMen', 'G16USSOth', 'geometry'],\n", + " dtype='object')" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2016_df.columns" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "4cd76b85", + "metadata": {}, + "outputs": [], + "source": [ + "precincts2016_df = precincts2016_df.rename(columns = {\n", + " 'G16PREDCli': 'PRES16D',\n", + " 'G16PRERTru': 'PRES16R',\n", + " 'G16USSDBen': 'SEN16D',\n", + " 'G16USSRGle': 'SEN16R'\n", + "})" + ] + }, + { + "cell_type": "markdown", + "id": "6118e4a4", + "metadata": {}, + "source": [ + "### In order to move all this data around, we'll need assignments of blocks to precincts for each of the precinct files." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "b226cfc9", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|████████████████████████████████████████| 356/356 [00:00<00:00, 786.59it/s]\n", + "100%|████████████████████████████████████████| 356/356 [00:00<00:00, 371.12it/s]\n", + "100%|████████████████████████████████████████| 356/356 [00:00<00:00, 892.50it/s]\n", + "100%|████████████████████████████████████████| 356/356 [00:01<00:00, 347.47it/s]\n", + "100%|████████████████████████████████████████| 346/346 [00:00<00:00, 725.00it/s]\n", + "100%|████████████████████████████████████████| 346/346 [00:01<00:00, 311.78it/s]\n" + ] + } + ], + "source": [ + "blocks_to_precincts2020_assignment = maup.assign(blocks_df.geometry, precincts2020_df.geometry)\n", + "blocks_to_precincts2018_assignment = maup.assign(blocks_df.geometry, precincts2018_df.geometry)\n", + "blocks_to_precincts2016_assignment = maup.assign(blocks_df.geometry, precincts2016_df.geometry)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "cdcaf044", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0 300\n", + "1 56\n", + "2 41\n", + "3 73\n", + "4 262\n", + " ... \n", + "10144 96\n", + "10145 234\n", + "10146 234\n", + "10147 292\n", + "10148 313\n", + "Length: 10149, dtype: int64" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_to_precincts2020_assignment" + ] + }, + { + "cell_type": "markdown", + "id": "fbed2841", + "metadata": {}, + "source": [ + "### First step: Aggregate population data from blocks to 2020 precincts.\n", + "### (We'll just use a few of the population columns for this demo.)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "61cf6d7e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Index(['STATEFP20', 'COUNTYFP20', 'TRACTCE20', 'BLOCKCE20', 'GEOID20',\n", + " 'NAME20', 'CD116', 'SLDL20', 'SLDU20', 'CD118', 'SLDL22', 'SLDU22',\n", + " 'MTFCC20', 'FUNCSTAT20', 'ALAND20', 'AWATER20', 'INTPTLAT20',\n", + " 'INTPTLON20', 'TOTPOP20', 'WHITE20', 'BLACK20', 'AMIN20', 'ASIAN20',\n", + " 'NHPI20', 'OTHER20', '2MORE20', 'HISP20', 'NH_WHITE20', 'NH_BLACK20',\n", + " 'NH_AMIN20', 'NH_ASIAN20', 'NH_NHPI20', 'NH_OTHER20', 'NH_2MORE20',\n", + " 'VAP20', 'HVAP20', 'WVAP20', 'BVAP20', 'AMINVAP20', 'ASIANVAP20',\n", + " 'NHPIVAP20', 'OTHERVAP20', '2MOREVAP20', 'H_WHITE20', 'H_BLACK20',\n", + " 'H_AMIN20', 'H_ASIAN20', 'H_NHPI20', 'H_OTHER20', 'H_2MORE20',\n", + " 'TOTPOP20R', 'HISP20R', 'NHWHITE20R', 'NHBLACK20R', 'NHAMIN20R',\n", + " 'NHASIAN20R', 'NH_NHPI20R', 'NHOTHER20R', 'NH2MORE20R', 'WHITE20R',\n", + " 'BLACK20R', 'AMIN20R', 'ASIAN20R', 'NHPI20R', 'OTHER20R', '2MORE20R',\n", + " 'geometry'],\n", + " dtype='object')" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df.columns" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "d1bf7b5e", + "metadata": {}, + "outputs": [], + "source": [ + "pop_cols = ['TOTPOP20', 'VAP20']" + ] + }, + { + "cell_type": "markdown", + "id": "5fd55604", + "metadata": {}, + "source": [ + "### We can use the assignment of blocks to precincts to aggregate populations from blocks up to precincts:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "5da80e36", + "metadata": {}, + "outputs": [], + "source": [ + "precincts2020_df[pop_cols] = blocks_df[pop_cols].groupby(blocks_to_precincts2020_assignment).sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "9905a96f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
TOTPOP20VAP20
0171171
11132930
212071047
3926738
4371283
\n", + "
" + ], + "text/plain": [ + " TOTPOP20 VAP20\n", + "0 171 171\n", + "1 1132 930\n", + "2 1207 1047\n", + "3 926 738\n", + "4 371 283" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2020_df[pop_cols].head()" + ] + }, + { + "cell_type": "markdown", + "id": "36a553ac", + "metadata": {}, + "source": [ + "### Check that we didn't gain/lose any population in the aggregation step:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "4d6bbc16", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "TOTPOP20 715522\n", + "VAP20 581062\n", + "dtype: int64" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[pop_cols].sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "e575bd32", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "TOTPOP20 715522\n", + "VAP20 581062\n", + "dtype: int64" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2020_df[pop_cols].sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "1d9a3f5d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PRECIDSTATEFP20COUNTYFP20NAMECD18SUD18SLD18VTDST20NOTESPRES20DSEN20DPRES20RSEN20RgeometryTOTPOP20VAP20
0131051653008031Denver 53013105031530None73751612POLYGON ((3144474.619 1694874.799, 3144509.314...171171
1131061660508031Denver 60513106031605None720702110137POLYGON ((3162200.074 1691024.061, 3162164.358...1132930
2131061660608031Denver 60613106031606None832819107121POLYGON ((3162459.795 1693616.802, 3162459.063...12071047
3131061660708031Denver 60713106031607None597595102113POLYGON ((3165142.137 1693683.542, 3165141.880...926738
4131061660908031Denver 60913106031609None2412335971POLYGON ((3152968.867 1690916.131, 3153065.627...371283
...................................................
351133071674608031Denver 74613307031746None17681699386469POLYGON ((3172960.862 1716278.085, 3172953.814...32842196
352134051654008031Denver 54013405031540None15581477416489POLYGON ((3139192.261 1699587.973, 3139181.643...33663268
353134051654108031Denver 54113405031541None15541504246297POLYGON ((3148046.593 1705187.168, 3148047.303...27742643
354134051654208031Denver 54213405031542None881828200247POLYGON ((3133494.258 1698947.396, 3133494.426...19941830
355134051654308031Denver 54313405031543None8378369390POLYGON ((3140186.339 1689621.761, 3140186.210...16781367
\n", + "

356 rows × 16 columns

\n", + "
" + ], + "text/plain": [ + " PRECID STATEFP20 COUNTYFP20 NAME CD18 SUD18 SLD18 VTDST20 \\\n", + "0 1310516530 08 031 Denver 530 1 31 05 031530 \n", + "1 1310616605 08 031 Denver 605 1 31 06 031605 \n", + "2 1310616606 08 031 Denver 606 1 31 06 031606 \n", + "3 1310616607 08 031 Denver 607 1 31 06 031607 \n", + "4 1310616609 08 031 Denver 609 1 31 06 031609 \n", + ".. ... ... ... ... ... ... ... ... \n", + "351 1330716746 08 031 Denver 746 1 33 07 031746 \n", + "352 1340516540 08 031 Denver 540 1 34 05 031540 \n", + "353 1340516541 08 031 Denver 541 1 34 05 031541 \n", + "354 1340516542 08 031 Denver 542 1 34 05 031542 \n", + "355 1340516543 08 031 Denver 543 1 34 05 031543 \n", + "\n", + " NOTES PRES20D SEN20D PRES20R SEN20R \\\n", + "0 None 73 75 16 12 \n", + "1 None 720 702 110 137 \n", + "2 None 832 819 107 121 \n", + "3 None 597 595 102 113 \n", + "4 None 241 233 59 71 \n", + ".. ... ... ... ... ... \n", + "351 None 1768 1699 386 469 \n", + "352 None 1558 1477 416 489 \n", + "353 None 1554 1504 246 297 \n", + "354 None 881 828 200 247 \n", + "355 None 837 836 93 90 \n", + "\n", + " geometry TOTPOP20 VAP20 \n", + "0 POLYGON ((3144474.619 1694874.799, 3144509.314... 171 171 \n", + "1 POLYGON ((3162200.074 1691024.061, 3162164.358... 1132 930 \n", + "2 POLYGON ((3162459.795 1693616.802, 3162459.063... 1207 1047 \n", + "3 POLYGON ((3165142.137 1693683.542, 3165141.880... 926 738 \n", + "4 POLYGON ((3152968.867 1690916.131, 3153065.627... 371 283 \n", + ".. ... ... ... \n", + "351 POLYGON ((3172960.862 1716278.085, 3172953.814... 3284 2196 \n", + "352 POLYGON ((3139192.261 1699587.973, 3139181.643... 3366 3268 \n", + "353 POLYGON ((3148046.593 1705187.168, 3148047.303... 2774 2643 \n", + "354 POLYGON ((3133494.258 1698947.396, 3133494.426... 1994 1830 \n", + "355 POLYGON ((3140186.339 1689621.761, 3140186.210... 1678 1367 \n", + "\n", + "[356 rows x 16 columns]" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2020_df" + ] + }, + { + "cell_type": "markdown", + "id": "35bf6d3f", + "metadata": {}, + "source": [ + "### Next step: Disaggregate votes from all three precinct files to blocks, using Voting Age Population (VAP20) for the weights. " + ] + }, + { + "cell_type": "markdown", + "id": "c78b0a30", + "metadata": {}, + "source": [ + "### 2016:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "c3c840ad", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Index(['COUNTYFP', 'NAMELSAD', 'PRES16D', 'PRES16R', 'G16PRELJoh',\n", + " 'G16PREGSte', 'G16PREIMcM', 'G16PREOth', 'SEN16D', 'SEN16R',\n", + " 'G16USSLWil', 'G16USSGMen', 'G16USSOth', 'geometry'],\n", + " dtype='object')" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2016_df.columns" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "34f07c2b", + "metadata": {}, + "outputs": [], + "source": [ + "elec2016_cols = ['PRES16D', 'PRES16R', 'SEN16D', 'SEN16R']" + ] + }, + { + "cell_type": "markdown", + "id": "6b37197a", + "metadata": {}, + "source": [ + "### Now we assign a \"weight\" to each block that's equal to the fraction of the total population of ALL blocks assigned to the same precinct that's contained in that block.\n", + "### IMPORTANT: Some precincts have zero population, which leads to a zero denominator and an undefined weight for all blocks assigned to that precinct. We can solve this problem by replacing NaN values with zeros.\n", + "### Occasionally some zero-population precincts contain a small, nonzero number of votes, and these votes will be lost in the disaggregation from precincts down to blocks. " + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "e4694fc7", + "metadata": {}, + "outputs": [], + "source": [ + "weights2016 = blocks_df[\"VAP20\"] / blocks_to_precincts2016_assignment.map(blocks_df[\"VAP20\"].groupby(blocks_to_precincts2016_assignment).sum())\n", + "weights2016 = weights2016.fillna(0)" + ] + }, + { + "cell_type": "markdown", + "id": "97376acf", + "metadata": {}, + "source": [ + "### Here's the disaggregation step, using the \"prorate\" function:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "3fa669eb", + "metadata": {}, + "outputs": [], + "source": [ + "prorated2016 = maup.prorate(blocks_to_precincts2016_assignment, precincts2016_df[elec2016_cols], weights2016)\n", + "blocks_df[elec2016_cols] = prorated2016" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "d4e5120a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PRES16DPRES16RSEN16DSEN16R
00.0000000.0000000.0000000.000000
115.3982305.13982315.1008855.883186
210.4026858.01006711.1308728.197315
361.0108475.95728858.5457636.505085
417.1045581.57439716.4825742.313003
\n", + "
" + ], + "text/plain": [ + " PRES16D PRES16R SEN16D SEN16R\n", + "0 0.000000 0.000000 0.000000 0.000000\n", + "1 15.398230 5.139823 15.100885 5.883186\n", + "2 10.402685 8.010067 11.130872 8.197315\n", + "3 61.010847 5.957288 58.545763 6.505085\n", + "4 17.104558 1.574397 16.482574 2.313003" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[elec2016_cols].head()" + ] + }, + { + "cell_type": "markdown", + "id": "b048f421", + "metadata": {}, + "source": [ + "### Check to see whether we gained/lost any votes:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "ede614d4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "PRES16D 244551\n", + "PRES16R 62690\n", + "SEN16D 238774\n", + "SEN16R 71078\n", + "dtype: int64" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2016_df[elec2016_cols].sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "b1b1bc43", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "PRES16D 244551.0\n", + "PRES16R 62690.0\n", + "SEN16D 238774.0\n", + "SEN16R 71078.0\n", + "dtype: float64" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[elec2016_cols].sum()" + ] + }, + { + "cell_type": "markdown", + "id": "adfe2fa6", + "metadata": {}, + "source": [ + "### 2018:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "ff59b2e4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Index(['COUNTYFP', 'VTDST', 'NAME', 'CD116FP', 'SLDUST', 'SLDLST', 'PRECID',\n", + " 'AG18D', 'AG18R', 'SOS18D', 'SOS18R', 'TRE18D', 'TRE18R', 'GOV18D',\n", + " 'GOV18R', 'REG18D', 'REG18R', 'USH18D', 'USH18R', 'TOTPOP', 'NH_WHITE',\n", + " 'NH_BLACK', 'NH_AMIN', 'NH_ASIAN', 'NH_NHPI', 'NH_OTHER', 'NH_2MORE',\n", + " 'HISP', 'H_WHITE', 'H_BLACK', 'H_AMIN', 'H_ASIAN', 'H_NHPI', 'H_OTHER',\n", + " 'H_2MORE', 'VAP', 'HVAP', 'WVAP', 'BVAP', 'AMINVAP', 'ASIANVAP',\n", + " 'NHPIVAP', 'OTHERVAP', '2MOREVAP', 'geometry'],\n", + " dtype='object')" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2018_df.columns" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "5cc8bbe8", + "metadata": {}, + "outputs": [], + "source": [ + "elec2018_cols = ['AG18D', 'AG18R', 'SOS18D', 'SOS18R', 'TRE18D', 'TRE18R', 'GOV18D', 'GOV18R', 'REG18D', 'REG18R']" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "6189e09e", + "metadata": {}, + "outputs": [], + "source": [ + "weights2018 = blocks_df[\"VAP20\"] / blocks_to_precincts2018_assignment.map(blocks_df[\"VAP20\"].groupby(blocks_to_precincts2018_assignment).sum())\n", + "weights2018 = weights2018.fillna(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "ec05ed4c", + "metadata": {}, + "outputs": [], + "source": [ + "prorated2018 = maup.prorate(blocks_to_precincts2018_assignment, precincts2018_df[elec2018_cols], weights2018)\n", + "blocks_df[elec2018_cols] = prorated2018" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "339eefea", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
AG18DAG18RSOS18DSOS18RTRE18DTRE18RGOV18DGOV18RREG18DREG18R
00.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.000000
114.3787616.22300913.6141597.00885013.7415936.43539814.3575226.22300913.4654875.883186
29.8617457.53154410.4442957.05302010.4234906.96979910.0697997.07382610.0281886.449664
359.7783055.27254258.0664417.12135658.8196616.43661061.1477974.38237357.5871194.519322
417.9403492.02144817.4738612.46849917.4155502.54624718.3873991.82707817.0073731.768767
\n", + "
" + ], + "text/plain": [ + " AG18D AG18R SOS18D SOS18R TRE18D TRE18R GOV18D \\\n", + "0 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 \n", + "1 14.378761 6.223009 13.614159 7.008850 13.741593 6.435398 14.357522 \n", + "2 9.861745 7.531544 10.444295 7.053020 10.423490 6.969799 10.069799 \n", + "3 59.778305 5.272542 58.066441 7.121356 58.819661 6.436610 61.147797 \n", + "4 17.940349 2.021448 17.473861 2.468499 17.415550 2.546247 18.387399 \n", + "\n", + " GOV18R REG18D REG18R \n", + "0 0.000000 0.000000 0.000000 \n", + "1 6.223009 13.465487 5.883186 \n", + "2 7.073826 10.028188 6.449664 \n", + "3 4.382373 57.587119 4.519322 \n", + "4 1.827078 17.007373 1.768767 " + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[elec2018_cols].head()" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "244b1927", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AG18D 232798\n", + "AG18R 64532\n", + "SOS18D 232255\n", + "SOS18R 67147\n", + "TRE18D 230382\n", + "TRE18R 66728\n", + "GOV18D 238762\n", + "GOV18R 60151\n", + "REG18D 223947\n", + "REG18R 57322\n", + "dtype: int64" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2018_df[elec2018_cols].sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "8d8bf1bc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AG18D 232798.0\n", + "AG18R 64532.0\n", + "SOS18D 232255.0\n", + "SOS18R 67147.0\n", + "TRE18D 230382.0\n", + "TRE18R 66728.0\n", + "GOV18D 238762.0\n", + "GOV18R 60151.0\n", + "REG18D 223947.0\n", + "REG18R 57322.0\n", + "dtype: float64" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[elec2018_cols].sum()" + ] + }, + { + "cell_type": "markdown", + "id": "45e4bc1b", + "metadata": {}, + "source": [ + "### If the goal is just to put all the data on 2020 precincts, we don't really have to disaggregate 2020 election data to blocks - but we might also want all the election data on blocks, so we'll go ahead and do it for completeness." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "b75e9d3c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Index(['PRECID', 'STATEFP20', 'COUNTYFP20', 'NAME', 'CD18', 'SUD18', 'SLD18',\n", + " 'VTDST20', 'NOTES', 'PRES20D', 'SEN20D', 'PRES20R', 'SEN20R',\n", + " 'geometry', 'TOTPOP20', 'VAP20'],\n", + " dtype='object')" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2020_df.columns" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "fe04f13c", + "metadata": {}, + "outputs": [], + "source": [ + "elec2020_cols = ['PRES20D', 'SEN20D', 'PRES20R', 'SEN20R']" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "735860ac", + "metadata": {}, + "outputs": [], + "source": [ + "weights2020 = blocks_df[\"VAP20\"] / blocks_to_precincts2020_assignment.map(blocks_df[\"VAP20\"].groupby(blocks_to_precincts2020_assignment).sum())\n", + "weights2020 = weights2020.fillna(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "1ace13c7", + "metadata": {}, + "outputs": [], + "source": [ + "prorated2020 = maup.prorate(blocks_to_precincts2020_assignment, precincts2020_df[elec2020_cols], weights2020)\n", + "blocks_df[elec2020_cols] = prorated2020" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "fdfe9942", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PRES20DSEN20DPRES20RSEN20R
00.0000000.0000000.0000000.000000
117.33097316.5663725.4371686.711504
213.89798713.6899338.4261748.863087
369.02237367.5844075.4094926.847458
421.67225220.9142091.7298932.468499
\n", + "
" + ], + "text/plain": [ + " PRES20D SEN20D PRES20R SEN20R\n", + "0 0.000000 0.000000 0.000000 0.000000\n", + "1 17.330973 16.566372 5.437168 6.711504\n", + "2 13.897987 13.689933 8.426174 8.863087\n", + "3 69.022373 67.584407 5.409492 6.847458\n", + "4 21.672252 20.914209 1.729893 2.468499" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[elec2020_cols].head()" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "0c02e6de", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "PRES20D 313293\n", + "SEN20D 305602\n", + "PRES20R 71618\n", + "SEN20R 80163\n", + "dtype: int64" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2020_df[elec2020_cols].sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "530aa5d6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "PRES20D 313293.0\n", + "SEN20D 305602.0\n", + "PRES20R 71618.0\n", + "SEN20R 80163.0\n", + "dtype: float64" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[elec2020_cols].sum()" + ] + }, + { + "cell_type": "markdown", + "id": "4bfcf5fb", + "metadata": {}, + "source": [ + "### Last step: Aggregate 2016 and 2018 election data up from blocks to 2020 precincts." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "22c17f41", + "metadata": {}, + "outputs": [], + "source": [ + "precincts2020_df[elec2016_cols] = blocks_df[elec2016_cols].groupby(blocks_to_precincts2020_assignment).sum()\n", + "precincts2020_df[elec2018_cols] = blocks_df[elec2018_cols].groupby(blocks_to_precincts2020_assignment).sum()" + ] + }, + { + "cell_type": "markdown", + "id": "df0347e2", + "metadata": {}, + "source": [ + "### Check to see whether we gained/lost any votes:" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "c5555a15", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "PRES16D 244551.0\n", + "PRES16R 62690.0\n", + "SEN16D 238774.0\n", + "SEN16R 71078.0\n", + "dtype: float64" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[elec2016_cols].sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "4e2ce9ec", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "PRES16D 244551.0\n", + "PRES16R 62690.0\n", + "SEN16D 238774.0\n", + "SEN16R 71078.0\n", + "dtype: float64" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2020_df[elec2016_cols].sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "c6b8d1c7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AG18D 232798.0\n", + "AG18R 64532.0\n", + "SOS18D 232255.0\n", + "SOS18R 67147.0\n", + "TRE18D 230382.0\n", + "TRE18R 66728.0\n", + "GOV18D 238762.0\n", + "GOV18R 60151.0\n", + "REG18D 223947.0\n", + "REG18R 57322.0\n", + "dtype: float64" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[elec2018_cols].sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "89074ca5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AG18D 232798.0\n", + "AG18R 64532.0\n", + "SOS18D 232255.0\n", + "SOS18R 67147.0\n", + "TRE18D 230382.0\n", + "TRE18R 66728.0\n", + "GOV18D 238762.0\n", + "GOV18R 60151.0\n", + "REG18D 223947.0\n", + "REG18R 57322.0\n", + "dtype: float64" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2020_df[elec2018_cols].sum()" + ] + }, + { + "cell_type": "markdown", + "id": "03e0cb1a", + "metadata": {}, + "source": [ + "### Success! Now we can save these shapefiles for later use:" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "dfab9d9e", + "metadata": {}, + "outputs": [], + "source": [ + "# blocks_df.to_file(\"./Shapefiles/DenverCo_blocks_with_data/DenverCo_blocks_with_data.shp\")\n", + "# precincts2020_df.to_file(\"./Shapefiles/DenverCo_precincts2020_with_data/DenverCo_precincts2020_with_data.shp\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11ab3377", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "c1d501b5", + "metadata": {}, + "source": [ + "### Now let's talk about potential problems! \n", + "### We started with \"cleaned-up\" versions of the precinct shapefiles. The \"doctor\" function is used to evaluate shapefiles for topological problems such as gaps and overlaps." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "01e1d0e2", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|████████████████████████████████████████| 356/356 [00:00<00:00, 962.45it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 10 holes.\n" + ] + }, + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "maup.doctor(precincts2020_df)" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "f40b7134", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|███████████████████████████████████████| 356/356 [00:00<00:00, 1036.65it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 10 holes.\n" + ] + }, + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "maup.doctor(precincts2018_df)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "40f79791", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|███████████████████████████████████████| 346/346 [00:00<00:00, 1024.45it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 10 holes.\n" + ] + }, + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "maup.doctor(precincts2016_df)" + ] + }, + { + "cell_type": "markdown", + "id": "97281549", + "metadata": {}, + "source": [ + "### These holes are actually \"real\" because Denver County is not simply connected; there are \"islands\" that belong to Arapahoe County. So these holes are not indicative of problems." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "f7fb0e39", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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++yz6txzz1U33nhjeQdZZoca80033aTOPvvsCoyscg415osvvlhdd911E2677LLL1Oc///lyDbEiDjRupZQqFAoqFAqpX/ziF0oppWzbVg0NDeqee+4pXTMyMqI8Ho/61a9+VZHxlsOhxLw3S5cuVYDavHlzuYZYduWIedu2baq5uVmtXr36gH/HHw9khuMwsyyLRx99lGQyyYIFC8p2v6OjowDEYrGy3We5lDPmxYsXc/HFF3PBBReUaXSVUa6Yn3zySebOncvnPvc56urqmD17Nj/96U/LONLyKVfMZ511Fn/+859Zt24dAG+++SYvvfQSF110UbmGWlYfJe5UKkU+ny/9f924cSO9vb0TnteRSIQzzjiDV199tSLjPhTliHlvRkdH0TSNqqqqMo20fMoVs23bXH311XzjG9/glFNOqdRwj0rHTfO2o93bb7/NggULyGQyBINBnnjiCU4++eSy3Ldt23zlK19h4cKFzJw5syz3WQ7ljvnRRx9l5cqVLFu2rIyjLK9yx9zd3c0DDzzA1772Nf71X/+VZcuWccMNN+B2u1m0aFEZR/7RlTvmf/mXfyEejzNjxgwMw8CyLG6//XY+//nPl3HUh+5Q4r7ppptoamoqJRi9vb0A1NfXT7iuvr6+9LmjQTlj/qBMJsNNN93ElVdeeVQ1PSt3zHfddRemaXLDDTdUashHLUk4DpPp06ezatUqRkdHefzxx1m0aBEvvPBCWZKOxYsXs3r1al566aUyjLR8yhnz1q1bufHGG3n22Wfxer0VGG15lPtxtm2buXPncscddwAwe/ZsVq9ezY9//OOjJuEod8y//vWv+eUvf8mSJUs45ZRTWLVqFV/5yldoamo6amKGjx73nXfeyaOPPsrzzz9/VD+X96ZSMefzea644gqUUjzwwAOVGv5HUs6YV6xYwfe//31WrlyJpmmHY/hHlyO9pnOiOv/889UXvvCF/V63v/W9xYsXq5aWFtXd3V3G0VXGocT8xBNPKEAZhlH6AJSmacowDFUoFCo06kNzqI9zW1ub+sd//McJt/3oRz9STU1N5Rpi2R1qzC0tLeqHP/zhhNtuvfVWNX369HINsSIOJO577rlHRSIRtWzZsgm3b9iwQQHqjTfemHD7xz72MXXDDTeUe6hlcygxj8vlcurSSy9VXV1damBgoBLDLKtDifl73/te6XfW7r/HdF1X7e3tFRz10UFmOI4Q27bJZrMf+euVUlx//fU88cQTPP/883R2dpZxdJVxKDGff/75vP322xNuu/baa5kxYwY33XQThmGUY4hld6iP88KFC/c47rxu3Tra29sPdWgVc6gxp1IpdH3i9jLDMLBt+1CHVlH7i/vuu+/m9ttv5+mnn2bu3LkTPtfZ2UlDQwN//vOfOe200wCIx+O8/vrrfPGLX6zksA/JocQMu2Y21q9fz3PPPUd1dXUlh1sWhxLz1VdfvceS0ic/+Umuvvpqrr322oqM92giCcdh8M1vfpOLLrqItrY2EokES5Ys4fnnn+fpp58G4JprrqG5uZnvfOc7AORyOd59993S33t6eli1ahXBYJApU6YAzjLKkiVL+N3vfkcoFCqt80YiEXw+3xGIcqJyxxwKhfbYnxIIBKiurj5q9q1U4nH+6le/yllnncUdd9zBFVdcwdKlS3nwwQd58MEHj0yQH1CJmC+55BJuv/122traOOWUU3jjjTe47777uO66645MkHtxsHHfddddfOtb32LJkiV0dHSU/r8Gg0GCwWCppsxtt93G1KlT6ezs5JZbbqGpqYlLL730SIU5QbljzufzXH755axcuZI//OEPWJZVuiYWi+F2u49MoLspd8zV1dV7JFUul4uGhgamT59+eIM7Eo70FMuJ4LrrrlPt7e3K7Xar2tpadf7556tnnnmm9Plzzz1XLVq0qPTvjRs3KmCPj3PPPbd0zd4+D6iHHnro8AW2D5WI+YOOtmOxlYr597//vZo5c6byeDxqxowZ6sEHHzxMEe1fJWKOx+PqxhtvVG1tbcrr9apJkyapm2++WWWz2cMY2b4dbNzt7e17jfvb3/526RrbttUtt9yi6uvrlcfjUeeff75au3btYYxq38od84c9FwD13HPPHd7gPkQlHucPOpGOxUp7eiGEEEJUnNThEEIIIUTFScIhhBBCiIqThEMIIYQQFScJhxBCCCEqThIOIYQQQlScJBxCCCGEqDhJOIQQQghRcZJwCCGEEKLiJOEQQgghjmMvvvgil1xyCU1NTWiaxm9/+9uDvg+lFN/97neZNm0aHo+H5uZmbr/99oO6D+mlIoQQQhzHkskkp556Ktdddx2XXXbZR7qPG2+8kWeeeYbvfve7zJo1i6GhIYaGhg7qPqS0uRBCCHGC0DSNJ554YkJTwGw2y80338yvfvUrRkZGmDlzJnfddRcf//jHAVizZg1dXV2sXr36kJrMyZKKEEIIcQL78pe/zKuvvsqjjz7KW2+9xec+9zk+9alPsX79egB+//vfM2nSJP7whz/Q2dlJR0cH//RP/3TQMxyScAghhBAnqC1btvDQQw/x2GOPcc455zB58mS+/vWvc/bZZ/PQQw8B0N3dzebNm3nsscd45JFHePjhh1mxYgWXX375QX0v2cMhhBBCnKDefvttLMti2rRpE27PZrNUV1cDYNs22WyWRx55pHTdz372M+bMmcPatWsPeJlFEg4hhBDiBDU2NoZhGKxYsQLDMCZ8LhgMAtDY2IhpmhOSkpNOOglwZkgk4RBCCCHEPs2ePRvLsujr6+Occ87Z6zULFy6kUCiwYcMGJk+eDMC6desAaG9vP+DvJadUhBBCiOPY2NgY77//PuAkGPfddx/nnXcesViMtrY2rrrqKl5++WXuvfdeZs+eTX9/P3/+85/p6uri4osvxrZt5s2bRzAY5P7778e2bRYvXkw4HOaZZ5454HFIwiGEEEIcx55//nnOO++8PW5ftGgRDz/8MPl8nttuu41HHnmEnp4eampqOPPMM/m3f/s3Zs2aBcD27du5/vrreeaZZwgEAlx00UXce++9xGKxAx6HJBxCCCGEqDg5FiuEEEKIipOEQwghhBAVJwmHEEIIISpOEg4hhBBCVJwkHEIIIYSoOEk4hBBCCFFxknAIIYQQouIk4RBCCCFExUnCIYQQQoiKk4RDCCGEEBUnCYcQQgghKu7/B5BLeRQ0750tAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "blocks_df.plot()" + ] + }, + { + "cell_type": "markdown", + "id": "25b38d08", + "metadata": {}, + "source": [ + "### But here are the \"original\" precinct files, extracted from statewide Colorado precinct files.\n", + "### (Sources: 2016 precinct file was compiled by VEST; 2018 precinct file was compiled by Haley Colgate with assistance from Todd Blees of the Colorado State Demographer's office, and 2020 file was compiled by Louis Pino of the Colorado State Legislative staff.)" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "8c6da770", + "metadata": {}, + "outputs": [], + "source": [ + "precincts2016_orig_df = gpd.read_file(\"../examples/Shapefiles/DenverCo_precincts2016_orig/DenverCo_precincts2016_orig.shp\")\n", + "precincts2018_orig_df = gpd.read_file(\"../examples/Shapefiles/DenverCo_precincts2018_orig/DenverCo_precincts2018_orig.shp\")\n", + "precincts2020_orig_df = gpd.read_file(\"../examples/Shapefiles/DenverCo_precincts2020_orig/DenverCo_precincts2020_orig.shp\")" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "6d4973d1", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|████████████████████████████████████████| 356/356 [00:00<00:00, 826.87it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 27 overlaps.\n", + "There are 33 holes.\n" + ] + }, + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "maup.doctor(precincts2020_orig_df)" + ] + }, + { + "cell_type": "markdown", + "id": "57ae1c50", + "metadata": {}, + "source": [ + "### When we assigned blocks to precincts above, every block was assigned to a precinct; we can confirm this by checking for unassigned blocks:" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "8cb2a3ee", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "0\n", + "0\n" + ] + } + ], + "source": [ + "print(len(blocks_df[blocks_to_precincts2020_assignment.isna()]))\n", + "print(len(blocks_df[blocks_to_precincts2018_assignment.isna()]))\n", + "print(len(blocks_df[blocks_to_precincts2016_assignment.isna()]))" + ] + }, + { + "cell_type": "markdown", + "id": "5726be17", + "metadata": {}, + "source": [ + "### But what if we assign blocks to the original versions?" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "812cf3ef", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|████████████████████████████████████████| 356/356 [00:00<00:00, 768.50it/s]\n", + "100%|████████████████████████████████████████| 356/356 [00:00<00:00, 358.08it/s]\n", + "100%|████████████████████████████████████████| 356/356 [00:00<00:00, 832.96it/s]\n", + "100%|████████████████████████████████████████| 356/356 [00:01<00:00, 302.34it/s]\n", + "100%|████████████████████████████████████████| 346/346 [00:00<00:00, 379.30it/s]\n", + "100%|███████████████████████████████████████| 346/346 [00:00<00:00, 1501.18it/s]\n" + ] + } + ], + "source": [ + "blocks_to_precincts2020_orig_assignment = maup.assign(blocks_df.geometry, precincts2020_orig_df.geometry)\n", + "blocks_to_precincts2018_orig_assignment = maup.assign(blocks_df.geometry, precincts2018_orig_df.geometry)\n", + "blocks_to_precincts2016_orig_assignment = maup.assign(blocks_df.geometry, precincts2016_orig_df.geometry)" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "bbf676c1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3\n", + "1\n", + "5\n" + ] + } + ], + "source": [ + "print(len(blocks_df[blocks_to_precincts2020_orig_assignment.isna()]))\n", + "print(len(blocks_df[blocks_to_precincts2018_orig_assignment.isna()]))\n", + "print(len(blocks_df[blocks_to_precincts2016_orig_assignment.isna()]))" + ] + }, + { + "cell_type": "markdown", + "id": "72389fe9", + "metadata": {}, + "source": [ + "### So they all missed a few!" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "91f7c456", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
STATEFP20COUNTYFP20TRACTCE20BLOCKCE20GEOID20NAME20CD116SLDL20SLDU20CD118SLDL22SLDU22MTFCC20FUNCSTAT20ALAND20AWATER20INTPTLAT20INTPTLON20TOTPOP20WHITE20BLACK20AMIN20ASIAN20NHPI20OTHER202MORE20HISP20NH_WHITE20NH_BLACK20NH_AMIN20NH_ASIAN20NH_NHPI20NH_OTHER20NH_2MORE20VAP20HVAP20WVAP20BVAP20AMINVAP20ASIANVAP20NHPIVAP20OTHERVAP202MOREVAP20H_WHITE20H_BLACK20H_AMIN20H_ASIAN20H_NHPI20H_OTHER20H_2MORE20TOTPOP20RHISP20RNHWHITE20RNHBLACK20RNHAMIN20RNHASIAN20RNH_NHPI20RNHOTHER20RNH2MORE20RWHITE20RBLACK20RAMIN20RASIAN20RNHPI20ROTHER20R2MORE20RgeometryPRES16DPRES16RSEN16DSEN16RAG18DAG18RSOS18DSOS18RTRE18DTRE18RGOV18DGOV18RREG18DREG18RPRES20DSEN20DPRES20RSEN20R
2896080319800011037080319800011037Block 103717331733G5040S211325620+39.8260443-104.6282154000000000000000000000000000000000000000000000000POLYGON ((3244624.987 1726684.593, 3244656.485...0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
6683080319800011039080319800011039Block 103917331733G5040S705520+39.8245484-104.6206486000000000000000000000000000000000000000000000000POLYGON ((3246737.729 1727192.367, 3247192.322...0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
8656080319800011038080319800011038Block 103817331733G5040S2373580+39.8247377-104.6244984000000000000000000000000000000000000000000000000POLYGON ((3244825.443 1726402.364, 3244867.095...0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
\n", + "
" + ], + "text/plain": [ + " STATEFP20 COUNTYFP20 TRACTCE20 BLOCKCE20 GEOID20 NAME20 \\\n", + "2896 08 031 980001 1037 080319800011037 Block 1037 \n", + "6683 08 031 980001 1039 080319800011039 Block 1039 \n", + "8656 08 031 980001 1038 080319800011038 Block 1038 \n", + "\n", + " CD116 SLDL20 SLDU20 CD118 SLDL22 SLDU22 MTFCC20 FUNCSTAT20 \\\n", + "2896 1 7 33 1 7 33 G5040 S \n", + "6683 1 7 33 1 7 33 G5040 S \n", + "8656 1 7 33 1 7 33 G5040 S \n", + "\n", + " ALAND20 AWATER20 INTPTLAT20 INTPTLON20 TOTPOP20 WHITE20 \\\n", + "2896 2113 25620 +39.8260443 -104.6282154 0 0 \n", + "6683 70552 0 +39.8245484 -104.6206486 0 0 \n", + "8656 237358 0 +39.8247377 -104.6244984 0 0 \n", + "\n", + " BLACK20 AMIN20 ASIAN20 NHPI20 OTHER20 2MORE20 HISP20 NH_WHITE20 \\\n", + "2896 0 0 0 0 0 0 0 0 \n", + "6683 0 0 0 0 0 0 0 0 \n", + "8656 0 0 0 0 0 0 0 0 \n", + "\n", + " NH_BLACK20 NH_AMIN20 NH_ASIAN20 NH_NHPI20 NH_OTHER20 NH_2MORE20 \\\n", + "2896 0 0 0 0 0 0 \n", + "6683 0 0 0 0 0 0 \n", + "8656 0 0 0 0 0 0 \n", + "\n", + " VAP20 HVAP20 WVAP20 BVAP20 AMINVAP20 ASIANVAP20 NHPIVAP20 \\\n", + "2896 0 0 0 0 0 0 0 \n", + "6683 0 0 0 0 0 0 0 \n", + "8656 0 0 0 0 0 0 0 \n", + "\n", + " OTHERVAP20 2MOREVAP20 H_WHITE20 H_BLACK20 H_AMIN20 H_ASIAN20 \\\n", + "2896 0 0 0 0 0 0 \n", + "6683 0 0 0 0 0 0 \n", + "8656 0 0 0 0 0 0 \n", + "\n", + " H_NHPI20 H_OTHER20 H_2MORE20 TOTPOP20R HISP20R NHWHITE20R \\\n", + "2896 0 0 0 0 0 0 \n", + "6683 0 0 0 0 0 0 \n", + "8656 0 0 0 0 0 0 \n", + "\n", + " NHBLACK20R NHAMIN20R NHASIAN20R NH_NHPI20R NHOTHER20R NH2MORE20R \\\n", + "2896 0 0 0 0 0 0 \n", + "6683 0 0 0 0 0 0 \n", + "8656 0 0 0 0 0 0 \n", + "\n", + " WHITE20R BLACK20R AMIN20R ASIAN20R NHPI20R OTHER20R 2MORE20R \\\n", + "2896 0 0 0 0 0 0 0 \n", + "6683 0 0 0 0 0 0 0 \n", + "8656 0 0 0 0 0 0 0 \n", + "\n", + " geometry PRES16D PRES16R \\\n", + "2896 POLYGON ((3244624.987 1726684.593, 3244656.485... 0.0 0.0 \n", + "6683 POLYGON ((3246737.729 1727192.367, 3247192.322... 0.0 0.0 \n", + "8656 POLYGON ((3244825.443 1726402.364, 3244867.095... 0.0 0.0 \n", + "\n", + " SEN16D SEN16R AG18D AG18R SOS18D SOS18R TRE18D TRE18R GOV18D \\\n", + "2896 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "6683 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "8656 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "\n", + " GOV18R REG18D REG18R PRES20D SEN20D PRES20R SEN20R \n", + "2896 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "6683 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "8656 0.0 0.0 0.0 0.0 0.0 0.0 0.0 " + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[blocks_to_precincts2020_orig_assignment.isna()]" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "4689ef17", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
STATEFP20COUNTYFP20TRACTCE20BLOCKCE20GEOID20NAME20CD116SLDL20SLDU20CD118SLDL22SLDU22MTFCC20FUNCSTAT20ALAND20AWATER20INTPTLAT20INTPTLON20TOTPOP20WHITE20BLACK20AMIN20ASIAN20NHPI20OTHER202MORE20HISP20NH_WHITE20NH_BLACK20NH_AMIN20NH_ASIAN20NH_NHPI20NH_OTHER20NH_2MORE20VAP20HVAP20WVAP20BVAP20AMINVAP20ASIANVAP20NHPIVAP20OTHERVAP202MOREVAP20H_WHITE20H_BLACK20H_AMIN20H_ASIAN20H_NHPI20H_OTHER20H_2MORE20TOTPOP20RHISP20RNHWHITE20RNHBLACK20RNHAMIN20RNHASIAN20RNH_NHPI20RNHOTHER20RNH2MORE20RWHITE20RBLACK20RAMIN20RASIAN20RNHPI20ROTHER20R2MORE20RgeometryPRES16DPRES16RSEN16DSEN16RAG18DAG18RSOS18DSOS18RTRE18DTRE18RGOV18DGOV18RREG18DREG18RPRES20DSEN20DPRES20RSEN20R
9812080310083882009080310083882009Block 200917331733G5040S16110+39.7728637-104.8004050000000000000000000000000000000000000000000000000POLYGON ((3196304.708 1707166.383, 3197014.777...0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
\n", + "
" + ], + "text/plain": [ + " STATEFP20 COUNTYFP20 TRACTCE20 BLOCKCE20 GEOID20 NAME20 \\\n", + "9812 08 031 008388 2009 080310083882009 Block 2009 \n", + "\n", + " CD116 SLDL20 SLDU20 CD118 SLDL22 SLDU22 MTFCC20 FUNCSTAT20 \\\n", + "9812 1 7 33 1 7 33 G5040 S \n", + "\n", + " ALAND20 AWATER20 INTPTLAT20 INTPTLON20 TOTPOP20 WHITE20 \\\n", + "9812 1611 0 +39.7728637 -104.8004050 0 0 \n", + "\n", + " BLACK20 AMIN20 ASIAN20 NHPI20 OTHER20 2MORE20 HISP20 NH_WHITE20 \\\n", + "9812 0 0 0 0 0 0 0 0 \n", + "\n", + " NH_BLACK20 NH_AMIN20 NH_ASIAN20 NH_NHPI20 NH_OTHER20 NH_2MORE20 \\\n", + "9812 0 0 0 0 0 0 \n", + "\n", + " VAP20 HVAP20 WVAP20 BVAP20 AMINVAP20 ASIANVAP20 NHPIVAP20 \\\n", + "9812 0 0 0 0 0 0 0 \n", + "\n", + " OTHERVAP20 2MOREVAP20 H_WHITE20 H_BLACK20 H_AMIN20 H_ASIAN20 \\\n", + "9812 0 0 0 0 0 0 \n", + "\n", + " H_NHPI20 H_OTHER20 H_2MORE20 TOTPOP20R HISP20R NHWHITE20R \\\n", + "9812 0 0 0 0 0 0 \n", + "\n", + " NHBLACK20R NHAMIN20R NHASIAN20R NH_NHPI20R NHOTHER20R NH2MORE20R \\\n", + "9812 0 0 0 0 0 0 \n", + "\n", + " WHITE20R BLACK20R AMIN20R ASIAN20R NHPI20R OTHER20R 2MORE20R \\\n", + "9812 0 0 0 0 0 0 0 \n", + "\n", + " geometry PRES16D PRES16R \\\n", + "9812 POLYGON ((3196304.708 1707166.383, 3197014.777... 0.0 0.0 \n", + "\n", + " SEN16D SEN16R AG18D AG18R SOS18D SOS18R TRE18D TRE18R GOV18D \\\n", + "9812 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "\n", + " GOV18R REG18D REG18R PRES20D SEN20D PRES20R SEN20R \n", + "9812 0.0 0.0 0.0 0.0 0.0 0.0 0.0 " + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[blocks_to_precincts2018_orig_assignment.isna()]" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "96ab7a1e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
STATEFP20COUNTYFP20TRACTCE20BLOCKCE20GEOID20NAME20CD116SLDL20SLDU20CD118SLDL22SLDU22MTFCC20FUNCSTAT20ALAND20AWATER20INTPTLAT20INTPTLON20TOTPOP20WHITE20BLACK20AMIN20ASIAN20NHPI20OTHER202MORE20HISP20NH_WHITE20NH_BLACK20NH_AMIN20NH_ASIAN20NH_NHPI20NH_OTHER20NH_2MORE20VAP20HVAP20WVAP20BVAP20AMINVAP20ASIANVAP20NHPIVAP20OTHERVAP202MOREVAP20H_WHITE20H_BLACK20H_AMIN20H_ASIAN20H_NHPI20H_OTHER20H_2MORE20TOTPOP20RHISP20RNHWHITE20RNHBLACK20RNHAMIN20RNHASIAN20RNH_NHPI20RNHOTHER20RNH2MORE20RWHITE20RBLACK20RAMIN20RASIAN20RNHPI20ROTHER20R2MORE20RgeometryPRES16DPRES16RSEN16DSEN16RAG18DAG18RSOS18DSOS18RTRE18DTRE18RGOV18DGOV18RREG18DREG18RPRES20DSEN20DPRES20RSEN20R
1997080310083887008080310083887008Block 700817331733G5040S102390+39.7729509-104.8078405000000000000000000000000000000000000000000000000POLYGON ((3193984.432 1707215.315, 3193983.602...0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
2592080310083887007080310083887007Block 700717331733G5040S14660+39.7731435-104.8088405000000000000000000000000000000000000000000000000POLYGON ((3194032.231 1707250.650, 3194531.988...0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
2729080310156002004080310156002004Block 200412321132G5040S30492100+39.6658476-105.0041615000000000000000000000000000000000000000000000000POLYGON ((3139496.394 1667748.388, 3139520.253...0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
8262080310083887009080310083887009Block 700917331733G5040S62600+39.7729837-104.8051077000000000000000000000000000000000000000000000000POLYGON ((3194531.881 1707232.230, 3194531.988...0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
9812080310083882009080310083882009Block 200917331733G5040S16110+39.7728637-104.8004050000000000000000000000000000000000000000000000000POLYGON ((3196304.708 1707166.383, 3197014.777...0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
\n", + "
" + ], + "text/plain": [ + " STATEFP20 COUNTYFP20 TRACTCE20 BLOCKCE20 GEOID20 NAME20 \\\n", + "1997 08 031 008388 7008 080310083887008 Block 7008 \n", + "2592 08 031 008388 7007 080310083887007 Block 7007 \n", + "2729 08 031 015600 2004 080310156002004 Block 2004 \n", + "8262 08 031 008388 7009 080310083887009 Block 7009 \n", + "9812 08 031 008388 2009 080310083882009 Block 2009 \n", + "\n", + " CD116 SLDL20 SLDU20 CD118 SLDL22 SLDU22 MTFCC20 FUNCSTAT20 \\\n", + "1997 1 7 33 1 7 33 G5040 S \n", + "2592 1 7 33 1 7 33 G5040 S \n", + "2729 1 2 32 1 1 32 G5040 S \n", + "8262 1 7 33 1 7 33 G5040 S \n", + "9812 1 7 33 1 7 33 G5040 S \n", + "\n", + " ALAND20 AWATER20 INTPTLAT20 INTPTLON20 TOTPOP20 WHITE20 \\\n", + "1997 10239 0 +39.7729509 -104.8078405 0 0 \n", + "2592 1466 0 +39.7731435 -104.8088405 0 0 \n", + "2729 3049 2100 +39.6658476 -105.0041615 0 0 \n", + "8262 6260 0 +39.7729837 -104.8051077 0 0 \n", + "9812 1611 0 +39.7728637 -104.8004050 0 0 \n", + "\n", + " BLACK20 AMIN20 ASIAN20 NHPI20 OTHER20 2MORE20 HISP20 NH_WHITE20 \\\n", + "1997 0 0 0 0 0 0 0 0 \n", + "2592 0 0 0 0 0 0 0 0 \n", + "2729 0 0 0 0 0 0 0 0 \n", + "8262 0 0 0 0 0 0 0 0 \n", + "9812 0 0 0 0 0 0 0 0 \n", + "\n", + " NH_BLACK20 NH_AMIN20 NH_ASIAN20 NH_NHPI20 NH_OTHER20 NH_2MORE20 \\\n", + "1997 0 0 0 0 0 0 \n", + "2592 0 0 0 0 0 0 \n", + "2729 0 0 0 0 0 0 \n", + "8262 0 0 0 0 0 0 \n", + "9812 0 0 0 0 0 0 \n", + "\n", + " VAP20 HVAP20 WVAP20 BVAP20 AMINVAP20 ASIANVAP20 NHPIVAP20 \\\n", + "1997 0 0 0 0 0 0 0 \n", + "2592 0 0 0 0 0 0 0 \n", + "2729 0 0 0 0 0 0 0 \n", + "8262 0 0 0 0 0 0 0 \n", + "9812 0 0 0 0 0 0 0 \n", + "\n", + " OTHERVAP20 2MOREVAP20 H_WHITE20 H_BLACK20 H_AMIN20 H_ASIAN20 \\\n", + "1997 0 0 0 0 0 0 \n", + "2592 0 0 0 0 0 0 \n", + "2729 0 0 0 0 0 0 \n", + "8262 0 0 0 0 0 0 \n", + "9812 0 0 0 0 0 0 \n", + "\n", + " H_NHPI20 H_OTHER20 H_2MORE20 TOTPOP20R HISP20R NHWHITE20R \\\n", + "1997 0 0 0 0 0 0 \n", + "2592 0 0 0 0 0 0 \n", + "2729 0 0 0 0 0 0 \n", + "8262 0 0 0 0 0 0 \n", + "9812 0 0 0 0 0 0 \n", + "\n", + " NHBLACK20R NHAMIN20R NHASIAN20R NH_NHPI20R NHOTHER20R NH2MORE20R \\\n", + "1997 0 0 0 0 0 0 \n", + "2592 0 0 0 0 0 0 \n", + "2729 0 0 0 0 0 0 \n", + "8262 0 0 0 0 0 0 \n", + "9812 0 0 0 0 0 0 \n", + "\n", + " WHITE20R BLACK20R AMIN20R ASIAN20R NHPI20R OTHER20R 2MORE20R \\\n", + "1997 0 0 0 0 0 0 0 \n", + "2592 0 0 0 0 0 0 0 \n", + "2729 0 0 0 0 0 0 0 \n", + "8262 0 0 0 0 0 0 0 \n", + "9812 0 0 0 0 0 0 0 \n", + "\n", + " geometry PRES16D PRES16R \\\n", + "1997 POLYGON ((3193984.432 1707215.315, 3193983.602... 0.0 0.0 \n", + "2592 POLYGON ((3194032.231 1707250.650, 3194531.988... 0.0 0.0 \n", + "2729 POLYGON ((3139496.394 1667748.388, 3139520.253... 0.0 0.0 \n", + "8262 POLYGON ((3194531.881 1707232.230, 3194531.988... 0.0 0.0 \n", + "9812 POLYGON ((3196304.708 1707166.383, 3197014.777... 0.0 0.0 \n", + "\n", + " SEN16D SEN16R AG18D AG18R SOS18D SOS18R TRE18D TRE18R GOV18D \\\n", + "1997 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "2592 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "2729 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "8262 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "9812 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "\n", + " GOV18R REG18D REG18R PRES20D SEN20D PRES20R SEN20R \n", + "1997 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "2592 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "2729 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "8262 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "9812 0.0 0.0 0.0 0.0 0.0 0.0 0.0 " + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[blocks_to_precincts2016_orig_assignment.isna()]" + ] + }, + { + "cell_type": "markdown", + "id": "220f4948", + "metadata": {}, + "source": [ + "### In this case the unassigned blocks have zero population and zero votes, so they wouldn't affect the aggregation/disaggregation of data - but this isn't always the case." + ] + }, + { + "cell_type": "markdown", + "id": "8e731eaf", + "metadata": {}, + "source": [ + "### MORAL: Shapefiles often come with significant topological problems that can affect data transfer in important ways! These problems should be diagnosed and repaired to the greatest extent possible prior to moving data around. \n", + "### For details about how Maup can repair these problems, see the \"Maup smart_repair demo\" notebook!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "324077ca", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12fe456f", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0a2ae944", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37a767b0", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ebcc0a9e", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/Maup data management demo.ipynb b/notebooks/Maup data management demo.ipynb new file mode 100644 index 0000000..63cd062 --- /dev/null +++ b/notebooks/Maup data management demo.ipynb @@ -0,0 +1,5636 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "1372272b", + "metadata": {}, + "source": [ + "### Demo notebook for data management using Maup, based on Denver County, CO" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "44231122", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import geopandas as gpd\n", + "import maup\n", + "\n", + "maup.progress.enabled = True\n", + "\n", + "pd.options.mode.chained_assignment = None\n", + "pd.set_option('display.max_columns', None)" + ] + }, + { + "cell_type": "markdown", + "id": "ccae0af8", + "metadata": {}, + "source": [ + "### Goal: Add population data and election data from 2016 and 2018 to 2020 precincts." + ] + }, + { + "cell_type": "markdown", + "id": "bc693ada", + "metadata": {}, + "source": [ + "### Here are the shapefiles that we'll need:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "2a4e791a", + "metadata": {}, + "outputs": [], + "source": [ + "blocks_df = gpd.read_file(\"../examples/Shapefiles/DenverCo_blocks/DenverCo_blocks.shp\")\n", + "precincts2016_df = gpd.read_file(\"../examples/Shapefiles/DenverCo_precincts2016_repaired/DenverCo_precincts2016_repaired.shp\")\n", + "precincts2018_df = gpd.read_file(\"../examples/Shapefiles/DenverCo_precincts2018_repaired/DenverCo_precincts2018_repaired.shp\")\n", + "precincts2020_df = gpd.read_file(\"../examples/Shapefiles/DenverCo_precincts2020_repaired/DenverCo_precincts2020_repaired.shp\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "b614fcef", + "metadata": {}, + "source": [ + "### Take a look at what information each of these shapefiles contains:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "9d840231", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
STATEFP20COUNTYFP20TRACTCE20BLOCKCE20GEOID20NAME20CD116SLDL20SLDU20CD118SLDL22SLDU22MTFCC20FUNCSTAT20ALAND20AWATER20INTPTLAT20INTPTLON20TOTPOP20WHITE20BLACK20AMIN20ASIAN20NHPI20OTHER202MORE20HISP20NH_WHITE20NH_BLACK20NH_AMIN20NH_ASIAN20NH_NHPI20NH_OTHER20NH_2MORE20VAP20HVAP20WVAP20BVAP20AMINVAP20ASIANVAP20NHPIVAP20OTHERVAP202MOREVAP20H_WHITE20H_BLACK20H_AMIN20H_ASIAN20H_NHPI20H_OTHER20H_2MORE20TOTPOP20RHISP20RNHWHITE20RNHBLACK20RNHAMIN20RNHASIAN20RNH_NHPI20RNHOTHER20RNH2MORE20RWHITE20RBLACK20RAMIN20RASIAN20RNHPI20ROTHER20R2MORE20Rgeometry
0080310005013004080310005013004Block 300414341434G5040S113600+39.7445040-105.0362730000000000000000000000000000000000000000000000000POLYGON ((3130205.522 1696255.187, 3130218.309...
1080310043034019080310043034019Block 401916311231G5040S173060+39.7138779-104.932222733290110027250010002422100100240100023372500100029011002POLYGON ((3159569.782 1685233.398, 3159568.175...
2080310055031008080310055031008Block 100811161126G5040S171200+39.6308773-105.029607138250140176220140143152101401430000033862201401425014017POLYGON ((3132214.313 1655090.209, 3132219.592...
3080310032041004080310032041004Block 100418311631G5040S156480+39.7376302-104.96899931088810007121685100006101168510007830000761081685100006881000712POLYGON ((3149182.154 1694282.223, 3149242.061...
4080310031011022080310031011022Block 102218331831G5040S147590+39.7475700-104.960387338370000011360000012912800000110000003813600000137000001POLYGON ((3151562.306 1697493.116, 3151561.779...
............................................................................................................................................................................................................
10144080310069032005080310069032005Block 200519311932G5040S186260+39.6706226-104.911557251300110613113000001945112601106110011054511130000019300110613POLYGON ((3165300.448 1669539.900, 3165299.396...
10145080319800011033080319800011033Block 103317331733G5040S118600+39.8255061-104.7465304000000000000000000000000000000000000000000000000POLYGON ((3211448.603 1727256.205, 3211526.127...
10146080319800011024080319800011024Block 102417331733G5040S1696250+39.8342487-104.7591599000000000000000000000000000000000000000000000000POLYGON ((3204485.897 1729532.389, 3204502.996...
10147080310004013008080310004013008Block 300814341434G5040S176610+39.7711189-105.019748372690000123680000015825600000210000117236800000169000012POLYGON ((3134776.811 1706335.477, 3134790.024...
10148080310009031012080310009031012Block 101214341434G5040S185850+39.7189546-105.02940608320034033235418034013563414032018192000032208455180340132013403323POLYGON ((3132206.908 1687391.415, 3132550.655...
\n", + "

10149 rows × 67 columns

\n", + "
" + ], + "text/plain": [ + " STATEFP20 COUNTYFP20 TRACTCE20 BLOCKCE20 GEOID20 NAME20 \\\n", + "0 08 031 000501 3004 080310005013004 Block 3004 \n", + "1 08 031 004303 4019 080310043034019 Block 4019 \n", + "2 08 031 005503 1008 080310055031008 Block 1008 \n", + "3 08 031 003204 1004 080310032041004 Block 1004 \n", + "4 08 031 003101 1022 080310031011022 Block 1022 \n", + "... ... ... ... ... ... ... \n", + "10144 08 031 006903 2005 080310069032005 Block 2005 \n", + "10145 08 031 980001 1033 080319800011033 Block 1033 \n", + "10146 08 031 980001 1024 080319800011024 Block 1024 \n", + "10147 08 031 000401 3008 080310004013008 Block 3008 \n", + "10148 08 031 000903 1012 080310009031012 Block 1012 \n", + "\n", + " CD116 SLDL20 SLDU20 CD118 SLDL22 SLDU22 MTFCC20 FUNCSTAT20 \\\n", + "0 1 4 34 1 4 34 G5040 S \n", + "1 1 6 31 1 2 31 G5040 S \n", + "2 1 1 16 1 1 26 G5040 S \n", + "3 1 8 31 1 6 31 G5040 S \n", + "4 1 8 33 1 8 31 G5040 S \n", + "... ... ... ... ... ... ... ... ... \n", + "10144 1 9 31 1 9 32 G5040 S \n", + "10145 1 7 33 1 7 33 G5040 S \n", + "10146 1 7 33 1 7 33 G5040 S \n", + "10147 1 4 34 1 4 34 G5040 S \n", + "10148 1 4 34 1 4 34 G5040 S \n", + "\n", + " ALAND20 AWATER20 INTPTLAT20 INTPTLON20 TOTPOP20 WHITE20 \\\n", + "0 11360 0 +39.7445040 -105.0362730 0 0 \n", + "1 17306 0 +39.7138779 -104.9322227 33 29 \n", + "2 17120 0 +39.6308773 -105.0296071 38 25 \n", + "3 15648 0 +39.7376302 -104.9689993 108 88 \n", + "4 14759 0 +39.7475700 -104.9603873 38 37 \n", + "... ... ... ... ... ... ... \n", + "10144 18626 0 +39.6706226 -104.9115572 51 30 \n", + "10145 11860 0 +39.8255061 -104.7465304 0 0 \n", + "10146 169625 0 +39.8342487 -104.7591599 0 0 \n", + "10147 17661 0 +39.7711189 -105.0197483 72 69 \n", + "10148 18585 0 +39.7189546 -105.0294060 83 20 \n", + "\n", + " BLACK20 AMIN20 ASIAN20 NHPI20 OTHER20 2MORE20 HISP20 NH_WHITE20 \\\n", + "0 0 0 0 0 0 0 0 0 \n", + "1 0 1 1 0 0 2 7 25 \n", + "2 0 1 4 0 1 7 6 22 \n", + "3 1 0 0 0 7 12 16 85 \n", + "4 0 0 0 0 0 1 1 36 \n", + "... ... ... ... ... ... ... ... ... \n", + "10144 0 1 1 0 6 13 11 30 \n", + "10145 0 0 0 0 0 0 0 0 \n", + "10146 0 0 0 0 0 0 0 0 \n", + "10147 0 0 0 0 1 2 3 68 \n", + "10148 0 3 4 0 33 23 54 18 \n", + "\n", + " NH_BLACK20 NH_AMIN20 NH_ASIAN20 NH_NHPI20 NH_OTHER20 NH_2MORE20 \\\n", + "0 0 0 0 0 0 0 \n", + "1 0 0 1 0 0 0 \n", + "2 0 1 4 0 1 4 \n", + "3 1 0 0 0 0 6 \n", + "4 0 0 0 0 0 1 \n", + "... ... ... ... ... ... ... \n", + "10144 0 0 0 0 1 9 \n", + "10145 0 0 0 0 0 0 \n", + "10146 0 0 0 0 0 0 \n", + "10147 0 0 0 0 0 1 \n", + "10148 0 3 4 0 1 3 \n", + "\n", + " VAP20 HVAP20 WVAP20 BVAP20 AMINVAP20 ASIANVAP20 NHPIVAP20 \\\n", + "0 0 0 0 0 0 0 0 \n", + "1 24 2 21 0 0 1 0 \n", + "2 31 5 21 0 1 4 0 \n", + "3 101 16 85 1 0 0 0 \n", + "4 29 1 28 0 0 0 0 \n", + "... ... ... ... ... ... ... ... \n", + "10144 45 11 26 0 1 1 0 \n", + "10145 0 0 0 0 0 0 0 \n", + "10146 0 0 0 0 0 0 0 \n", + "10147 58 2 56 0 0 0 0 \n", + "10148 56 34 14 0 3 2 0 \n", + "\n", + " OTHERVAP20 2MOREVAP20 H_WHITE20 H_BLACK20 H_AMIN20 H_ASIAN20 \\\n", + "0 0 0 0 0 0 0 \n", + "1 0 2 4 0 1 0 \n", + "2 1 4 3 0 0 0 \n", + "3 7 8 3 0 0 0 \n", + "4 0 1 1 0 0 0 \n", + "... ... ... ... ... ... ... \n", + "10144 6 11 0 0 1 1 \n", + "10145 0 0 0 0 0 0 \n", + "10146 0 0 0 0 0 0 \n", + "10147 0 2 1 0 0 0 \n", + "10148 18 19 2 0 0 0 \n", + "\n", + " H_NHPI20 H_OTHER20 H_2MORE20 TOTPOP20R HISP20R NHWHITE20R \\\n", + "0 0 0 0 0 0 0 \n", + "1 0 0 2 33 7 25 \n", + "2 0 0 3 38 6 22 \n", + "3 0 7 6 108 16 85 \n", + "4 0 0 0 38 1 36 \n", + "... ... ... ... ... ... ... \n", + "10144 0 5 4 51 11 30 \n", + "10145 0 0 0 0 0 0 \n", + "10146 0 0 0 0 0 0 \n", + "10147 0 1 1 72 3 68 \n", + "10148 0 32 20 84 55 18 \n", + "\n", + " NHBLACK20R NHAMIN20R NHASIAN20R NH_NHPI20R NHOTHER20R NH2MORE20R \\\n", + "0 0 0 0 0 0 0 \n", + "1 0 0 1 0 0 0 \n", + "2 0 1 4 0 1 4 \n", + "3 1 0 0 0 0 6 \n", + "4 0 0 0 0 0 1 \n", + "... ... ... ... ... ... ... \n", + "10144 0 0 0 0 1 9 \n", + "10145 0 0 0 0 0 0 \n", + "10146 0 0 0 0 0 0 \n", + "10147 0 0 0 0 0 1 \n", + "10148 0 3 4 0 1 3 \n", + "\n", + " WHITE20R BLACK20R AMIN20R ASIAN20R NHPI20R OTHER20R 2MORE20R \\\n", + "0 0 0 0 0 0 0 0 \n", + "1 29 0 1 1 0 0 2 \n", + "2 25 0 1 4 0 1 7 \n", + "3 88 1 0 0 0 7 12 \n", + "4 37 0 0 0 0 0 1 \n", + "... ... ... ... ... ... ... ... \n", + "10144 30 0 1 1 0 6 13 \n", + "10145 0 0 0 0 0 0 0 \n", + "10146 0 0 0 0 0 0 0 \n", + "10147 69 0 0 0 0 1 2 \n", + "10148 20 1 3 4 0 33 23 \n", + "\n", + " geometry \n", + "0 POLYGON ((3130205.522 1696255.187, 3130218.309... \n", + "1 POLYGON ((3159569.782 1685233.398, 3159568.175... \n", + "2 POLYGON ((3132214.313 1655090.209, 3132219.592... \n", + "3 POLYGON ((3149182.154 1694282.223, 3149242.061... \n", + "4 POLYGON ((3151562.306 1697493.116, 3151561.779... \n", + "... ... \n", + "10144 POLYGON ((3165300.448 1669539.900, 3165299.396... \n", + "10145 POLYGON ((3211448.603 1727256.205, 3211526.127... \n", + "10146 POLYGON ((3204485.897 1729532.389, 3204502.996... \n", + "10147 POLYGON ((3134776.811 1706335.477, 3134790.024... \n", + "10148 POLYGON ((3132206.908 1687391.415, 3132550.655... \n", + "\n", + "[10149 rows x 67 columns]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "22521abd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
COUNTYFPNAMELSADG16PREDCliG16PRERTruG16PRELJohG16PREGSteG16PREIMcMG16PREOthG16USSDBenG16USSRGleG16USSLWilG16USSGMenG16USSOthgeometry
0031Denver 10172316334211973016426179POLYGON ((3125703.139 1681147.799, 3125702.944...
1031Denver 10275716623257773817428149POLYGON ((3129675.455 1682118.384, 3129674.269...
2031Denver 103678241442241470322734189POLYGON ((3129642.865 1679001.901, 3129708.711...
3031Denver 20367711433102565114320152POLYGON ((3146588.324 1694971.352, 3146587.373...
4031Denver 2089868859384292210640545POLYGON ((3146274.972 1693705.886, 3146340.216...
.............................................
341031Denver 916929449722381092648349167POLYGON ((3164780.350 1664424.144, 3165010.513...
342031Denver 92751628534912652130620121POLYGON ((3163781.804 1663194.808, 3163746.863...
343031Denver 9237393365525316732348561412POLYGON ((3173239.768 1665060.235, 3173239.714...
344031Denver 9375523354587355335519102POLYGON ((3173156.086 1655193.513, 3173161.344...
345031Denver 9334032222811834082311280POLYGON ((3169603.158 1661791.346, 3169558.084...
\n", + "

346 rows × 14 columns

\n", + "
" + ], + "text/plain": [ + " COUNTYFP NAMELSAD G16PREDCli G16PRERTru G16PRELJoh G16PREGSte \\\n", + "0 031 Denver 101 723 163 34 21 \n", + "1 031 Denver 102 757 166 23 25 \n", + "2 031 Denver 103 678 241 44 22 \n", + "3 031 Denver 203 677 114 33 10 \n", + "4 031 Denver 208 986 88 59 38 \n", + ".. ... ... ... ... ... ... \n", + "341 031 Denver 916 929 449 72 23 \n", + "342 031 Denver 927 516 285 34 9 \n", + "343 031 Denver 923 739 336 55 25 \n", + "344 031 Denver 937 552 335 45 8 \n", + "345 031 Denver 933 403 222 28 11 \n", + "\n", + " G16PREIMcM G16PREOth G16USSDBen G16USSRGle G16USSLWil G16USSGMen \\\n", + "0 1 9 730 164 26 17 \n", + "1 7 7 738 174 28 14 \n", + "2 4 14 703 227 34 18 \n", + "3 2 5 651 143 20 15 \n", + "4 4 2 922 106 40 54 \n", + ".. ... ... ... ... ... ... \n", + "341 8 10 926 483 49 16 \n", + "342 12 6 521 306 20 12 \n", + "343 3 16 732 348 56 14 \n", + "344 7 3 553 355 19 10 \n", + "345 8 3 408 231 12 8 \n", + "\n", + " G16USSOth geometry \n", + "0 9 POLYGON ((3125703.139 1681147.799, 3125702.944... \n", + "1 9 POLYGON ((3129675.455 1682118.384, 3129674.269... \n", + "2 9 POLYGON ((3129642.865 1679001.901, 3129708.711... \n", + "3 2 POLYGON ((3146588.324 1694971.352, 3146587.373... \n", + "4 5 POLYGON ((3146274.972 1693705.886, 3146340.216... \n", + ".. ... ... \n", + "341 7 POLYGON ((3164780.350 1664424.144, 3165010.513... \n", + "342 1 POLYGON ((3163781.804 1663194.808, 3163746.863... \n", + "343 12 POLYGON ((3173239.768 1665060.235, 3173239.714... \n", + "344 2 POLYGON ((3173156.086 1655193.513, 3173161.344... \n", + "345 0 POLYGON ((3169603.158 1661791.346, 3169558.084... \n", + "\n", + "[346 rows x 14 columns]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2016_df" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "8849c8cf", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
COUNTYFPVTDSTNAMECD116FPSLDUSTSLDLSTPRECIDAG18DAG18RSOS18DSOS18RTRE18DTRE18RGOV18DGOV18RREG18DREG18RUSH18DUSH18RTOTPOPNH_WHITENH_BLACKNH_AMINNH_ASIANNH_NHPINH_OTHERNH_2MOREHISPH_WHITEH_BLACKH_AMINH_ASIANH_NHPIH_OTHERH_2MOREVAPHVAPWVAPBVAPAMINVAPASIANVAPNHPIVAPOTHERVAP2MOREVAPgeometry
0031031745Denver 7450103300713307167451084303107331610463341137272105025411212640000000000000000000000000POLYGON ((3167607.595 1714575.543, 3167607.566...
1031031540Denver 5400103400513405165407601937512157152567681967131767711790000000000000000000000000POLYGON ((3139202.439 1699577.829, 3139240.848...
2031031744Denver 74401033007133071674410003091003312994307105128097126710442619131301100028001002529128313011000POLYGON ((3180920.093 1705405.138, 3180920.088...
3031031530Denver 53001031005131051653064562864867462566517210729330082211000091171221062933008POLYGON ((3144153.912 1694345.882, 3144155.827...
4031031940Denver 940010310091310916940395267400273386271422254375247417244274258108002420000202684252108002POLYGON ((3165244.173 1654452.896, 3165241.340...
..........................................................................................................................................
351031031102Denver 102010340011340116102577134581129572135556130559126585123416943411856422053031011553307913013101152653186435355393210117POLYGON ((3133625.259 1681579.939, 3133626.217...
352031031101Denver 101010340011340116101615122619123615125613127599122634115441457866341092734357717802598441509154278521134993128792521POLYGON ((3125719.959 1678986.315, 3125719.959...
353031031924Denver 92401031009131091692446384476874738546894458864688744631586746196853620412096371555048659364184114296401856826133POLYGON ((3180600.942 1663222.850, 3180600.864...
354031031604Denver 604010330061330616604717133734126718130745121708114737116457889314081899046164108941326267050810632206877959911361944101POLYGON ((3173051.077 1693417.146, 3173051.078...
355031031746Denver 7460103300713307167464621464691464461604951234451274901270000000000000000000000000POLYGON ((3178132.638 1716671.381, 3178133.440...
\n", + "

356 rows × 45 columns

\n", + "
" + ], + "text/plain": [ + " COUNTYFP VTDST NAME CD116FP SLDUST SLDLST PRECID AG18D \\\n", + "0 031 031745 Denver 745 01 033 007 1330716745 1084 \n", + "1 031 031540 Denver 540 01 034 005 1340516540 760 \n", + "2 031 031744 Denver 744 01 033 007 1330716744 1000 \n", + "3 031 031530 Denver 530 01 031 005 1310516530 64 \n", + "4 031 031940 Denver 940 01 031 009 1310916940 395 \n", + ".. ... ... ... ... ... ... ... ... \n", + "351 031 031102 Denver 102 01 034 001 1340116102 577 \n", + "352 031 031101 Denver 101 01 034 001 1340116101 615 \n", + "353 031 031924 Denver 924 01 031 009 1310916924 463 \n", + "354 031 031604 Denver 604 01 033 006 1330616604 717 \n", + "355 031 031746 Denver 746 01 033 007 1330716746 462 \n", + "\n", + " AG18R SOS18D SOS18R TRE18D TRE18R GOV18D GOV18R REG18D REG18R \\\n", + "0 303 1073 316 1046 334 1137 272 1050 254 \n", + "1 193 751 215 715 256 768 196 713 176 \n", + "2 309 1003 312 994 307 1051 280 971 267 \n", + "3 5 62 8 64 8 67 4 62 5 \n", + "4 267 400 273 386 271 422 254 375 247 \n", + ".. ... ... ... ... ... ... ... ... ... \n", + "351 134 581 129 572 135 556 130 559 126 \n", + "352 122 619 123 615 125 613 127 599 122 \n", + "353 84 476 87 473 85 468 94 458 86 \n", + "354 133 734 126 718 130 745 121 708 114 \n", + "355 146 469 146 446 160 495 123 445 127 \n", + "\n", + " USH18D USH18R TOTPOP NH_WHITE NH_BLACK NH_AMIN NH_ASIAN NH_NHPI \\\n", + "0 1121 264 0 0 0 0 0 0 \n", + "1 771 179 0 0 0 0 0 0 \n", + "2 1044 261 91 31 30 1 1 0 \n", + "3 66 5 172 107 29 3 3 0 \n", + "4 417 244 274 258 1 0 8 0 \n", + ".. ... ... ... ... ... ... ... ... \n", + "351 585 123 4169 434 118 56 422 0 \n", + "352 634 115 4414 578 66 34 109 2 \n", + "353 468 87 4463 1586 746 19 685 3 \n", + "354 737 116 4578 893 1408 18 990 4 \n", + "355 490 127 0 0 0 0 0 0 \n", + "\n", + " NH_OTHER NH_2MORE HISP H_WHITE H_BLACK H_AMIN H_ASIAN H_NHPI \\\n", + "0 0 0 0 0 0 0 0 0 \n", + "1 0 0 0 0 0 0 0 0 \n", + "2 0 0 28 0 0 1 0 0 \n", + "3 0 8 22 11 0 0 0 0 \n", + "4 0 2 4 2 0 0 0 0 \n", + ".. ... ... ... ... ... ... ... ... \n", + "351 5 30 3101 1553 30 79 13 0 \n", + "352 7 34 3577 1780 25 98 4 4 \n", + "353 6 204 1209 637 15 5 5 0 \n", + "354 6 164 1089 413 26 26 7 0 \n", + "355 0 0 0 0 0 0 0 0 \n", + "\n", + " H_OTHER H_2MORE VAP HVAP WVAP BVAP AMINVAP ASIANVAP NHPIVAP \\\n", + "0 0 0 0 0 0 0 0 0 0 \n", + "1 0 0 0 0 0 0 0 0 0 \n", + "2 25 2 91 28 31 30 1 1 0 \n", + "3 9 1 171 22 106 29 3 3 0 \n", + "4 2 0 268 4 252 1 0 8 0 \n", + ".. ... ... ... ... ... ... ... ... ... \n", + "351 1310 115 2653 1864 353 55 39 321 0 \n", + "352 1509 154 2785 2113 499 31 28 79 2 \n", + "353 486 59 3641 841 1429 640 18 568 2 \n", + "354 508 106 3220 687 795 991 13 619 4 \n", + "355 0 0 0 0 0 0 0 0 0 \n", + "\n", + " OTHERVAP 2MOREVAP geometry \n", + "0 0 0 POLYGON ((3167607.595 1714575.543, 3167607.566... \n", + "1 0 0 POLYGON ((3139202.439 1699577.829, 3139240.848... \n", + "2 0 0 POLYGON ((3180920.093 1705405.138, 3180920.088... \n", + "3 0 8 POLYGON ((3144153.912 1694345.882, 3144155.827... \n", + "4 0 2 POLYGON ((3165244.173 1654452.896, 3165241.340... \n", + ".. ... ... ... \n", + "351 1 17 POLYGON ((3133625.259 1681579.939, 3133626.217... \n", + "352 5 21 POLYGON ((3125719.959 1678986.315, 3125719.959... \n", + "353 6 133 POLYGON ((3180600.942 1663222.850, 3180600.864... \n", + "354 4 101 POLYGON ((3173051.077 1693417.146, 3173051.078... \n", + "355 0 0 POLYGON ((3178132.638 1716671.381, 3178133.440... \n", + "\n", + "[356 rows x 45 columns]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2018_df" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "f6af11f7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PRECIDSTATEFP20COUNTYFP20NAMECD18SUD18SLD18VTDST20NOTESPRES20DSEN20DPRES20RSEN20Rgeometry
0131051653008031Denver 53013105031530None73751612POLYGON ((3144474.619 1694874.799, 3144509.314...
1131061660508031Denver 60513106031605None720702110137POLYGON ((3162200.074 1691024.061, 3162164.358...
2131061660608031Denver 60613106031606None832819107121POLYGON ((3162459.795 1693616.802, 3162459.063...
3131061660708031Denver 60713106031607None597595102113POLYGON ((3165142.137 1693683.542, 3165141.880...
4131061660908031Denver 60913106031609None2412335971POLYGON ((3152968.867 1690916.131, 3153065.627...
.............................................
351133071674608031Denver 74613307031746None17681699386469POLYGON ((3172960.862 1716278.085, 3172953.814...
352134051654008031Denver 54013405031540None15581477416489POLYGON ((3139192.261 1699587.973, 3139181.643...
353134051654108031Denver 54113405031541None15541504246297POLYGON ((3148046.593 1705187.168, 3148047.303...
354134051654208031Denver 54213405031542None881828200247POLYGON ((3133494.258 1698947.396, 3133494.426...
355134051654308031Denver 54313405031543None8378369390POLYGON ((3140186.339 1689621.761, 3140186.210...
\n", + "

356 rows × 14 columns

\n", + "
" + ], + "text/plain": [ + " PRECID STATEFP20 COUNTYFP20 NAME CD18 SUD18 SLD18 VTDST20 \\\n", + "0 1310516530 08 031 Denver 530 1 31 05 031530 \n", + "1 1310616605 08 031 Denver 605 1 31 06 031605 \n", + "2 1310616606 08 031 Denver 606 1 31 06 031606 \n", + "3 1310616607 08 031 Denver 607 1 31 06 031607 \n", + "4 1310616609 08 031 Denver 609 1 31 06 031609 \n", + ".. ... ... ... ... ... ... ... ... \n", + "351 1330716746 08 031 Denver 746 1 33 07 031746 \n", + "352 1340516540 08 031 Denver 540 1 34 05 031540 \n", + "353 1340516541 08 031 Denver 541 1 34 05 031541 \n", + "354 1340516542 08 031 Denver 542 1 34 05 031542 \n", + "355 1340516543 08 031 Denver 543 1 34 05 031543 \n", + "\n", + " NOTES PRES20D SEN20D PRES20R SEN20R \\\n", + "0 None 73 75 16 12 \n", + "1 None 720 702 110 137 \n", + "2 None 832 819 107 121 \n", + "3 None 597 595 102 113 \n", + "4 None 241 233 59 71 \n", + ".. ... ... ... ... ... \n", + "351 None 1768 1699 386 469 \n", + "352 None 1558 1477 416 489 \n", + "353 None 1554 1504 246 297 \n", + "354 None 881 828 200 247 \n", + "355 None 837 836 93 90 \n", + "\n", + " geometry \n", + "0 POLYGON ((3144474.619 1694874.799, 3144509.314... \n", + "1 POLYGON ((3162200.074 1691024.061, 3162164.358... \n", + "2 POLYGON ((3162459.795 1693616.802, 3162459.063... \n", + "3 POLYGON ((3165142.137 1693683.542, 3165141.880... \n", + "4 POLYGON ((3152968.867 1690916.131, 3153065.627... \n", + ".. ... \n", + "351 POLYGON ((3172960.862 1716278.085, 3172953.814... \n", + "352 POLYGON ((3139192.261 1699587.973, 3139181.643... \n", + "353 POLYGON ((3148046.593 1705187.168, 3148047.303... \n", + "354 POLYGON ((3133494.258 1698947.396, 3133494.426... \n", + "355 POLYGON ((3140186.339 1689621.761, 3140186.210... \n", + "\n", + "[356 rows x 14 columns]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2020_df" + ] + }, + { + "cell_type": "markdown", + "id": "b93f583a", + "metadata": {}, + "source": [ + "### So the blocks file has lots of population data and the precinct files each have election data for one year.\n", + "### It might be convenient to rename some of the election columns in the 2016 file so that they have the same format as the other years." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "ad1aa151", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Index(['COUNTYFP', 'NAMELSAD', 'G16PREDCli', 'G16PRERTru', 'G16PRELJoh',\n", + " 'G16PREGSte', 'G16PREIMcM', 'G16PREOth', 'G16USSDBen', 'G16USSRGle',\n", + " 'G16USSLWil', 'G16USSGMen', 'G16USSOth', 'geometry'],\n", + " dtype='object')" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2016_df.columns" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "4cd76b85", + "metadata": {}, + "outputs": [], + "source": [ + "precincts2016_df = precincts2016_df.rename(columns = {\n", + " 'G16PREDCli': 'PRES16D',\n", + " 'G16PRERTru': 'PRES16R',\n", + " 'G16USSDBen': 'SEN16D',\n", + " 'G16USSRGle': 'SEN16R'\n", + "})" + ] + }, + { + "cell_type": "markdown", + "id": "6118e4a4", + "metadata": {}, + "source": [ + "### In order to move all this data around, we'll need assignments of blocks to precincts for each of the precinct files." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "b226cfc9", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|████████████████████████████████████████| 356/356 [00:00<00:00, 786.59it/s]\n", + "100%|████████████████████████████████████████| 356/356 [00:00<00:00, 371.12it/s]\n", + "100%|████████████████████████████████████████| 356/356 [00:00<00:00, 892.50it/s]\n", + "100%|████████████████████████████████████████| 356/356 [00:01<00:00, 347.47it/s]\n", + "100%|████████████████████████████████████████| 346/346 [00:00<00:00, 725.00it/s]\n", + "100%|████████████████████████████████████████| 346/346 [00:01<00:00, 311.78it/s]\n" + ] + } + ], + "source": [ + "blocks_to_precincts2020_assignment = maup.assign(blocks_df.geometry, precincts2020_df.geometry)\n", + "blocks_to_precincts2018_assignment = maup.assign(blocks_df.geometry, precincts2018_df.geometry)\n", + "blocks_to_precincts2016_assignment = maup.assign(blocks_df.geometry, precincts2016_df.geometry)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "cdcaf044", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0 300\n", + "1 56\n", + "2 41\n", + "3 73\n", + "4 262\n", + " ... \n", + "10144 96\n", + "10145 234\n", + "10146 234\n", + "10147 292\n", + "10148 313\n", + "Length: 10149, dtype: int64" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_to_precincts2020_assignment" + ] + }, + { + "cell_type": "markdown", + "id": "fbed2841", + "metadata": {}, + "source": [ + "### First step: Aggregate population data from blocks to 2020 precincts.\n", + "### (We'll just use a few of the population columns for this demo.)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "61cf6d7e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Index(['STATEFP20', 'COUNTYFP20', 'TRACTCE20', 'BLOCKCE20', 'GEOID20',\n", + " 'NAME20', 'CD116', 'SLDL20', 'SLDU20', 'CD118', 'SLDL22', 'SLDU22',\n", + " 'MTFCC20', 'FUNCSTAT20', 'ALAND20', 'AWATER20', 'INTPTLAT20',\n", + " 'INTPTLON20', 'TOTPOP20', 'WHITE20', 'BLACK20', 'AMIN20', 'ASIAN20',\n", + " 'NHPI20', 'OTHER20', '2MORE20', 'HISP20', 'NH_WHITE20', 'NH_BLACK20',\n", + " 'NH_AMIN20', 'NH_ASIAN20', 'NH_NHPI20', 'NH_OTHER20', 'NH_2MORE20',\n", + " 'VAP20', 'HVAP20', 'WVAP20', 'BVAP20', 'AMINVAP20', 'ASIANVAP20',\n", + " 'NHPIVAP20', 'OTHERVAP20', '2MOREVAP20', 'H_WHITE20', 'H_BLACK20',\n", + " 'H_AMIN20', 'H_ASIAN20', 'H_NHPI20', 'H_OTHER20', 'H_2MORE20',\n", + " 'TOTPOP20R', 'HISP20R', 'NHWHITE20R', 'NHBLACK20R', 'NHAMIN20R',\n", + " 'NHASIAN20R', 'NH_NHPI20R', 'NHOTHER20R', 'NH2MORE20R', 'WHITE20R',\n", + " 'BLACK20R', 'AMIN20R', 'ASIAN20R', 'NHPI20R', 'OTHER20R', '2MORE20R',\n", + " 'geometry'],\n", + " dtype='object')" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df.columns" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "d1bf7b5e", + "metadata": {}, + "outputs": [], + "source": [ + "pop_cols = ['TOTPOP20', 'VAP20']" + ] + }, + { + "cell_type": "markdown", + "id": "5fd55604", + "metadata": {}, + "source": [ + "### We can use the assignment of blocks to precincts to aggregate populations from blocks up to precincts:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "5da80e36", + "metadata": {}, + "outputs": [], + "source": [ + "precincts2020_df[pop_cols] = blocks_df[pop_cols].groupby(blocks_to_precincts2020_assignment).sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "9905a96f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
TOTPOP20VAP20
0171171
11132930
212071047
3926738
4371283
\n", + "
" + ], + "text/plain": [ + " TOTPOP20 VAP20\n", + "0 171 171\n", + "1 1132 930\n", + "2 1207 1047\n", + "3 926 738\n", + "4 371 283" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2020_df[pop_cols].head()" + ] + }, + { + "cell_type": "markdown", + "id": "36a553ac", + "metadata": {}, + "source": [ + "### Check that we didn't gain/lose any population in the aggregation step:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "4d6bbc16", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "TOTPOP20 715522\n", + "VAP20 581062\n", + "dtype: int64" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[pop_cols].sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "e575bd32", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "TOTPOP20 715522\n", + "VAP20 581062\n", + "dtype: int64" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2020_df[pop_cols].sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "1d9a3f5d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PRECIDSTATEFP20COUNTYFP20NAMECD18SUD18SLD18VTDST20NOTESPRES20DSEN20DPRES20RSEN20RgeometryTOTPOP20VAP20
0131051653008031Denver 53013105031530None73751612POLYGON ((3144474.619 1694874.799, 3144509.314...171171
1131061660508031Denver 60513106031605None720702110137POLYGON ((3162200.074 1691024.061, 3162164.358...1132930
2131061660608031Denver 60613106031606None832819107121POLYGON ((3162459.795 1693616.802, 3162459.063...12071047
3131061660708031Denver 60713106031607None597595102113POLYGON ((3165142.137 1693683.542, 3165141.880...926738
4131061660908031Denver 60913106031609None2412335971POLYGON ((3152968.867 1690916.131, 3153065.627...371283
...................................................
351133071674608031Denver 74613307031746None17681699386469POLYGON ((3172960.862 1716278.085, 3172953.814...32842196
352134051654008031Denver 54013405031540None15581477416489POLYGON ((3139192.261 1699587.973, 3139181.643...33663268
353134051654108031Denver 54113405031541None15541504246297POLYGON ((3148046.593 1705187.168, 3148047.303...27742643
354134051654208031Denver 54213405031542None881828200247POLYGON ((3133494.258 1698947.396, 3133494.426...19941830
355134051654308031Denver 54313405031543None8378369390POLYGON ((3140186.339 1689621.761, 3140186.210...16781367
\n", + "

356 rows × 16 columns

\n", + "
" + ], + "text/plain": [ + " PRECID STATEFP20 COUNTYFP20 NAME CD18 SUD18 SLD18 VTDST20 \\\n", + "0 1310516530 08 031 Denver 530 1 31 05 031530 \n", + "1 1310616605 08 031 Denver 605 1 31 06 031605 \n", + "2 1310616606 08 031 Denver 606 1 31 06 031606 \n", + "3 1310616607 08 031 Denver 607 1 31 06 031607 \n", + "4 1310616609 08 031 Denver 609 1 31 06 031609 \n", + ".. ... ... ... ... ... ... ... ... \n", + "351 1330716746 08 031 Denver 746 1 33 07 031746 \n", + "352 1340516540 08 031 Denver 540 1 34 05 031540 \n", + "353 1340516541 08 031 Denver 541 1 34 05 031541 \n", + "354 1340516542 08 031 Denver 542 1 34 05 031542 \n", + "355 1340516543 08 031 Denver 543 1 34 05 031543 \n", + "\n", + " NOTES PRES20D SEN20D PRES20R SEN20R \\\n", + "0 None 73 75 16 12 \n", + "1 None 720 702 110 137 \n", + "2 None 832 819 107 121 \n", + "3 None 597 595 102 113 \n", + "4 None 241 233 59 71 \n", + ".. ... ... ... ... ... \n", + "351 None 1768 1699 386 469 \n", + "352 None 1558 1477 416 489 \n", + "353 None 1554 1504 246 297 \n", + "354 None 881 828 200 247 \n", + "355 None 837 836 93 90 \n", + "\n", + " geometry TOTPOP20 VAP20 \n", + "0 POLYGON ((3144474.619 1694874.799, 3144509.314... 171 171 \n", + "1 POLYGON ((3162200.074 1691024.061, 3162164.358... 1132 930 \n", + "2 POLYGON ((3162459.795 1693616.802, 3162459.063... 1207 1047 \n", + "3 POLYGON ((3165142.137 1693683.542, 3165141.880... 926 738 \n", + "4 POLYGON ((3152968.867 1690916.131, 3153065.627... 371 283 \n", + ".. ... ... ... \n", + "351 POLYGON ((3172960.862 1716278.085, 3172953.814... 3284 2196 \n", + "352 POLYGON ((3139192.261 1699587.973, 3139181.643... 3366 3268 \n", + "353 POLYGON ((3148046.593 1705187.168, 3148047.303... 2774 2643 \n", + "354 POLYGON ((3133494.258 1698947.396, 3133494.426... 1994 1830 \n", + "355 POLYGON ((3140186.339 1689621.761, 3140186.210... 1678 1367 \n", + "\n", + "[356 rows x 16 columns]" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2020_df" + ] + }, + { + "cell_type": "markdown", + "id": "35bf6d3f", + "metadata": {}, + "source": [ + "### Next step: Disaggregate votes from all three precinct files to blocks, using Voting Age Population (VAP20) for the weights. " + ] + }, + { + "cell_type": "markdown", + "id": "c78b0a30", + "metadata": {}, + "source": [ + "### 2016:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "c3c840ad", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Index(['COUNTYFP', 'NAMELSAD', 'PRES16D', 'PRES16R', 'G16PRELJoh',\n", + " 'G16PREGSte', 'G16PREIMcM', 'G16PREOth', 'SEN16D', 'SEN16R',\n", + " 'G16USSLWil', 'G16USSGMen', 'G16USSOth', 'geometry'],\n", + " dtype='object')" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2016_df.columns" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "34f07c2b", + "metadata": {}, + "outputs": [], + "source": [ + "elec2016_cols = ['PRES16D', 'PRES16R', 'SEN16D', 'SEN16R']" + ] + }, + { + "cell_type": "markdown", + "id": "6b37197a", + "metadata": {}, + "source": [ + "### Now we assign a \"weight\" to each block that's equal to the fraction of the total population of ALL blocks assigned to the same precinct that's contained in that block.\n", + "### IMPORTANT: Some precincts have zero population, which leads to a zero denominator and an undefined weight for all blocks assigned to that precinct. We can solve this problem by replacing NaN values with zeros.\n", + "### Occasionally some zero-population precincts contain a small, nonzero number of votes, and these votes will be lost in the disaggregation from precincts down to blocks. " + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "e4694fc7", + "metadata": {}, + "outputs": [], + "source": [ + "weights2016 = blocks_df[\"VAP20\"] / blocks_to_precincts2016_assignment.map(blocks_df[\"VAP20\"].groupby(blocks_to_precincts2016_assignment).sum())\n", + "weights2016 = weights2016.fillna(0)" + ] + }, + { + "cell_type": "markdown", + "id": "97376acf", + "metadata": {}, + "source": [ + "### Here's the disaggregation step, using the \"prorate\" function:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "3fa669eb", + "metadata": {}, + "outputs": [], + "source": [ + "prorated2016 = maup.prorate(blocks_to_precincts2016_assignment, precincts2016_df[elec2016_cols], weights2016)\n", + "blocks_df[elec2016_cols] = prorated2016" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "d4e5120a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PRES16DPRES16RSEN16DSEN16R
00.0000000.0000000.0000000.000000
115.3982305.13982315.1008855.883186
210.4026858.01006711.1308728.197315
361.0108475.95728858.5457636.505085
417.1045581.57439716.4825742.313003
\n", + "
" + ], + "text/plain": [ + " PRES16D PRES16R SEN16D SEN16R\n", + "0 0.000000 0.000000 0.000000 0.000000\n", + "1 15.398230 5.139823 15.100885 5.883186\n", + "2 10.402685 8.010067 11.130872 8.197315\n", + "3 61.010847 5.957288 58.545763 6.505085\n", + "4 17.104558 1.574397 16.482574 2.313003" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[elec2016_cols].head()" + ] + }, + { + "cell_type": "markdown", + "id": "b048f421", + "metadata": {}, + "source": [ + "### Check to see whether we gained/lost any votes:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "ede614d4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "PRES16D 244551\n", + "PRES16R 62690\n", + "SEN16D 238774\n", + "SEN16R 71078\n", + "dtype: int64" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2016_df[elec2016_cols].sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "b1b1bc43", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "PRES16D 244551.0\n", + "PRES16R 62690.0\n", + "SEN16D 238774.0\n", + "SEN16R 71078.0\n", + "dtype: float64" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[elec2016_cols].sum()" + ] + }, + { + "cell_type": "markdown", + "id": "adfe2fa6", + "metadata": {}, + "source": [ + "### 2018:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "ff59b2e4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Index(['COUNTYFP', 'VTDST', 'NAME', 'CD116FP', 'SLDUST', 'SLDLST', 'PRECID',\n", + " 'AG18D', 'AG18R', 'SOS18D', 'SOS18R', 'TRE18D', 'TRE18R', 'GOV18D',\n", + " 'GOV18R', 'REG18D', 'REG18R', 'USH18D', 'USH18R', 'TOTPOP', 'NH_WHITE',\n", + " 'NH_BLACK', 'NH_AMIN', 'NH_ASIAN', 'NH_NHPI', 'NH_OTHER', 'NH_2MORE',\n", + " 'HISP', 'H_WHITE', 'H_BLACK', 'H_AMIN', 'H_ASIAN', 'H_NHPI', 'H_OTHER',\n", + " 'H_2MORE', 'VAP', 'HVAP', 'WVAP', 'BVAP', 'AMINVAP', 'ASIANVAP',\n", + " 'NHPIVAP', 'OTHERVAP', '2MOREVAP', 'geometry'],\n", + " dtype='object')" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2018_df.columns" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "5cc8bbe8", + "metadata": {}, + "outputs": [], + "source": [ + "elec2018_cols = ['AG18D', 'AG18R', 'SOS18D', 'SOS18R', 'TRE18D', 'TRE18R', 'GOV18D', 'GOV18R', 'REG18D', 'REG18R']" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "6189e09e", + "metadata": {}, + "outputs": [], + "source": [ + "weights2018 = blocks_df[\"VAP20\"] / blocks_to_precincts2018_assignment.map(blocks_df[\"VAP20\"].groupby(blocks_to_precincts2018_assignment).sum())\n", + "weights2018 = weights2018.fillna(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "ec05ed4c", + "metadata": {}, + "outputs": [], + "source": [ + "prorated2018 = maup.prorate(blocks_to_precincts2018_assignment, precincts2018_df[elec2018_cols], weights2018)\n", + "blocks_df[elec2018_cols] = prorated2018" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "339eefea", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
AG18DAG18RSOS18DSOS18RTRE18DTRE18RGOV18DGOV18RREG18DREG18R
00.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.0000000.000000
114.3787616.22300913.6141597.00885013.7415936.43539814.3575226.22300913.4654875.883186
29.8617457.53154410.4442957.05302010.4234906.96979910.0697997.07382610.0281886.449664
359.7783055.27254258.0664417.12135658.8196616.43661061.1477974.38237357.5871194.519322
417.9403492.02144817.4738612.46849917.4155502.54624718.3873991.82707817.0073731.768767
\n", + "
" + ], + "text/plain": [ + " AG18D AG18R SOS18D SOS18R TRE18D TRE18R GOV18D \\\n", + "0 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 \n", + "1 14.378761 6.223009 13.614159 7.008850 13.741593 6.435398 14.357522 \n", + "2 9.861745 7.531544 10.444295 7.053020 10.423490 6.969799 10.069799 \n", + "3 59.778305 5.272542 58.066441 7.121356 58.819661 6.436610 61.147797 \n", + "4 17.940349 2.021448 17.473861 2.468499 17.415550 2.546247 18.387399 \n", + "\n", + " GOV18R REG18D REG18R \n", + "0 0.000000 0.000000 0.000000 \n", + "1 6.223009 13.465487 5.883186 \n", + "2 7.073826 10.028188 6.449664 \n", + "3 4.382373 57.587119 4.519322 \n", + "4 1.827078 17.007373 1.768767 " + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[elec2018_cols].head()" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "244b1927", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AG18D 232798\n", + "AG18R 64532\n", + "SOS18D 232255\n", + "SOS18R 67147\n", + "TRE18D 230382\n", + "TRE18R 66728\n", + "GOV18D 238762\n", + "GOV18R 60151\n", + "REG18D 223947\n", + "REG18R 57322\n", + "dtype: int64" + ] + }, + "execution_count": 30, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2018_df[elec2018_cols].sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "8d8bf1bc", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AG18D 232798.0\n", + "AG18R 64532.0\n", + "SOS18D 232255.0\n", + "SOS18R 67147.0\n", + "TRE18D 230382.0\n", + "TRE18R 66728.0\n", + "GOV18D 238762.0\n", + "GOV18R 60151.0\n", + "REG18D 223947.0\n", + "REG18R 57322.0\n", + "dtype: float64" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[elec2018_cols].sum()" + ] + }, + { + "cell_type": "markdown", + "id": "45e4bc1b", + "metadata": {}, + "source": [ + "### If the goal is just to put all the data on 2020 precincts, we don't really have to disaggregate 2020 election data to blocks - but we might also want all the election data on blocks, so we'll go ahead and do it for completeness." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "b75e9d3c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Index(['PRECID', 'STATEFP20', 'COUNTYFP20', 'NAME', 'CD18', 'SUD18', 'SLD18',\n", + " 'VTDST20', 'NOTES', 'PRES20D', 'SEN20D', 'PRES20R', 'SEN20R',\n", + " 'geometry', 'TOTPOP20', 'VAP20'],\n", + " dtype='object')" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2020_df.columns" + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "fe04f13c", + "metadata": {}, + "outputs": [], + "source": [ + "elec2020_cols = ['PRES20D', 'SEN20D', 'PRES20R', 'SEN20R']" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "735860ac", + "metadata": {}, + "outputs": [], + "source": [ + "weights2020 = blocks_df[\"VAP20\"] / blocks_to_precincts2020_assignment.map(blocks_df[\"VAP20\"].groupby(blocks_to_precincts2020_assignment).sum())\n", + "weights2020 = weights2020.fillna(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "1ace13c7", + "metadata": {}, + "outputs": [], + "source": [ + "prorated2020 = maup.prorate(blocks_to_precincts2020_assignment, precincts2020_df[elec2020_cols], weights2020)\n", + "blocks_df[elec2020_cols] = prorated2020" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "fdfe9942", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
PRES20DSEN20DPRES20RSEN20R
00.0000000.0000000.0000000.000000
117.33097316.5663725.4371686.711504
213.89798713.6899338.4261748.863087
369.02237367.5844075.4094926.847458
421.67225220.9142091.7298932.468499
\n", + "
" + ], + "text/plain": [ + " PRES20D SEN20D PRES20R SEN20R\n", + "0 0.000000 0.000000 0.000000 0.000000\n", + "1 17.330973 16.566372 5.437168 6.711504\n", + "2 13.897987 13.689933 8.426174 8.863087\n", + "3 69.022373 67.584407 5.409492 6.847458\n", + "4 21.672252 20.914209 1.729893 2.468499" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[elec2020_cols].head()" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "0c02e6de", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "PRES20D 313293\n", + "SEN20D 305602\n", + "PRES20R 71618\n", + "SEN20R 80163\n", + "dtype: int64" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2020_df[elec2020_cols].sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "530aa5d6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "PRES20D 313293.0\n", + "SEN20D 305602.0\n", + "PRES20R 71618.0\n", + "SEN20R 80163.0\n", + "dtype: float64" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[elec2020_cols].sum()" + ] + }, + { + "cell_type": "markdown", + "id": "4bfcf5fb", + "metadata": {}, + "source": [ + "### Last step: Aggregate 2016 and 2018 election data up from blocks to 2020 precincts." + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "22c17f41", + "metadata": {}, + "outputs": [], + "source": [ + "precincts2020_df[elec2016_cols] = blocks_df[elec2016_cols].groupby(blocks_to_precincts2020_assignment).sum()\n", + "precincts2020_df[elec2018_cols] = blocks_df[elec2018_cols].groupby(blocks_to_precincts2020_assignment).sum()" + ] + }, + { + "cell_type": "markdown", + "id": "df0347e2", + "metadata": {}, + "source": [ + "### Check to see whether we gained/lost any votes:" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "c5555a15", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "PRES16D 244551.0\n", + "PRES16R 62690.0\n", + "SEN16D 238774.0\n", + "SEN16R 71078.0\n", + "dtype: float64" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[elec2016_cols].sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "4e2ce9ec", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "data": { + "text/plain": [ + "PRES16D 244551.0\n", + "PRES16R 62690.0\n", + "SEN16D 238774.0\n", + "SEN16R 71078.0\n", + "dtype: float64" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2020_df[elec2016_cols].sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "c6b8d1c7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AG18D 232798.0\n", + "AG18R 64532.0\n", + "SOS18D 232255.0\n", + "SOS18R 67147.0\n", + "TRE18D 230382.0\n", + "TRE18R 66728.0\n", + "GOV18D 238762.0\n", + "GOV18R 60151.0\n", + "REG18D 223947.0\n", + "REG18R 57322.0\n", + "dtype: float64" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[elec2018_cols].sum()" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "89074ca5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "AG18D 232798.0\n", + "AG18R 64532.0\n", + "SOS18D 232255.0\n", + "SOS18R 67147.0\n", + "TRE18D 230382.0\n", + "TRE18R 66728.0\n", + "GOV18D 238762.0\n", + "GOV18R 60151.0\n", + "REG18D 223947.0\n", + "REG18R 57322.0\n", + "dtype: float64" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "precincts2020_df[elec2018_cols].sum()" + ] + }, + { + "cell_type": "markdown", + "id": "03e0cb1a", + "metadata": {}, + "source": [ + "### Success! Now we can save these shapefiles for later use:" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "dfab9d9e", + "metadata": {}, + "outputs": [], + "source": [ + "# blocks_df.to_file(\"./Shapefiles/DenverCo_blocks_with_data/DenverCo_blocks_with_data.shp\")\n", + "# precincts2020_df.to_file(\"./Shapefiles/DenverCo_precincts2020_with_data/DenverCo_precincts2020_with_data.shp\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "11ab3377", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "c1d501b5", + "metadata": {}, + "source": [ + "### Now let's talk about potential problems! \n", + "### We started with \"cleaned-up\" versions of the precinct shapefiles. The \"doctor\" function is used to evaluate shapefiles for topological problems such as gaps and overlaps." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "01e1d0e2", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|████████████████████████████████████████| 356/356 [00:00<00:00, 962.45it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 10 holes.\n" + ] + }, + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "maup.doctor(precincts2020_df)" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "f40b7134", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|███████████████████████████████████████| 356/356 [00:00<00:00, 1036.65it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 10 holes.\n" + ] + }, + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "maup.doctor(precincts2018_df)" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "40f79791", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|███████████████████████████████████████| 346/346 [00:00<00:00, 1024.45it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 10 holes.\n" + ] + }, + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 47, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "maup.doctor(precincts2016_df)" + ] + }, + { + "cell_type": "markdown", + "id": "97281549", + "metadata": {}, + "source": [ + "### These holes are actually \"real\" because Denver County is not simply connected; there are \"islands\" that belong to Arapahoe County. So these holes are not indicative of problems." + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "f7fb0e39", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 48, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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++yz6txzz1U33nhjeQdZZoca80033aTOPvvsCoyscg415osvvlhdd911E2677LLL1Oc///lyDbEiDjRupZQqFAoqFAqpX/ziF0oppWzbVg0NDeqee+4pXTMyMqI8Ho/61a9+VZHxlsOhxLw3S5cuVYDavHlzuYZYduWIedu2baq5uVmtXr36gH/HHw9khuMwsyyLRx99lGQyyYIFC8p2v6OjowDEYrGy3We5lDPmxYsXc/HFF3PBBReUaXSVUa6Yn3zySebOncvnPvc56urqmD17Nj/96U/LONLyKVfMZ511Fn/+859Zt24dAG+++SYvvfQSF110UbmGWlYfJe5UKkU+ny/9f924cSO9vb0TnteRSIQzzjiDV199tSLjPhTliHlvRkdH0TSNqqqqMo20fMoVs23bXH311XzjG9/glFNOqdRwj0rHTfO2o93bb7/NggULyGQyBINBnnjiCU4++eSy3Ldt23zlK19h4cKFzJw5syz3WQ7ljvnRRx9l5cqVLFu2rIyjLK9yx9zd3c0DDzzA1772Nf71X/+VZcuWccMNN+B2u1m0aFEZR/7RlTvmf/mXfyEejzNjxgwMw8CyLG6//XY+//nPl3HUh+5Q4r7ppptoamoqJRi9vb0A1NfXT7iuvr6+9LmjQTlj/qBMJsNNN93ElVdeeVQ1PSt3zHfddRemaXLDDTdUashHLUk4DpPp06ezatUqRkdHefzxx1m0aBEvvPBCWZKOxYsXs3r1al566aUyjLR8yhnz1q1bufHGG3n22Wfxer0VGG15lPtxtm2buXPncscddwAwe/ZsVq9ezY9//OOjJuEod8y//vWv+eUvf8mSJUs45ZRTWLVqFV/5yldoamo6amKGjx73nXfeyaOPPsrzzz9/VD+X96ZSMefzea644gqUUjzwwAOVGv5HUs6YV6xYwfe//31WrlyJpmmHY/hHlyO9pnOiOv/889UXvvCF/V63v/W9xYsXq5aWFtXd3V3G0VXGocT8xBNPKEAZhlH6AJSmacowDFUoFCo06kNzqI9zW1ub+sd//McJt/3oRz9STU1N5Rpi2R1qzC0tLeqHP/zhhNtuvfVWNX369HINsSIOJO577rlHRSIRtWzZsgm3b9iwQQHqjTfemHD7xz72MXXDDTeUe6hlcygxj8vlcurSSy9VXV1damBgoBLDLKtDifl73/te6XfW7r/HdF1X7e3tFRz10UFmOI4Q27bJZrMf+euVUlx//fU88cQTPP/883R2dpZxdJVxKDGff/75vP322xNuu/baa5kxYwY33XQThmGUY4hld6iP88KFC/c47rxu3Tra29sPdWgVc6gxp1IpdH3i9jLDMLBt+1CHVlH7i/vuu+/m9ttv5+mnn2bu3LkTPtfZ2UlDQwN//vOfOe200wCIx+O8/vrrfPGLX6zksA/JocQMu2Y21q9fz3PPPUd1dXUlh1sWhxLz1VdfvceS0ic/+Umuvvpqrr322oqM92giCcdh8M1vfpOLLrqItrY2EokES5Ys4fnnn+fpp58G4JprrqG5uZnvfOc7AORyOd59993S33t6eli1ahXBYJApU6YAzjLKkiVL+N3vfkcoFCqt80YiEXw+3xGIcqJyxxwKhfbYnxIIBKiurj5q9q1U4nH+6le/yllnncUdd9zBFVdcwdKlS3nwwQd58MEHj0yQH1CJmC+55BJuv/122traOOWUU3jjjTe47777uO66645MkHtxsHHfddddfOtb32LJkiV0dHSU/r8Gg0GCwWCppsxtt93G1KlT6ezs5JZbbqGpqYlLL730SIU5QbljzufzXH755axcuZI//OEPWJZVuiYWi+F2u49MoLspd8zV1dV7JFUul4uGhgamT59+eIM7Eo70FMuJ4LrrrlPt7e3K7Xar2tpadf7556tnnnmm9Plzzz1XLVq0qPTvjRs3KmCPj3PPPbd0zd4+D6iHHnro8AW2D5WI+YOOtmOxlYr597//vZo5c6byeDxqxowZ6sEHHzxMEe1fJWKOx+PqxhtvVG1tbcrr9apJkyapm2++WWWz2cMY2b4dbNzt7e17jfvb3/526RrbttUtt9yi6uvrlcfjUeeff75au3btYYxq38od84c9FwD13HPPHd7gPkQlHucPOpGOxUp7eiGEEEJUnNThEEIIIUTFScIhhBBCiIqThEMIIYQQFScJhxBCCCEqThIOIYQQQlScJBxCCCGEqDhJOIQQQghRcZJwCCGEEKLiJOEQQgghjmMvvvgil1xyCU1NTWiaxm9/+9uDvg+lFN/97neZNm0aHo+H5uZmbr/99oO6D+mlIoQQQhzHkskkp556Ktdddx2XXXbZR7qPG2+8kWeeeYbvfve7zJo1i6GhIYaGhg7qPqS0uRBCCHGC0DSNJ554YkJTwGw2y80338yvfvUrRkZGmDlzJnfddRcf//jHAVizZg1dXV2sXr36kJrMyZKKEEIIcQL78pe/zKuvvsqjjz7KW2+9xec+9zk+9alPsX79egB+//vfM2nSJP7whz/Q2dlJR0cH//RP/3TQMxyScAghhBAnqC1btvDQQw/x2GOPcc455zB58mS+/vWvc/bZZ/PQQw8B0N3dzebNm3nsscd45JFHePjhh1mxYgWXX375QX0v2cMhhBBCnKDefvttLMti2rRpE27PZrNUV1cDYNs22WyWRx55pHTdz372M+bMmcPatWsPeJlFEg4hhBDiBDU2NoZhGKxYsQLDMCZ8LhgMAtDY2IhpmhOSkpNOOglwZkgk4RBCCCHEPs2ePRvLsujr6+Occ87Z6zULFy6kUCiwYcMGJk+eDMC6desAaG9vP+DvJadUhBBCiOPY2NgY77//PuAkGPfddx/nnXcesViMtrY2rrrqKl5++WXuvfdeZs+eTX9/P3/+85/p6uri4osvxrZt5s2bRzAY5P7778e2bRYvXkw4HOaZZ5454HFIwiGEEEIcx55//nnOO++8PW5ftGgRDz/8MPl8nttuu41HHnmEnp4eampqOPPMM/m3f/s3Zs2aBcD27du5/vrreeaZZwgEAlx00UXce++9xGKxAx6HJBxCCCGEqDg5FiuEEEKIipOEQwghhBAVJwmHEEIIISpOEg4hhBBCVJwkHEIIIYSoOEk4hBBCCFFxknAIIYQQouIk4RBCCCFExUnCIYQQQoiKk4RDCCGEEBUnCYcQQgghKu7/B5BLeRQ0750tAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "blocks_df.plot()" + ] + }, + { + "cell_type": "markdown", + "id": "25b38d08", + "metadata": {}, + "source": [ + "### But here are the \"original\" precinct files, extracted from statewide Colorado precinct files.\n", + "### (Sources: 2016 precinct file was compiled by VEST; 2018 precinct file was compiled by Haley Colgate with assistance from Todd Blees of the Colorado State Demographer's office, and 2020 file was compiled by Louis Pino of the Colorado State Legislative staff.)" + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "8c6da770", + "metadata": {}, + "outputs": [], + "source": [ + "precincts2016_orig_df = gpd.read_file(\"../examples/Shapefiles/DenverCo_precincts2016_orig/DenverCo_precincts2016_orig.shp\")\n", + "precincts2018_orig_df = gpd.read_file(\"../examples/Shapefiles/DenverCo_precincts2018_orig/DenverCo_precincts2018_orig.shp\")\n", + "precincts2020_orig_df = gpd.read_file(\"../examples/Shapefiles/DenverCo_precincts2020_orig/DenverCo_precincts2020_orig.shp\")" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "6d4973d1", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|████████████████████████████████████████| 356/356 [00:00<00:00, 826.87it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 27 overlaps.\n", + "There are 33 holes.\n" + ] + }, + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "maup.doctor(precincts2020_orig_df)" + ] + }, + { + "cell_type": "markdown", + "id": "57ae1c50", + "metadata": {}, + "source": [ + "### When we assigned blocks to precincts above, every block was assigned to a precinct; we can confirm this by checking for unassigned blocks:" + ] + }, + { + "cell_type": "code", + "execution_count": 51, + "id": "8cb2a3ee", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n", + "0\n", + "0\n" + ] + } + ], + "source": [ + "print(len(blocks_df[blocks_to_precincts2020_assignment.isna()]))\n", + "print(len(blocks_df[blocks_to_precincts2018_assignment.isna()]))\n", + "print(len(blocks_df[blocks_to_precincts2016_assignment.isna()]))" + ] + }, + { + "cell_type": "markdown", + "id": "5726be17", + "metadata": {}, + "source": [ + "### But what if we assign blocks to the original versions?" + ] + }, + { + "cell_type": "code", + "execution_count": 52, + "id": "812cf3ef", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|████████████████████████████████████████| 356/356 [00:00<00:00, 768.50it/s]\n", + "100%|████████████████████████████████████████| 356/356 [00:00<00:00, 358.08it/s]\n", + "100%|████████████████████████████████████████| 356/356 [00:00<00:00, 832.96it/s]\n", + "100%|████████████████████████████████████████| 356/356 [00:01<00:00, 302.34it/s]\n", + "100%|████████████████████████████████████████| 346/346 [00:00<00:00, 379.30it/s]\n", + "100%|███████████████████████████████████████| 346/346 [00:00<00:00, 1501.18it/s]\n" + ] + } + ], + "source": [ + "blocks_to_precincts2020_orig_assignment = maup.assign(blocks_df.geometry, precincts2020_orig_df.geometry)\n", + "blocks_to_precincts2018_orig_assignment = maup.assign(blocks_df.geometry, precincts2018_orig_df.geometry)\n", + "blocks_to_precincts2016_orig_assignment = maup.assign(blocks_df.geometry, precincts2016_orig_df.geometry)" + ] + }, + { + "cell_type": "code", + "execution_count": 53, + "id": "bbf676c1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "3\n", + "1\n", + "5\n" + ] + } + ], + "source": [ + "print(len(blocks_df[blocks_to_precincts2020_orig_assignment.isna()]))\n", + "print(len(blocks_df[blocks_to_precincts2018_orig_assignment.isna()]))\n", + "print(len(blocks_df[blocks_to_precincts2016_orig_assignment.isna()]))" + ] + }, + { + "cell_type": "markdown", + "id": "72389fe9", + "metadata": {}, + "source": [ + "### So they all missed a few!" + ] + }, + { + "cell_type": "code", + "execution_count": 54, + "id": "91f7c456", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
STATEFP20COUNTYFP20TRACTCE20BLOCKCE20GEOID20NAME20CD116SLDL20SLDU20CD118SLDL22SLDU22MTFCC20FUNCSTAT20ALAND20AWATER20INTPTLAT20INTPTLON20TOTPOP20WHITE20BLACK20AMIN20ASIAN20NHPI20OTHER202MORE20HISP20NH_WHITE20NH_BLACK20NH_AMIN20NH_ASIAN20NH_NHPI20NH_OTHER20NH_2MORE20VAP20HVAP20WVAP20BVAP20AMINVAP20ASIANVAP20NHPIVAP20OTHERVAP202MOREVAP20H_WHITE20H_BLACK20H_AMIN20H_ASIAN20H_NHPI20H_OTHER20H_2MORE20TOTPOP20RHISP20RNHWHITE20RNHBLACK20RNHAMIN20RNHASIAN20RNH_NHPI20RNHOTHER20RNH2MORE20RWHITE20RBLACK20RAMIN20RASIAN20RNHPI20ROTHER20R2MORE20RgeometryPRES16DPRES16RSEN16DSEN16RAG18DAG18RSOS18DSOS18RTRE18DTRE18RGOV18DGOV18RREG18DREG18RPRES20DSEN20DPRES20RSEN20R
2896080319800011037080319800011037Block 103717331733G5040S211325620+39.8260443-104.6282154000000000000000000000000000000000000000000000000POLYGON ((3244624.987 1726684.593, 3244656.485...0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
6683080319800011039080319800011039Block 103917331733G5040S705520+39.8245484-104.6206486000000000000000000000000000000000000000000000000POLYGON ((3246737.729 1727192.367, 3247192.322...0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
8656080319800011038080319800011038Block 103817331733G5040S2373580+39.8247377-104.6244984000000000000000000000000000000000000000000000000POLYGON ((3244825.443 1726402.364, 3244867.095...0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
\n", + "
" + ], + "text/plain": [ + " STATEFP20 COUNTYFP20 TRACTCE20 BLOCKCE20 GEOID20 NAME20 \\\n", + "2896 08 031 980001 1037 080319800011037 Block 1037 \n", + "6683 08 031 980001 1039 080319800011039 Block 1039 \n", + "8656 08 031 980001 1038 080319800011038 Block 1038 \n", + "\n", + " CD116 SLDL20 SLDU20 CD118 SLDL22 SLDU22 MTFCC20 FUNCSTAT20 \\\n", + "2896 1 7 33 1 7 33 G5040 S \n", + "6683 1 7 33 1 7 33 G5040 S \n", + "8656 1 7 33 1 7 33 G5040 S \n", + "\n", + " ALAND20 AWATER20 INTPTLAT20 INTPTLON20 TOTPOP20 WHITE20 \\\n", + "2896 2113 25620 +39.8260443 -104.6282154 0 0 \n", + "6683 70552 0 +39.8245484 -104.6206486 0 0 \n", + "8656 237358 0 +39.8247377 -104.6244984 0 0 \n", + "\n", + " BLACK20 AMIN20 ASIAN20 NHPI20 OTHER20 2MORE20 HISP20 NH_WHITE20 \\\n", + "2896 0 0 0 0 0 0 0 0 \n", + "6683 0 0 0 0 0 0 0 0 \n", + "8656 0 0 0 0 0 0 0 0 \n", + "\n", + " NH_BLACK20 NH_AMIN20 NH_ASIAN20 NH_NHPI20 NH_OTHER20 NH_2MORE20 \\\n", + "2896 0 0 0 0 0 0 \n", + "6683 0 0 0 0 0 0 \n", + "8656 0 0 0 0 0 0 \n", + "\n", + " VAP20 HVAP20 WVAP20 BVAP20 AMINVAP20 ASIANVAP20 NHPIVAP20 \\\n", + "2896 0 0 0 0 0 0 0 \n", + "6683 0 0 0 0 0 0 0 \n", + "8656 0 0 0 0 0 0 0 \n", + "\n", + " OTHERVAP20 2MOREVAP20 H_WHITE20 H_BLACK20 H_AMIN20 H_ASIAN20 \\\n", + "2896 0 0 0 0 0 0 \n", + "6683 0 0 0 0 0 0 \n", + "8656 0 0 0 0 0 0 \n", + "\n", + " H_NHPI20 H_OTHER20 H_2MORE20 TOTPOP20R HISP20R NHWHITE20R \\\n", + "2896 0 0 0 0 0 0 \n", + "6683 0 0 0 0 0 0 \n", + "8656 0 0 0 0 0 0 \n", + "\n", + " NHBLACK20R NHAMIN20R NHASIAN20R NH_NHPI20R NHOTHER20R NH2MORE20R \\\n", + "2896 0 0 0 0 0 0 \n", + "6683 0 0 0 0 0 0 \n", + "8656 0 0 0 0 0 0 \n", + "\n", + " WHITE20R BLACK20R AMIN20R ASIAN20R NHPI20R OTHER20R 2MORE20R \\\n", + "2896 0 0 0 0 0 0 0 \n", + "6683 0 0 0 0 0 0 0 \n", + "8656 0 0 0 0 0 0 0 \n", + "\n", + " geometry PRES16D PRES16R \\\n", + "2896 POLYGON ((3244624.987 1726684.593, 3244656.485... 0.0 0.0 \n", + "6683 POLYGON ((3246737.729 1727192.367, 3247192.322... 0.0 0.0 \n", + "8656 POLYGON ((3244825.443 1726402.364, 3244867.095... 0.0 0.0 \n", + "\n", + " SEN16D SEN16R AG18D AG18R SOS18D SOS18R TRE18D TRE18R GOV18D \\\n", + "2896 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "6683 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "8656 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "\n", + " GOV18R REG18D REG18R PRES20D SEN20D PRES20R SEN20R \n", + "2896 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "6683 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "8656 0.0 0.0 0.0 0.0 0.0 0.0 0.0 " + ] + }, + "execution_count": 54, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[blocks_to_precincts2020_orig_assignment.isna()]" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "id": "4689ef17", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
STATEFP20COUNTYFP20TRACTCE20BLOCKCE20GEOID20NAME20CD116SLDL20SLDU20CD118SLDL22SLDU22MTFCC20FUNCSTAT20ALAND20AWATER20INTPTLAT20INTPTLON20TOTPOP20WHITE20BLACK20AMIN20ASIAN20NHPI20OTHER202MORE20HISP20NH_WHITE20NH_BLACK20NH_AMIN20NH_ASIAN20NH_NHPI20NH_OTHER20NH_2MORE20VAP20HVAP20WVAP20BVAP20AMINVAP20ASIANVAP20NHPIVAP20OTHERVAP202MOREVAP20H_WHITE20H_BLACK20H_AMIN20H_ASIAN20H_NHPI20H_OTHER20H_2MORE20TOTPOP20RHISP20RNHWHITE20RNHBLACK20RNHAMIN20RNHASIAN20RNH_NHPI20RNHOTHER20RNH2MORE20RWHITE20RBLACK20RAMIN20RASIAN20RNHPI20ROTHER20R2MORE20RgeometryPRES16DPRES16RSEN16DSEN16RAG18DAG18RSOS18DSOS18RTRE18DTRE18RGOV18DGOV18RREG18DREG18RPRES20DSEN20DPRES20RSEN20R
9812080310083882009080310083882009Block 200917331733G5040S16110+39.7728637-104.8004050000000000000000000000000000000000000000000000000POLYGON ((3196304.708 1707166.383, 3197014.777...0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
\n", + "
" + ], + "text/plain": [ + " STATEFP20 COUNTYFP20 TRACTCE20 BLOCKCE20 GEOID20 NAME20 \\\n", + "9812 08 031 008388 2009 080310083882009 Block 2009 \n", + "\n", + " CD116 SLDL20 SLDU20 CD118 SLDL22 SLDU22 MTFCC20 FUNCSTAT20 \\\n", + "9812 1 7 33 1 7 33 G5040 S \n", + "\n", + " ALAND20 AWATER20 INTPTLAT20 INTPTLON20 TOTPOP20 WHITE20 \\\n", + "9812 1611 0 +39.7728637 -104.8004050 0 0 \n", + "\n", + " BLACK20 AMIN20 ASIAN20 NHPI20 OTHER20 2MORE20 HISP20 NH_WHITE20 \\\n", + "9812 0 0 0 0 0 0 0 0 \n", + "\n", + " NH_BLACK20 NH_AMIN20 NH_ASIAN20 NH_NHPI20 NH_OTHER20 NH_2MORE20 \\\n", + "9812 0 0 0 0 0 0 \n", + "\n", + " VAP20 HVAP20 WVAP20 BVAP20 AMINVAP20 ASIANVAP20 NHPIVAP20 \\\n", + "9812 0 0 0 0 0 0 0 \n", + "\n", + " OTHERVAP20 2MOREVAP20 H_WHITE20 H_BLACK20 H_AMIN20 H_ASIAN20 \\\n", + "9812 0 0 0 0 0 0 \n", + "\n", + " H_NHPI20 H_OTHER20 H_2MORE20 TOTPOP20R HISP20R NHWHITE20R \\\n", + "9812 0 0 0 0 0 0 \n", + "\n", + " NHBLACK20R NHAMIN20R NHASIAN20R NH_NHPI20R NHOTHER20R NH2MORE20R \\\n", + "9812 0 0 0 0 0 0 \n", + "\n", + " WHITE20R BLACK20R AMIN20R ASIAN20R NHPI20R OTHER20R 2MORE20R \\\n", + "9812 0 0 0 0 0 0 0 \n", + "\n", + " geometry PRES16D PRES16R \\\n", + "9812 POLYGON ((3196304.708 1707166.383, 3197014.777... 0.0 0.0 \n", + "\n", + " SEN16D SEN16R AG18D AG18R SOS18D SOS18R TRE18D TRE18R GOV18D \\\n", + "9812 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "\n", + " GOV18R REG18D REG18R PRES20D SEN20D PRES20R SEN20R \n", + "9812 0.0 0.0 0.0 0.0 0.0 0.0 0.0 " + ] + }, + "execution_count": 55, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[blocks_to_precincts2018_orig_assignment.isna()]" + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "id": "96ab7a1e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
STATEFP20COUNTYFP20TRACTCE20BLOCKCE20GEOID20NAME20CD116SLDL20SLDU20CD118SLDL22SLDU22MTFCC20FUNCSTAT20ALAND20AWATER20INTPTLAT20INTPTLON20TOTPOP20WHITE20BLACK20AMIN20ASIAN20NHPI20OTHER202MORE20HISP20NH_WHITE20NH_BLACK20NH_AMIN20NH_ASIAN20NH_NHPI20NH_OTHER20NH_2MORE20VAP20HVAP20WVAP20BVAP20AMINVAP20ASIANVAP20NHPIVAP20OTHERVAP202MOREVAP20H_WHITE20H_BLACK20H_AMIN20H_ASIAN20H_NHPI20H_OTHER20H_2MORE20TOTPOP20RHISP20RNHWHITE20RNHBLACK20RNHAMIN20RNHASIAN20RNH_NHPI20RNHOTHER20RNH2MORE20RWHITE20RBLACK20RAMIN20RASIAN20RNHPI20ROTHER20R2MORE20RgeometryPRES16DPRES16RSEN16DSEN16RAG18DAG18RSOS18DSOS18RTRE18DTRE18RGOV18DGOV18RREG18DREG18RPRES20DSEN20DPRES20RSEN20R
1997080310083887008080310083887008Block 700817331733G5040S102390+39.7729509-104.8078405000000000000000000000000000000000000000000000000POLYGON ((3193984.432 1707215.315, 3193983.602...0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
2592080310083887007080310083887007Block 700717331733G5040S14660+39.7731435-104.8088405000000000000000000000000000000000000000000000000POLYGON ((3194032.231 1707250.650, 3194531.988...0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
2729080310156002004080310156002004Block 200412321132G5040S30492100+39.6658476-105.0041615000000000000000000000000000000000000000000000000POLYGON ((3139496.394 1667748.388, 3139520.253...0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
8262080310083887009080310083887009Block 700917331733G5040S62600+39.7729837-104.8051077000000000000000000000000000000000000000000000000POLYGON ((3194531.881 1707232.230, 3194531.988...0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
9812080310083882009080310083882009Block 200917331733G5040S16110+39.7728637-104.8004050000000000000000000000000000000000000000000000000POLYGON ((3196304.708 1707166.383, 3197014.777...0.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.00.0
\n", + "
" + ], + "text/plain": [ + " STATEFP20 COUNTYFP20 TRACTCE20 BLOCKCE20 GEOID20 NAME20 \\\n", + "1997 08 031 008388 7008 080310083887008 Block 7008 \n", + "2592 08 031 008388 7007 080310083887007 Block 7007 \n", + "2729 08 031 015600 2004 080310156002004 Block 2004 \n", + "8262 08 031 008388 7009 080310083887009 Block 7009 \n", + "9812 08 031 008388 2009 080310083882009 Block 2009 \n", + "\n", + " CD116 SLDL20 SLDU20 CD118 SLDL22 SLDU22 MTFCC20 FUNCSTAT20 \\\n", + "1997 1 7 33 1 7 33 G5040 S \n", + "2592 1 7 33 1 7 33 G5040 S \n", + "2729 1 2 32 1 1 32 G5040 S \n", + "8262 1 7 33 1 7 33 G5040 S \n", + "9812 1 7 33 1 7 33 G5040 S \n", + "\n", + " ALAND20 AWATER20 INTPTLAT20 INTPTLON20 TOTPOP20 WHITE20 \\\n", + "1997 10239 0 +39.7729509 -104.8078405 0 0 \n", + "2592 1466 0 +39.7731435 -104.8088405 0 0 \n", + "2729 3049 2100 +39.6658476 -105.0041615 0 0 \n", + "8262 6260 0 +39.7729837 -104.8051077 0 0 \n", + "9812 1611 0 +39.7728637 -104.8004050 0 0 \n", + "\n", + " BLACK20 AMIN20 ASIAN20 NHPI20 OTHER20 2MORE20 HISP20 NH_WHITE20 \\\n", + "1997 0 0 0 0 0 0 0 0 \n", + "2592 0 0 0 0 0 0 0 0 \n", + "2729 0 0 0 0 0 0 0 0 \n", + "8262 0 0 0 0 0 0 0 0 \n", + "9812 0 0 0 0 0 0 0 0 \n", + "\n", + " NH_BLACK20 NH_AMIN20 NH_ASIAN20 NH_NHPI20 NH_OTHER20 NH_2MORE20 \\\n", + "1997 0 0 0 0 0 0 \n", + "2592 0 0 0 0 0 0 \n", + "2729 0 0 0 0 0 0 \n", + "8262 0 0 0 0 0 0 \n", + "9812 0 0 0 0 0 0 \n", + "\n", + " VAP20 HVAP20 WVAP20 BVAP20 AMINVAP20 ASIANVAP20 NHPIVAP20 \\\n", + "1997 0 0 0 0 0 0 0 \n", + "2592 0 0 0 0 0 0 0 \n", + "2729 0 0 0 0 0 0 0 \n", + "8262 0 0 0 0 0 0 0 \n", + "9812 0 0 0 0 0 0 0 \n", + "\n", + " OTHERVAP20 2MOREVAP20 H_WHITE20 H_BLACK20 H_AMIN20 H_ASIAN20 \\\n", + "1997 0 0 0 0 0 0 \n", + "2592 0 0 0 0 0 0 \n", + "2729 0 0 0 0 0 0 \n", + "8262 0 0 0 0 0 0 \n", + "9812 0 0 0 0 0 0 \n", + "\n", + " H_NHPI20 H_OTHER20 H_2MORE20 TOTPOP20R HISP20R NHWHITE20R \\\n", + "1997 0 0 0 0 0 0 \n", + "2592 0 0 0 0 0 0 \n", + "2729 0 0 0 0 0 0 \n", + "8262 0 0 0 0 0 0 \n", + "9812 0 0 0 0 0 0 \n", + "\n", + " NHBLACK20R NHAMIN20R NHASIAN20R NH_NHPI20R NHOTHER20R NH2MORE20R \\\n", + "1997 0 0 0 0 0 0 \n", + "2592 0 0 0 0 0 0 \n", + "2729 0 0 0 0 0 0 \n", + "8262 0 0 0 0 0 0 \n", + "9812 0 0 0 0 0 0 \n", + "\n", + " WHITE20R BLACK20R AMIN20R ASIAN20R NHPI20R OTHER20R 2MORE20R \\\n", + "1997 0 0 0 0 0 0 0 \n", + "2592 0 0 0 0 0 0 0 \n", + "2729 0 0 0 0 0 0 0 \n", + "8262 0 0 0 0 0 0 0 \n", + "9812 0 0 0 0 0 0 0 \n", + "\n", + " geometry PRES16D PRES16R \\\n", + "1997 POLYGON ((3193984.432 1707215.315, 3193983.602... 0.0 0.0 \n", + "2592 POLYGON ((3194032.231 1707250.650, 3194531.988... 0.0 0.0 \n", + "2729 POLYGON ((3139496.394 1667748.388, 3139520.253... 0.0 0.0 \n", + "8262 POLYGON ((3194531.881 1707232.230, 3194531.988... 0.0 0.0 \n", + "9812 POLYGON ((3196304.708 1707166.383, 3197014.777... 0.0 0.0 \n", + "\n", + " SEN16D SEN16R AG18D AG18R SOS18D SOS18R TRE18D TRE18R GOV18D \\\n", + "1997 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "2592 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "2729 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "8262 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "9812 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "\n", + " GOV18R REG18D REG18R PRES20D SEN20D PRES20R SEN20R \n", + "1997 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "2592 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "2729 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "8262 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n", + "9812 0.0 0.0 0.0 0.0 0.0 0.0 0.0 " + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "blocks_df[blocks_to_precincts2016_orig_assignment.isna()]" + ] + }, + { + "cell_type": "markdown", + "id": "220f4948", + "metadata": {}, + "source": [ + "### In this case the unassigned blocks have zero population and zero votes, so they wouldn't affect the aggregation/disaggregation of data - but this isn't always the case." + ] + }, + { + "cell_type": "markdown", + "id": "8e731eaf", + "metadata": {}, + "source": [ + "### MORAL: Shapefiles often come with significant topological problems that can affect data transfer in important ways! These problems should be diagnosed and repaired to the greatest extent possible prior to moving data around. \n", + "### For details about how Maup can repair these problems, see the \"Maup smart_repair demo\" notebook!" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "324077ca", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "12fe456f", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0a2ae944", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37a767b0", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ebcc0a9e", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/notebooks/Maup smart_repair demo.ipynb b/notebooks/Maup smart_repair demo.ipynb new file mode 100644 index 0000000..45bd408 --- /dev/null +++ b/notebooks/Maup smart_repair demo.ipynb @@ -0,0 +1,874 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "f09d728f", + "metadata": {}, + "source": [ + "### Demo notebook for Maup's smart_repair function for fixing topological problems in shapefiles" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a60f23c3", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import geopandas as gpd\n", + "\n", + "from geopandas import GeoSeries, GeoDataFrame\n", + "\n", + "import shapely\n", + "from shapely.geometry import Polygon\n", + "\n", + "import maup\n", + "from maup import smart_repair, quick_repair\n", + "\n", + "import matplotlib.pyplot as plt\n" + ] + }, + { + "cell_type": "markdown", + "id": "6d6a30fd", + "metadata": {}, + "source": [ + "### First we'll explore a toy example of \"precincts\" and \"counties\":" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "333decdb", + "metadata": {}, + "outputs": [], + "source": [ + "import random\n", + "random.seed(2023) # For reproducibility\n", + "\n", + "ppolys = []\n", + "\n", + "for i in range(4):\n", + " for j in range(4):\n", + " poly = Polygon(\n", + " [(0.5*i + 0.1*k, 0.5*j + (random.random() - 0.5)/12) for k in range(6)] +\n", + " [(0.5*(i+1) + (random.random() - 0.5)/12, 0.5*j + 0.1*k) for k in range(1,6)] +\n", + " [(0.5*(i+1) - 0.1*k, 0.5*(j+1) + (random.random() - 0.5)/12) for k in range(1,6)] +\n", + " [(0.5*i + (random.random() - 0.5)/12, 0.5*(j+1) - 0.1*k) for k in range(1,5)]\n", + " )\n", + " ppolys.append(poly)\n", + " \n", + "toy_precincts_df = gpd.GeoDataFrame(geometry = gpd.GeoSeries(ppolys))" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "b4958891", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "toy_precincts_df.plot(cmap='tab20', alpha=0.7)" + ] + }, + { + "cell_type": "markdown", + "id": "810a47d7", + "metadata": {}, + "source": [ + "### Check for gaps and overlaps:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "5db0e210", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "There are 28 overlaps.\n", + "There are 23 holes.\n" + ] + }, + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "maup.doctor(toy_precincts_df)" + ] + }, + { + "cell_type": "markdown", + "id": "88cfb3eb", + "metadata": {}, + "source": [ + "### First do a basic repair of the toy precincts:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "37602357", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Snapping all geometries to a grid with precision 10^( -10 ) to avoid GEOS errors.\n", + "Identifying overlaps...\n", + "Resolving overlaps...\n", + "Assigning order 2 pieces...\n", + "Assigning order 3 pieces...\n", + "Filling gaps...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Gaps to simplify: 100%|█████████████████████████| 23/23 [00:00<00:00, 36.40it/s]\n", + "Gaps to fill: 100%|█████████████████████████████| 10/10 [00:00<00:00, 16.40it/s]\n" + ] + } + ], + "source": [ + "toy_precincts_repaired_df = smart_repair(toy_precincts_df)" + ] + }, + { + "cell_type": "markdown", + "id": "da5cfb42", + "metadata": {}, + "source": [ + "### Check that the repair succeeded:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "099facc7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "maup.doctor(toy_precincts_repaired_df)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "a9649dd3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "toy_precincts_repaired_df.plot(cmap='tab20', alpha=0.7)" + ] + }, + { + "cell_type": "markdown", + "id": "1ef948c0", + "metadata": {}, + "source": [ + "### Now suppose that the precincts are intended to nest cleanly into the following \"toy counties:\"" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "f1b508e0", + "metadata": {}, + "outputs": [], + "source": [ + "cpoly1 = Polygon([(0,0), (1,0), (1,1), (0,1)])\n", + "cpoly2 = Polygon([(1,0), (2,0), (2,1), (1,1)])\n", + "cpoly3 = Polygon([(0,1), (1,1), (1,2), (0,2)])\n", + "cpoly4 = Polygon([(1,1), (2,1), (2,2), (1,2)])\n", + "\n", + "toy_counties_df = gpd.GeoDataFrame(geometry = gpd.GeoSeries([cpoly1, cpoly2, cpoly3, cpoly4]))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "fe27dff1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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9zgt/X6GQiH97W9/66KuzJWcnKzi4mJt2rRJmzZtUkpKiiZNmqT9+/d3dWvdSlNTkxYvXqyf/vSnuvvuu1us6+jXzx7x7fDo/rKyspSVlRV5PGHCBN155536/e9/r+eff74LOwO+/z9WI0aMiDyeMGGCjh8/rldeeUV/+MMfurCz7mXRokU6dOiQduzY0aV99IorrsGDBys2Nla1tbVR87W1tXK73c2ucbvdtup7k9aczx+74YYbNHbsWB07dqwjWuzxWvr9TEhIUN++fbuoq55l/Pjx/H7+H48//rg+/PBDffbZZ9f8M1Id/frZK4IrLi5O48aNU3l5eWSuqalJ5eXlUVcB/1dWVlZUvSRt3bq1xfrepDXn88caGxt18OBBJScnd1SbPRq/nx2vqqqK3099/wceH3/8cb3//vv69NNPlZaWds01Hf772S63eBhg3bp1ltPptEpKSqz/+Z//sR577DFrwIABViAQsCzLsh555BFr2bJlkfrPP//c6tOnj/Xyyy9bhw8ftgoKCqwbbrjBOnjwYFcdQrdi93wuX77c+vjjj63jx49b+/bts2bNmmXFx8dbX3zxRVcdQrdy8eJF68CBA9aBAwcsSdaKFSusAwcOWF9//bVlWZa1bNky65FHHonU/+Uvf7H69etnPf3009bhw4etoqIiKzY21iorK+uqQ+hW7J7PV155xSotLbWOHj1qHTx40HriiSesmJgY609/+lNXHUK3sXDhQsvlclkVFRVWTU1NZPz1r3+N1HT262evCS7Lsqz/+I//sG699VYrLi7OGj9+vLVr167IcxMnTrTmzp0bVb9hwwbrjjvusOLi4qxRo0ZZmzdv7uSOuzc753Px4sWR2qSkJOvv//7vrf3793dB193T5duxfzwun8O5c+daEydOvGJNRkaGFRcXZw0dOtR6++23O73v7sru+XzxxRet22+/3YqPj7cGDRpkTZo0yfr000+7pvluprnzKCnq962zXz/5syYAAKP0ive4AAA9B8EFADAKwQUAMArBBQAwCsEFADAKwQUAMArBBQAwCsEFADAKwQUAMArBBQAwCsEFADAKwQUAMMr/AwywEQrhMdU0AAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "toy_counties_df.plot(cmap='tab20')" + ] + }, + { + "cell_type": "markdown", + "id": "703128a2", + "metadata": {}, + "source": [ + "### We can perform a \"county-aware\" repair as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "b36d3de2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Snapping all geometries to a grid with precision 10^( -10 ) to avoid GEOS errors.\n", + "Identifying overlaps...\n", + "Resolving overlaps and filling gaps...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Gaps to simplify in region 0: 100%|█████████████| 16/16 [00:00<00:00, 59.89it/s]\n", + "Gaps to fill in region 0: 100%|███████████████████| 4/4 [00:00<00:00, 46.28it/s]\n", + "Gaps to simplify in region 1: 100%|█████████████| 17/17 [00:00<00:00, 43.15it/s]\n", + "Gaps to fill in region 1: 100%|███████████████████| 7/7 [00:00<00:00, 21.96it/s]\n", + "Gaps to simplify in region 2: 100%|█████████████| 15/15 [00:00<00:00, 48.20it/s]\n", + "Gaps to fill in region 2: 100%|███████████████████| 7/7 [00:00<00:00, 26.13it/s]\n", + "Gaps to simplify in region 3: 100%|█████████████| 17/17 [00:00<00:00, 45.25it/s]\n", + "Gaps to fill in region 3: 100%|███████████████████| 5/5 [00:00<00:00, 18.95it/s]\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "toy_precincts_repaired_county_aware_df = smart_repair(toy_precincts_df, nest_within_regions = toy_counties_df)\n", + "toy_precincts_repaired_county_aware_df.plot(cmap = \"tab20\", alpha=0.7)" + ] + }, + { + "cell_type": "markdown", + "id": "55e77296", + "metadata": {}, + "source": [ + "### Next, suppose that we'd like to get rid of small rook adjacencies at corner points where 4 precincts meet. We might reasonably estimate that these all have length less than 0.1, so we can accomplish this as follows:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "84a7eb10", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Snapping all geometries to a grid with precision 10^( -10 ) to avoid GEOS errors.\n", + "Identifying overlaps...\n", + "Resolving overlaps and filling gaps...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Gaps to simplify in region 0: 100%|█████████████| 16/16 [00:00<00:00, 56.65it/s]\n", + "Gaps to fill in region 0: 100%|███████████████████| 4/4 [00:00<00:00, 44.55it/s]\n", + "Gaps to simplify in region 1: 100%|█████████████| 17/17 [00:00<00:00, 43.17it/s]\n", + "Gaps to fill in region 1: 100%|███████████████████| 7/7 [00:00<00:00, 22.09it/s]\n", + "Gaps to simplify in region 2: 100%|█████████████| 15/15 [00:00<00:00, 47.50it/s]\n", + "Gaps to fill in region 2: 100%|███████████████████| 7/7 [00:00<00:00, 26.23it/s]\n", + "Gaps to simplify in region 3: 100%|█████████████| 17/17 [00:00<00:00, 46.05it/s]\n", + "Gaps to fill in region 3: 100%|███████████████████| 5/5 [00:00<00:00, 19.08it/s]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Converting small rook adjacencies to queen...\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "toy_precincts_repaired_county_aware_rook_to_queen_df = smart_repair(toy_precincts_df, nest_within_regions = toy_counties_df, min_rook_length = 0.1)\n", + "toy_precincts_repaired_county_aware_rook_to_queen_df.plot(cmap = \"tab20\", alpha=0.7)" + ] + }, + { + "cell_type": "markdown", + "id": "5c5473a3", + "metadata": {}, + "source": [ + "### The difference is hard to see, so let's zoom in on gap between the 4 original precincts in the upper left-hand corner.\n", + "\n", + "### Original precincts:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "3c5faa10", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "f, ax = plt.subplots(1)\n", + "\n", + "ax.set_xlim(0.35, 0.7)\n", + "ax.set_ylim(1.35, 1.7)\n", + "#remove x axis and y axis\n", + "plt.yticks([])\n", + "plt.xticks([])\n", + "\n", + "toy_precincts_df.plot(ax=ax, cmap='tab20', alpha=0.7)\n", + "\n", + "plt.box(on=True)\n", + "\n", + "plt.show()\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "id": "76172f30", + "metadata": {}, + "source": [ + "### County-aware repair:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "d78b3465", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "f, ax = plt.subplots(1)\n", + "\n", + "ax.set_xlim(0.35, 0.7)\n", + "ax.set_ylim(1.35, 1.7)\n", + "#remove x axis and y axis\n", + "plt.yticks([])\n", + "plt.xticks([])\n", + "\n", + "toy_precincts_repaired_county_aware_df.plot(ax=ax, cmap='tab20', alpha=0.7)\n", + "\n", + "plt.box(on=True)\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "markdown", + "id": "d4f13d55", + "metadata": {}, + "source": [ + "### County-aware repair with rook adjacency converted to queen" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "1ad963e8", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "f, ax = plt.subplots(1)\n", + "\n", + "ax.set_xlim(0.35, 0.7)\n", + "ax.set_ylim(1.35, 1.7)\n", + "#remove x axis and y axis\n", + "plt.yticks([])\n", + "plt.xticks([])\n", + "\n", + "toy_precincts_repaired_county_aware_rook_to_queen_df.plot(ax=ax, cmap='tab20', alpha=0.7)\n", + "\n", + "plt.box(on=True)\n", + "\n", + "plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "66485de1", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9d2c67ca", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "48dfabae", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "382b91a3", + "metadata": {}, + "source": [ + "### Now let's look a particularly gnarly gap from the Colorado 2020 precinct shapefile. This region consists of 15 precincts that all adjoin a single gap along a county boundary." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "bcfcf407", + "metadata": {}, + "outputs": [], + "source": [ + "bad_gap_region_df = gpd.read_file(\"../examples/Shapefiles/bad_gap_region/bad_gap_region.shp\")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "91e76d9d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "bad_gap_region_df.plot(cmap='tab20')" + ] + }, + { + "cell_type": "markdown", + "id": "9cb29918", + "metadata": {}, + "source": [ + "### The gap is hard to see, so let's stretch it out:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "84c4b87e", + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "f, ax = plt.subplots(1)\n", + "\n", + "plt.yticks([])\n", + "plt.xticks([])\n", + "\n", + "plt.xlim([3028000, 3029500])\n", + "plt.ylim([1600000, 1756000])\n", + "\n", + "bad_gap_region_df.plot(ax=ax, cmap='tab20', aspect=0.01)" + ] + }, + { + "cell_type": "markdown", + "id": "e251bad0", + "metadata": {}, + "source": [ + "### Maup's old \"autorepair\" function (now called \"quick_repair\") will assign this entire gap to a single precinct, thereby creating inaccurate adjacency relations between precincts adjoining the gap:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "5bf6378c", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/jnc/Research and Presentations/Gerrymandering and elections/Maup stuff/maup_2_0_0/repair.py:384: UserWarning: The indices of the two GeoSeries are different.\n", + " result = targets.union(sources_to_absorb)\n", + "/Users/jnc/Research and Presentations/Gerrymandering and elections/Maup stuff/maup_2_0_0/repair.py:384: UserWarning: The indices of the two GeoSeries are different.\n", + " result = targets.union(sources_to_absorb)\n" + ] + } + ], + "source": [ + "bad_gap_region_quick_repair_df = quick_repair(bad_gap_region_df)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "1d73521a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "f, ax = plt.subplots(1)\n", + "\n", + "plt.yticks([])\n", + "plt.xticks([])\n", + "\n", + "plt.xlim([3028000, 3029500])\n", + "plt.ylim([1600000, 1756000])\n", + "\n", + "bad_gap_region_quick_repair_df.plot(ax=ax, cmap='tab20', aspect=0.01)" + ] + }, + { + "cell_type": "markdown", + "id": "0ffaeff5", + "metadata": {}, + "source": [ + "### The \"smart_repair\" function does a much better job:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "2f16f2a4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Snapping all geometries to a grid with precision 10^( -5 ) to avoid GEOS errors.\n", + "Identifying overlaps...\n", + "Resolving overlaps...\n", + "Assigning order 2 pieces...\n", + "Filling gaps...\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Gaps to simplify: 100%|███████████████████████████| 3/3 [00:10<00:00, 3.34s/it]\n", + "Gaps to fill: 100%|███████████████████████████████| 1/1 [00:04<00:00, 4.98s/it]\n" + ] + } + ], + "source": [ + "bad_gap_region_smart_repair_df = smart_repair(bad_gap_region_df)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "0f2a3b6d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "f, ax = plt.subplots(1)\n", + "\n", + "plt.yticks([])\n", + "plt.xticks([])\n", + "\n", + "plt.xlim([3028000, 3029500])\n", + "plt.ylim([1600000, 1756000])\n", + "\n", + "bad_gap_region_smart_repair_df.plot(ax=ax, cmap='tab20', aspect=0.01)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "006d434e", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "679dc6f7", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "81a6aaea", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b8bb2b0", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9ea7919f", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/pyproject.toml b/pyproject.toml index 57fda45..175fad7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "maup" -version = "1.1.3" +version = "2.0.0" description = "The geospatial toolkit for redistricting data" authors = [ "Metric Geometry and Gerrymandering Group ", diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/conftest.py b/tests/conftest.py index 34d7f68..3f6c1b0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,7 +1,7 @@ import geopandas as gp +import pandas as pd import pytest from shapely.geometry import Polygon -import pandas as pd import maup CRS = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs" @@ -90,10 +90,9 @@ def square_mostly_in_top_left(): def squares_some_neat_some_overlapping( square_mostly_in_top_left, squares_within_four_square_grid ): - result = pd.concat( - [squares_within_four_square_grid, square_mostly_in_top_left], - ignore_index=True, - ) + result = pd.concat([squares_within_four_square_grid, + square_mostly_in_top_left], ignore_index=True + ) result.crs = CRS return result diff --git a/tests/test_crs.py b/tests/test_crs.py index f617870..7fe09ea 100644 --- a/tests/test_crs.py +++ b/tests/test_crs.py @@ -5,8 +5,8 @@ def test_require_same_crs(square, four_square_grid): square_gdf = gpd.GeoDataFrame([{"geometry": square}]) - square_gdf.crs = "epsg:4269" - four_square_grid.crs = "epsg:4326" + square_gdf.crs = 4432 + four_square_grid.crs = 4433 @require_same_crs def f(sources, targets): diff --git a/tests/test_repair.py b/tests/test_repair.py index 0801258..6747e10 100644 --- a/tests/test_repair.py +++ b/tests/test_repair.py @@ -1,6 +1,6 @@ import geopandas import maup -from maup.repair import count_overlaps +from maup.repair import count_overlaps, autorepair, quick_repair import pytest # These tests are losely based off the test_example_case in test_prorate.py @@ -35,12 +35,14 @@ def test_example_autorepair_MI(): assert holes.unary_union.area > 100 assert len(holes) > 0 - shp["geometry"] = maup.autorepair(shp, relative_threshold=None) + shp["geometry"] = maup.quick_repair(shp, relative_threshold=None) assert count_overlaps(shp) == 0 holes = maup.repair.holes_of_union(shp) assert holes.empty or holes.unary_union.area < 1e-10 # overlaps are not guaranteed to disappear - assert maup.doctor(shp) + # This assertion is a terrible idea since there will almost certainly still be small + # gaps and overlaps remaining! + # assert maup.doctor(shp) def test_snap_shp_to_grid(): shp = geopandas.read_file("zip://./examples/MI.zip") # MI shapefile @@ -97,4 +99,8 @@ def test_crop_to(): def test_apply_func_error(): with pytest.raises(TypeError): - maup.repair.apply_func_to_polygon_parts("not a Polygon object", lambda x: x) \ No newline at end of file + maup.repair.apply_func_to_polygon_parts("not a Polygon object", lambda x: x) + + +# def test_quick_repair_equals_autorepair(): + \ No newline at end of file diff --git a/tests/test_smart_repair.py b/tests/test_smart_repair.py new file mode 100644 index 0000000..a6912d1 --- /dev/null +++ b/tests/test_smart_repair.py @@ -0,0 +1,83 @@ +import random +import geopandas +import maup +import pytest +from shapely.geometry import Point, Polygon + +from maup import assign, doctor +from maup.adjacencies import adjacencies +from maup.smart_repair import smart_repair + + +@pytest.fixture +def toy_precincts_geoseries(): + random.seed(2023) + ppolys = [] + for i in range(4): + for j in range(4): + poly = Polygon( + [(0.5*i + 0.1*k, 0.5*j + (random.random() - 0.5)/12) for k in range(6)] + + [(0.5*(i+1) + (random.random() - 0.5)/12, 0.5*j + 0.1*k) for k in range(1,6)] + + [(0.5*(i+1) - 0.1*k, 0.5*(j+1) + (random.random() - 0.5)/12) for k in range(1,6)] + + [(0.5*i + (random.random() - 0.5)/12, 0.5*(j+1) - 0.1*k) for k in range(1,5)] + ) + ppolys.append(poly) + + return geopandas.GeoSeries(ppolys) + +@pytest.fixture +def toy_precincts_geodataframe(): + random.seed(2023) + ppolys = [] + for i in range(4): + for j in range(4): + poly = Polygon( + [(0.5*i + 0.1*k, 0.5*j + (random.random() - 0.5)/12) for k in range(6)] + + [(0.5*(i+1) + (random.random() - 0.5)/12, 0.5*j + 0.1*k) for k in range(1,6)] + + [(0.5*(i+1) - 0.1*k, 0.5*(j+1) + (random.random() - 0.5)/12) for k in range(1,6)] + + [(0.5*i + (random.random() - 0.5)/12, 0.5*(j+1) - 0.1*k) for k in range(1,5)] + ) + ppolys.append(poly) + + return geopandas.GeoDataFrame(geometry = geopandas.GeoSeries(ppolys)) + +@pytest.fixture +def toy_counties_geodataframe(): + cpoly1 = Polygon([(0,0), (1,0), (1,1), (0,1)]) + cpoly2 = Polygon([(1,0), (2,0), (2,1), (1,1)]) + cpoly3 = Polygon([(0,1), (1,1), (1,2), (0,2)]) + cpoly4 = Polygon([(1,1), (2,1), (2,2), (1,2)]) + + return geopandas.GeoDataFrame(geometry = geopandas.GeoSeries([cpoly1, cpoly2, cpoly3, cpoly4])) + + +class TestSmartRepair: + def test_smart_repair_basic_output_from_gdf_clean(self, toy_precincts_geodataframe): + repaired_gdf = smart_repair(toy_precincts_geodataframe) + assert isinstance(repaired_gdf, geopandas.GeoDataFrame) + assert doctor(repaired_gdf) + + def test_smart_repair_basic_output_from_gs_clean(self, toy_precincts_geoseries): + repaired_gs = smart_repair(toy_precincts_geoseries) + assert isinstance(repaired_gs, geopandas.GeoSeries) + assert doctor(repaired_gs) + + def test_nest_within_regions(self, toy_precincts_geodataframe, toy_counties_geodataframe): + repaired_with_regions_gdf = smart_repair(toy_precincts_geodataframe, + nest_within_regions = toy_counties_geodataframe + ) + p_to_c = assign(toy_precincts_geodataframe, toy_counties_geodataframe) + for p in p_to_c.index: + assert toy_counties_geodataframe.geometry[p_to_c[p]].contains(repaired_with_regions_gdf.geometry[p]) + + def test_small_rook_to_queen(self, toy_precincts_geodataframe): + repaired_basic_gdf = smart_repair(toy_precincts_geodataframe) + assert min(adjacencies(repaired_basic_gdf).length) < 0.05 + + repaired_srtq_gdf = smart_repair(toy_precincts_geodataframe, min_rook_length=0.05) + assert min(adjacencies(repaired_srtq_gdf).length) > 0.05 + + +# There should also be a lot of unit tests for all the component functions, +# but this could mushroom into a BIG project that will have to wait for another day! +