Source code for gerrytools.data.remap

import ast
from typing import Callable


[docs] def remap(plans, unitmaps, popmap=None) -> Callable: """ Re-maps assignments to the specified set of units. Args: plans (DataFrame): The Pandas DataFrame produced by ``tabularized()``. unitmaps (dict): A dictionary whose keys are unit types appearing in the `unitsType` column, and whose values are dictionaries mapping unique identifiers of one set of geometries to unique identifiers (or lists of unique identifiers) of another set of geometries; these correspond to mappings generated by `unitmap()` and the inverse mapping generated by `invert()`. popmap (dict, optional): A mapping from unit unique identifiers to population values. Only applies when we are mapping from smaller units to larger ones. Returns: A function """ def _(row): # Get the assignment for the row. assignment = ast.literal_eval(row["plan"]) # Attempt to get the type of units specified by the row; if we # can't – i.e.the user didn't specify a unit mapping corresponding # to that unit typein `unitmaps` – we leave the assignment alone and warn the user. try: unitsType = row["units"] unitmap = unitmaps[unitsType] except BaseException: print(f"No unit mapping provided for {row['units']}; skipping.") return assignment # What kind of mapping do we have? If `mapping` is from a single key # to a single value, then we're mapping units one-to-one (e.g. block # IDs to VTD IDs); otherwise, we're mapping units one-to-many (e.g. # VTD IDs to blocks). If `mapping` is of the former type, then # it's possible that some larger units may comprise smaller units # in multiple districts. If this is the case, then we assign larger # units to the district in which most of the larger unit's population # resides; otherwise, we simply assign all smaller units to whichever # district the larger unit's in. firstvalue = next(iter(unitmap.values())) unitmapdirection = "down" # Mark which kind of mapping we have. if isinstance(firstvalue, list): unitmapdirection = "down" else: unitmapdirection = "up" # Now, based on the mapping type, return the appropriate mapping. if unitmapdirection == "down": return _down(unitmap, assignment) return _up(unitmap, popmap, assignment) plans["plan"] = plans.apply(_, axis=1) return plans
def _down(unitmap, assignment) -> dict: """ Maps districting assignments from larger units to smaller units (which nest in the larger units). Args: unitmap (dict): Dictionary which maps larger unit identifiers to lists of smaller unit identifiers (e.g. VTD identifiers to block identifiers). assignment (dict): Maps larger unit identifiers to districts. Returns: Maps smaller unit identifiers to districts according to `assignment`. """ # Create an empty mapping. mapped = {} # For each of the keys in the provided assignment, get the units to which # the keys correspond, and assign them appropriately. for bigger, district in assignment.items(): smallers = unitmap[bigger] mapped.update({smaller: district for smaller in smallers}) return mapped def _up(unitmap, assignment, popmap): return assignment