Partitions¶
Partitions assign graph nodes to districts and expose the resulting plan statistics.
- class gerrychain.partition.GeographicPartition(graph: Graph | FrozenGraph | networkx.Graph[NodeT, dict[str, Any], dict[str, Any]] | None = None, assignment: Mapping[NodeT, PartT] | Assignment | str | None = None, updaters: Mapping[str, Callable[[Partition], Any]] | None = None, parent: Partition | None = None, flips: Mapping[NodeT, PartT] | None = None, use_default_updaters: bool = True)[source]¶
Bases:
PartitionA Partition with areas, perimeters, and boundary information included. These additional data allow you to compute compactness scores like Polsby-Popper.
Initialize a Partition instance.
- Parameters:
graph (Graph | FrozenGraph | networkx.Graph | None, optional) – Underlying graph. Required for a root partition. Defaults to
None.assignment (Mapping[Hashable, Hashable] | Assignment | str | None, optional) – Node to district assignment, or a node attribute containing it. Defaults to
None.updaters (Mapping[str, Callable[[Partition], Any]] | None, optional) – Named updater functions. Their result types vary by updater. Defaults to
None.parent (Partition | None, optional) – Parent partition for a child. Defaults to
None.flips (Mapping[Hashable, Hashable] | None, optional) – Reassignments relative to the parent. Defaults to
None.use_default_updaters (bool, optional) – Whether to include default updaters. Defaults to
True.
- class gerrychain.partition.Partition(graph: Graph | FrozenGraph | networkx.Graph[NodeT, dict[str, Any], dict[str, Any]] | None = None, assignment: Mapping[NodeT, PartT] | Assignment | str | None = None, updaters: Mapping[str, Callable[[Partition], Any]] | None = None, parent: Partition | None = None, flips: Mapping[NodeT, PartT] | None = None, use_default_updaters: bool = True)[source]¶
Bases:
objectThe Partition class represents a partition of the nodes of the graph into districts (parts). Every iteration of MarkovChain creates a new Partition object from the previous Partition object by performing the set of flips (changes in association of a node to a district (perhaps confusingly called a “part”).
Perhaps the primary class attribute is “assignment” which stores the set of nodes in each district (“part”).
Note that the “parts” class attribute is actually a function that returns the “parts” of the assignment class attribute.
A Partition object also provides access (via __getitem__()) to the values computed by updater functions (see comment on updaters in the file updaters/flows.py).
Note that by default the constructor for a Partition object will convert the underlying graph in a Graph object from NetworkX to RustworkX - because RustworkX is so much faster than NetworkX. This is done in the _first_time() function below.
It is perhaps worth noting that when we convert the underlying graph object from NX to RX, we create a mapping dict that records the “original” NX node_ids and the new RX node_ids. It is stored as a class attribute of the new Graph object: Graph.nx_to_rx_node_id_map. We use this mapping to update the “assignment” class to use the new RX node_ids.
Lastly the “subgraphs” class attribute stores a subgraph for each district (“part”). Note that this is done for efficiency reasons because creating a subgraph is expensive - so subgraphs are created lazily (on demand) and subsequently cached.
- assignment¶
Maps node IDs to district IDs.
- Type:
Assignment
- parts¶
Maps district IDs to the set of nodes in that district.
- subgraphs¶
Maps district IDs to the induced subgraph of that district.
- Type:
SubgraphView
Initialize a Partition instance.
- Parameters:
graph (Graph | FrozenGraph | networkx.Graph | None, optional) – Underlying graph. Required for a root partition. Defaults to
None.assignment (Mapping[Hashable, Hashable] | Assignment | str | None, optional) – Node to district assignment, or a node attribute containing it. Defaults to
None.updaters (Mapping[str, Callable[[Partition], Any]] | None, optional) – Named updater functions. Their result types vary by updater. Defaults to
None.parent (Partition | None, optional) – Parent partition for a child. Defaults to
None.flips (Mapping[Hashable, Hashable] | None, optional) – Reassignments relative to the parent. Defaults to
None.use_default_updaters (bool, optional) – Whether to include default updaters. Defaults to
True.
- property assignment_vector: ndarray¶
The part (district) label of each node, as an array indexed by internal node id.
Computed lazily and cached. When this partition’s parent has already emitted its vector, the child’s is built by copying it and rewriting only the flipped entries, so emitting the vector at every step of a chain costs an array copy (C-speed) plus the handful of flips rather than an O(n) rebuild from the assignment mapping.
The returned array is read-only, since child partitions build their vectors from it; call
.copy()on it if you need a mutable version. Positions are internal node ids; useGraph.original_nx_node_id_for_internal_node_id()to translate back to the original node labels.- Returns:
Array of length
nwhosei-th entry is the part of nodei.- Return type:
numpy.ndarray
- crosses_parts(edge: tuple[Hashable, Hashable]) bool[source]¶
Return True if the edge crosses from one part of the partition to another.
This method returns True if the edge crosses from one part of the partition to another. It returns true if the edge crosses from one part of the partition to another.
- flip(flips: Mapping[NodeT, PartT], flips_passed_in_use_original_nx_node_ids: bool = False) Partition[source]¶
Returns the new partition obtained by performing the given flips on this partition.
This method returns the new partition obtained by performing the given flips on this partition. It returns new Partition.
- Parameters:
flips (dict) – dictionary assigning nodes of the graph to their new districts
flips_passed_in_use_original_nx_node_ids (bool) – Denotes whether the node_ids in the flips are original NX node_ids or whether they are internal RX node_ids. The only time this is set to True is for testing when the test wants to provide explicit flips using NX node_ids (because the test cannot know what node_ids RX will choose when we convert the underlying graph object).
- Returns:
the new Partition
- Return type:
- classmethod from_districtr_file(graph: Graph, districtr_file: str | PathLike[str], updaters: Mapping[str, Callable[[Partition], Any]] | None = None) Partition[source]¶
Return partition created from the Districtr file.
Create a Partition from a districting plan created with Districtr, a free and open-source web app created by MGGG for drawing districts.
The provided
graphshould be created from the same shapefile as the Districtr module used to draw the districting plan. These shapefiles may be found in a repository in the mggg-states GitHub organization, or by request from MGGG.- Parameters:
graph (Graph) – The graph to create the Partition from
districtr_file (str | os.PathLike) – the path to the
.jsonfile exported from Districtrupdaters (Mapping[str, Callable] | None, optional) – Dictionary of updaters.
- Returns:
The partition created from the Districtr file
- Return type:
- classmethod from_random_assignment(graph: ~gerrychain.graph.graph.Graph, n_parts: int, epsilon: float, pop_col: str, updaters: ~collections.abc.Mapping[str, ~collections.abc.Callable[[~gerrychain.partition.partition.Partition], ~typing.Any]] | None = None, use_default_updaters: bool = True, partition_fn: ~gerrychain.partition.initial_partition_generators.PartitionFn = <function recursive_tree_part>, *, rng: ~random.Random | int | None = None) Partition[source]¶
Create a Partition with a random assignment of nodes to districts.
This method creates a Partition with a random assignment of nodes to districts. It returns partition created with a random assignment.
- Parameters:
graph (Graph) – The graph to create the Partition from.
n_parts (int) – The number of districts to divide the nodes into.
epsilon (float) – The maximum relative population deviation from the ideal
pop_col (str) – The column of the graph’s node data that holds the population data.
updaters (Mapping[str, Callable] | None, optional) – Dictionary of updaters.
use_default_updaters (bool, optional) – If False, do not include default updaters.
partition_fn (PartitionFn, optional) – The function to use to partition the graph into
n_parts; it returns the full node-to-part assignment dict. It is called with this method’s normalizedrng, which takes precedence over anrngpartially bound to thepartition_fnparameter. Defaults to gerrychain.partition.recursive_tree_part.rng (random.Random | int | None, optional) – Source of randomness. An integer creates a reproducible RNG;
Nonecreates an independent RNG from system entropy.
- Returns:
The partition created with a random assignment
- Return type:
- keys() KeysView[str][source]¶
Return the names of this partition’s updaters.
Important: a Partition’s mapping/
[]interface is over its updaters, not its nodes. Sopartition["population"]evaluates the"population"updater, andkeys()returns the updater names (e.g.["cut_edges", "population", ...]) - not node_ids or part_ids. This often surprises people who think of a Partition as a node-to-district map.The actual node-to-part data lives in
assignment(partition.assignment, a node_id -> part_id mapping), and the inverse part-to-nodes view isparts(partition.parts).- Returns:
A view of the updater names available on this partition.
- Return type:
KeysView[str]
- plot(geometries: geopandas.GeoDataFrame | geopandas.GeoSeries | None = None, **kwargs: Any) matplotlib.axes.Axes[source]¶
Plot the partition, using the provided geometries.
This method plots the partition, using the provided geometries. It returns matplotlib axes object. Which plots the Partition.
- Parameters:
geometries (geopandas.GeoDataFrame or geopandas.GeoSeries) – A GeoDataFrame or GeoSeries holding the geometries to use for plotting. Its Index should match the node labels of the partition’s underlying Graph.
**kwargs (Any) – Additional arguments to pass to geopandas.GeoDataFrame.plot to adjust the plot.
- Returns:
The matplotlib axes object. Which plots the Partition.
- Return type:
matplotlib.axes.Axes
- gerrychain.partition.recursive_seed_part(graph: ~gerrychain.graph.graph.Graph, parts: ~collections.abc.Sequence[~collections.abc.Hashable], pop_target: float | int, pop_col: str, epsilon: float, bipartition_tree_fn: ~gerrychain.tree.bipartition_tree.BipartitionTreeFn = functools.partial(<function bipartition_tree>, max_attempts=100000), node_repeats: int = 0, n: int | None = None, ceil: int | None = None, *, rng: ~random.Random | int | None = None) dict[Hashable, Hashable][source]¶
Returns an assignment dictionary with
num_distsdistricts balanced withinepsilon.Returns an assignment dictionary with
num_distsdistricts balanced withinepsilonof.pop_targetby recursively splitting graph using _recursive_seed_part_inner.- Parameters:
graph (Graph) – The graph
parts (Sequence) – Iterable of part labels (like
[0,1,2]orrange(4)).pop_target (float | int) – Target population for each part of the partition
pop_col (str) – Node attribute key holding population data
epsilon (float) – How far (as a percentage of
pop_target) frompop_targetthe parts of the partition can bebipartition_tree_fn (BipartitionTreeFn, optional) – Function used to find balanced partitions at the 2-district level. Defaults to
gerrychain.tree.bipartition_tree.node_repeats (int, optional) – Additional roots to try on each spanning tree before drawing a new tree. Defaults to 0.
n (int | None, optional) – Either a positive integer (greater than 1) or None. If n is a positive integer, this function will recursively create a seed plan by either biting off districts from graph or dividing graph into n chunks and recursing into each of these. If n is None, this function prime factors
num_dists = n_1*n_2*...*n_k(n_1 > n_2 > … n_k) and recursively partitions graph into n_1 chunks. Defaults to None.ceil (int | None, optional) – Either a positive integer (at least 2) or None. Relevant only if n is None. If
ceilis a positive integer then finds the largest factor ofnum_distsless than or equal toceil, and recursively splits graph into that number of chunks, or bites off a district if that number is 1. Defaults to None.rng (random.Random | int | None, optional) – Source of randomness. Pass a shared
Randomfor repeated standalone calls; an integer restarts the stream each call.
- Returns:
New assignments for the nodes of
graph.- Return type:
- gerrychain.partition.recursive_tree_part(graph: ~gerrychain.graph.graph.Graph, parts: ~collections.abc.Sequence[~collections.abc.Hashable], pop_target: float | int, pop_col: str, epsilon: float, node_repeats: int = 0, bipartition_tree_fn: ~gerrychain.tree.bipartition_tree.BipartitionTreeFn = functools.partial(<function bipartition_tree>, max_attempts=100000), *, rng: ~random.Random | int | None = None) dict[Hashable, Hashable][source]¶
Return new assignments for the nodes of
graph.Uses
gerrychain.tree.bipartition_treerecursively to partition a tree intolen(parts)parts of populationpop_target(withinepsilon).Can be used to generate initial seed plans (partition assignments) or to implement ReCom-like “merge walk” proposals.
- Parameters:
graph (Graph) – The graph to partition into
len(parts)\(\varepsilon\)-balanced parts.parts (Sequence) – Iterable of part (district) labels (like
[0,1,2]orrange(4)).pop_target (float | int) – Target population for each part of the partition.
pop_col (str) – Node attribute key holding population data.
epsilon (float) – How far (as a percentage of
pop_target) frompop_targetthe parts of the partition can be.node_repeats (int, optional) – Additional roots to try on each spanning tree before drawing a new tree. Defaults to 0.
bipartition_tree_fn (BipartitionTreeFn, optional) – The partition method to use. Defaults to
partial(bipartition_tree, max_attempts=100000).rng (random.Random | int | None, optional) – Source of randomness. Pass a shared
Randomfor repeated standalone calls; an integer restarts the stream each call.
- Returns:
New assignments for the nodes of
graph.- Return type: