CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-gerrychain

Use Markov chain Monte Carlo to analyze districting plans and gerrymanders

Pending
Overview
Eval results
Files

partition-management.mddocs/

Partition Management

Create and manipulate district partitions representing assignments of geographic units to electoral districts. Partitions track updatable attributes and support efficient manipulation operations.

Capabilities

Basic Partition Creation

Create partitions with district assignments and updatable attributes that are automatically computed and cached.

class Partition:
    def __init__(
        self, 
        graph: Graph, 
        assignment: Union[Dict[NodeId, DistrictId], str], 
        updaters: Dict[str, UpdaterFunction] = None
    ) -> None:
        """
        Create a new partition of a graph into districts.
        
        Parameters:
        - graph (Graph): The underlying graph
        - assignment (Union[Dict, str]): District assignments as dict or column name
        - updaters (Dict[str, UpdaterFunction], optional): Functions to compute attributes
        
        Returns:
        None
        """

    def __getitem__(self, key: str) -> Any:
        """
        Access partition attributes computed by updaters.
        
        Parameters:
        - key (str): Name of the attribute
        
        Returns:
        Any: The computed attribute value
        """

    def flip(self, flips: Dict[NodeId, DistrictId]) -> "Partition":
        """
        Create a new partition with specified node assignments changed.
        
        Parameters:
        - flips (Dict[NodeId, DistrictId]): Node ID to new district ID mappings
        
        Returns:
        Partition: New partition with flipped assignments
        """

    def crosses_parts(self, edge: Tuple[NodeId, NodeId]) -> bool:
        """
        Check if an edge crosses district boundaries.
        
        Parameters:
        - edge (Tuple[NodeId, NodeId]): Edge as (node1, node2) tuple
        
        Returns:
        bool: True if edge crosses districts, False otherwise
        """

Usage example:

from gerrychain import Partition, Graph
from gerrychain.updaters import Tally, cut_edges

# Create basic partition
graph = Graph.from_file("precincts.shp")
partition = Partition(
    graph,
    assignment="district",  # Use 'district' column from shapefile
    updaters={
        "population": Tally("population"),
        "cut_edges": cut_edges
    }
)

# Access attributes
total_pop = sum(partition["population"].values())
num_cut_edges = len(partition["cut_edges"])

# Create new partition with flipped assignments
new_partition = partition.flip({node_id: new_district})

Geographic Partition Creation

Create partitions with geographic data and spatial capabilities, extending basic partitions with coordinate reference system support and file I/O.

class GeographicPartition(Partition):
    @classmethod
    def from_file(
        cls,
        filename: str,
        district_column: str,
        adjacency: str = "rook",
        updaters: Dict[str, UpdaterFunction] = None,
        **kwargs
    ) -> "GeographicPartition":
        """
        Create a GeographicPartition from a shapefile or GeoJSON.
        
        Parameters:
        - filename (str): Path to geographic data file
        - district_column (str): Column containing district assignments
        - adjacency (str): Adjacency type - "rook" or "queen"
        - updaters (Dict[str, UpdaterFunction], optional): Attribute updaters
        - **kwargs: Additional arguments passed to Graph.from_file
        
        Returns:
        GeographicPartition: New geographic partition
        """

    @classmethod
    def from_districtr_file(
        cls,
        graph: Graph,
        assignment_file: str,
        updaters: Dict[str, UpdaterFunction] = None
    ) -> "GeographicPartition":
        """
        Create a GeographicPartition from a Districtr assignment file.
        
        Parameters:
        - graph (Graph): The underlying graph
        - assignment_file (str): Path to Districtr JSON assignment file
        - updaters (Dict[str, UpdaterFunction], optional): Attribute updaters
        
        Returns:
        GeographicPartition: New geographic partition from Districtr data
        """

Usage example:

from gerrychain import GeographicPartition
from gerrychain.updaters import Tally, cut_edges

# From shapefile
partition = GeographicPartition.from_file(
    "precincts.shp",
    district_column="district",
    adjacency="queen",
    updaters={
        "population": Tally("population"),
        "cut_edges": cut_edges
    }
)

# From Districtr file
graph = Graph.from_file("precincts.shp")
partition = GeographicPartition.from_districtr_file(
    graph,
    "assignment.json",
    updaters={"population": Tally("population")}
)

Partition Assignment Operations

Manipulate and query district assignments efficiently.

def get_assignment(
    nodes: Iterable[NodeId], 
    assignment: Union[Dict[NodeId, DistrictId], str]
) -> Dict[NodeId, DistrictId]:
    """
    Extract assignment mapping for specified nodes.
    
    Parameters:
    - nodes (Iterable[NodeId]): Nodes to get assignments for
    - assignment (Union[Dict, str]): Assignment dict or column name
    
    Returns:
    Dict[NodeId, DistrictId]: Assignment mapping for specified nodes
    """

Subgraph Views

Create views of partition subgraphs for efficient district-level analysis.

class SubgraphView:
    def __init__(self, partition: Partition, districts: List[DistrictId]) -> None:
        """
        Create a view of partition subgraph for specified districts.
        
        Parameters:
        - partition (Partition): The partition
        - districts (List[DistrictId]): Districts to include in view
        
        Returns:
        None
        """

Advanced Usage Patterns

Common patterns for working with partitions in analysis workflows:

# Multi-step partition modification
partition = GeographicPartition.from_file("precincts.shp", "district")

# Chain multiple flips
step1 = partition.flip({node1: district1})
step2 = step1.flip({node2: district2})
final = step2.flip({node3: district3})

# Batch operations for efficiency
flips = {node1: district1, node2: district2, node3: district3}
result = partition.flip(flips)

# Check boundary crossings
boundary_edges = [
    edge for edge in graph.edges() 
    if partition.crosses_parts(edge)
]

# Access computed attributes
districts = list(partition.parts.keys())
district_populations = {
    dist: partition["population"][dist] 
    for dist in districts
}

# Parent tracking for Markov chains
current_partition = initial_partition
for step in range(1000):
    proposed = propose_flip(current_partition)
    if is_valid(proposed) and accept(proposed):
        current_partition = proposed
        # proposed.parent points to current_partition

Types

Assignment = Dict[NodeId, DistrictId]  # Node to district mapping
NodeId = Union[int, str]               # Graph node identifier  
DistrictId = int                       # District identifier
UpdaterFunction = Callable[[Partition], Any]  # Updater function type

Install with Tessl CLI

npx tessl i tessl/pypi-gerrychain

docs

acceptance-functions.md

constraints.md

data-aggregation.md

evaluation-metrics.md

graph-operations.md

index.md

markov-chain-analysis.md

optimization.md

partition-management.md

proposal-algorithms.md

tile.json