Ejemplo n.º 1
0
def test_join_can_handle_right_index():
    graph = Graph([("01", "02"), ("02", "03"), ("03", "01")])
    df = pandas.DataFrame({"16SenDVote": [20, 30, 50], "node": ["01", "02", "03"]})

    graph.join(df, ["16SenDVote"], right_index="node")

    assert graph.nodes["01"]["16SenDVote"] == 20
    assert graph.nodes["02"]["16SenDVote"] == 30
    assert graph.nodes["03"]["16SenDVote"] == 50
Ejemplo n.º 2
0
def test_add_data_to_graph_can_handle_column_names_that_start_with_numbers():
    graph = Graph([("01", "02"), ("02", "03"), ("03", "01")])
    df = pandas.DataFrame({"16SenDVote": [20, 30, 50], "node": ["01", "02", "03"]})
    df = df.set_index("node")

    graph.add_data(df, ["16SenDVote"])

    assert graph.nodes["01"]["16SenDVote"] == 20
    assert graph.nodes["02"]["16SenDVote"] == 30
    assert graph.nodes["03"]["16SenDVote"] == 50
Ejemplo n.º 3
0
def test_make_graph_from_dataframe_gives_correct_graph(geodataframe):
    df = geodataframe.set_index("ID")
    graph = Graph.from_geodataframe(df)

    assert edge_set_equal(
        set(graph.edges), {("a", "b"), ("a", "c"), ("b", "d"), ("c", "d")}
    )
Ejemplo n.º 4
0
def test_from_file_and_then_to_json_with_geometries(shapefile, target_file):
    graph = Graph.from_file(shapefile)

    # Even the geometry column is copied to the graph
    assert all("geometry" in node_data for node_data in graph.nodes.values())

    graph.to_json(target_file, include_geometries_as_geojson=True)
Ejemplo n.º 5
0
def twelve_by_twelve_with_pop():
    xy_grid = networkx.grid_graph([12, 12])
    nodes = {node: node[1] + 12 * node[0] for node in xy_grid}
    grid = networkx.relabel_nodes(xy_grid, nodes)
    for node in grid:
        grid.nodes[node]["pop"] = 1
    return Graph.from_networkx(grid)
Ejemplo n.º 6
0
def test_identifies_boundary_nodes(geodataframe_with_boundary):
    df = geodataframe_with_boundary.set_index("ID")
    graph = Graph.from_geodataframe(df)

    for node in ("a", "b", "c", "e"):
        assert graph.nodes[node]["boundary_node"]
    assert not graph.nodes["d"]["boundary_node"]
Ejemplo n.º 7
0
def test_from_file_and_then_to_json_does_not_error(shapefile, target_file):
    graph = Graph.from_file(shapefile)

    # Even the geometry column is copied to the graph
    assert all("geometry" in node_data for node_data in graph.nodes.values())

    graph.to_json(target_file)
Ejemplo n.º 8
0
def test_make_graph_works_with_queen_adjacency(geodataframe):
    df = geodataframe.set_index("ID")
    graph = Graph.from_geodataframe(df, adjacency="queen")

    assert edge_set_equal(
        set(graph.edges),
        {("a", "b"), ("a", "c"), ("b", "d"), ("c", "d"), ("a", "d"), ("b", "c")},
    )
Ejemplo n.º 9
0
def discontiguous_partition_with_flips():
    graph = nx.Graph()
    graph.add_nodes_from(range(4))
    graph.add_edges_from([(0, 1), (1, 2), (2, 3)])
    partition = Partition(Graph.from_networkx(graph), {0: 0, 1: 1, 2: 1, 3: 0})

    # This flip will maintain discontiguity.
    return partition, {1: 0}
Ejemplo n.º 10
0
def test_can_pass_queen_or_rook_strings_to_control_adjacency(geodataframe):
    df = geodataframe.set_index("ID")
    graph = Graph.from_geodataframe(df, adjacency="queen")

    assert edge_set_equal(
        set(graph.edges),
        {("a", "b"), ("a", "c"), ("b", "d"), ("c", "d"), ("a", "d"), ("b", "c")},
    )
Ejemplo n.º 11
0
def test_data_and_geometry(gdf_with_data):
    df = gdf_with_data
    graph = Graph.from_geodataframe(df, cols_to_add=["data", "data2"])
    assert graph.geometry is df.geometry
    #graph.add_data(df[["data"]])
    assert (graph.data["data"] == df["data"]).all()
    #graph.add_data(df[["data2"]])
    assert list(graph.data.columns) == ["data", "data2"]
Ejemplo n.º 12
0
def test_computes_boundary_perims(geodataframe_with_boundary):
    df = geodataframe_with_boundary.set_index("ID")
    graph = Graph.from_geodataframe(df, reproject=False)

    expected = {"a": 5, "e": 5, "b": 1, "c": 1}

    for node, value in expected.items():
        assert graph.nodes[node]["boundary_perim"] == value
Ejemplo n.º 13
0
def test_does_not_reproject_by_default(geodataframe):
    df = geodataframe.set_index("ID")
    graph = Graph.from_geodataframe(df)

    for node in ("a", "b", "c", "d"):
        assert graph.nodes[node]["area"] == 1.0

    for edge in graph.edges:
        assert graph.edges[edge]["shared_perim"] == 1.0
Ejemplo n.º 14
0
def test_can_insist_on_not_reprojecting(geodataframe):
    df = geodataframe.set_index("ID")
    graph = Graph.from_geodataframe(df, reproject=False)

    for node in ("a", "b", "c", "d"):
        assert graph.nodes[node]["area"] == 1

    for edge in graph.edges:
        assert graph.edges[edge]["shared_perim"] == 1
Ejemplo n.º 15
0
def example_geographic_partition():
    graph = Graph.from_networkx(networkx.complete_graph(3))
    assignment = {0: 1, 1: 1, 2: 2}
    for node in graph.nodes:
        graph.nodes[node]["boundary_node"] = False
        graph.nodes[node]["area"] = 1
    for edge in graph.edges:
        graph.edges[edge]["shared_perim"] = 1
    return GeographicPartition(graph, assignment, None, None, None)
Ejemplo n.º 16
0
def test_reproject(geodataframe):
    # I don't know what the areas and perimeters are in UTM for these made-up polygons,
    # but I'm pretty sure they're not 1.
    df = geodataframe.set_index("ID")
    graph = Graph.from_geodataframe(df, reproject=True)

    for node in ("a", "b", "c", "d"):
        assert graph.nodes[node]["area"] != 1

    for edge in graph.edges:
        assert graph.edges[edge]["shared_perim"] != 1
Ejemplo n.º 17
0
    def from_json_graph(cls, graph_path, assignment, updaters=None):
        """Creates a :class:`Partition` from a json file containing a
        serialized NetworkX `adjacency_data` object. Files of this
        kind for each state are available in the @gerrymandr/vtd-adjacency-graphs
        GitHub repository.

        :param graph_path: String filename for the json file
        :param assignment: String key for the node attribute giving a district
            assignment, or a dictionary mapping node IDs to district IDs.
        :param updaters: (optional) Dictionary of updater functions to
            attach to the partition, in addition to the default_updaters of `cls`.
        """
        graph = Graph.from_json(graph_path)
        return cls(graph, assignment, updaters)
Ejemplo n.º 18
0
def test_bipartition_tree_returns_a_tree(graph_with_pop):
    ideal_pop = sum(graph_with_pop.nodes[node]["pop"] for node in graph_with_pop) / 2
    tree = Graph.from_networkx(networkx.Graph(
        [(0, 1), (1, 2), (1, 4), (3, 4), (4, 5), (3, 6), (6, 7), (6, 8)]
    ))
    for node in tree:
        tree.nodes[node]["pop"] = graph_with_pop.nodes[node]["pop"]

    result = bipartition_tree(
        graph_with_pop, "pop", ideal_pop, 0.25, 10, tree, lambda x: 4
    )

    assert networkx.is_tree(tree.subgraph(result))
    assert networkx.is_tree(
        tree.subgraph({node for node in tree if node not in result})
    )
Ejemplo n.º 19
0
def test_Partition_can_update_stats():
    graph = networkx.complete_graph(3)
    assignment = {0: 1, 1: 1, 2: 2}

    graph.nodes[0]["stat"] = 1
    graph.nodes[1]["stat"] = 2
    graph.nodes[2]["stat"] = 3

    updaters = {"total_stat": Tally("stat", alias="total_stat")}

    partition = Partition(Graph.from_networkx(graph), assignment, updaters)
    assert partition["total_stat"][2] == 3
    flip = {1: 2}

    new_partition = partition.flip(flip)
    assert new_partition["total_stat"][2] == 5
Ejemplo n.º 20
0
def test_find_balanced_cuts_contraction():
    tree = Graph.from_networkx(networkx.Graph(
        [(0, 1), (1, 2), (1, 4), (3, 4), (4, 5), (3, 6), (6, 7), (6, 8)]
    ))

    # 0 - 1 - 2
    #   ||
    # 3= 4 - 5
    # ||
    # 6- 7
    # |
    # 8

    populated_tree = PopulatedGraph(
        tree, {node: 1 for node in tree}, len(tree) / 2, 0.5
    )
    cuts = find_balanced_edge_cuts_contraction(populated_tree)
    edges = set(tuple(sorted(cut.edge)) for cut in cuts)
    assert edges == {(1, 4), (3, 4), (3, 6)}
Ejemplo n.º 21
0
    def __init__(
        self,
        dimensions=None,
        with_diagonals=False,
        assignment=None,
        updaters=None,
        parent=None,
        flips=None,
    ):
        """
        :param dimensions: tuple (m,n) of the desired dimensions of the grid.
        :param with_diagonals: (optional, defaults to False) whether to include diagonals
            as edges of the graph (i.e., whether to use 'queen' adjacency rather than
            'rook' adjacency).
        :param assignment: (optional) dict matching nodes to their districts. If not
            provided, partitions the grid into 4 quarters of roughly equal size.
        :param updaters: (optional) dict matching names of attributes of the Partition
            to functions that compute their values. If not provided, the Grid
            configures the cut_edges updater for convenience.
        """
        if dimensions:
            self.dimensions = dimensions
            graph = Graph.from_networkx(
                create_grid_graph(dimensions, with_diagonals))

            if not assignment:
                thresholds = tuple(math.floor(n / 2) for n in self.dimensions)
                assignment = {
                    node: color_quadrants(node, thresholds)
                    for node in graph.nodes
                }

            if not updaters:
                updaters = dict()
            updaters.update(self.default_updaters)

            super().__init__(graph, assignment, updaters)
        elif parent:
            self.dimensions = parent.dimensions
            super().__init__(parent=parent, flips=flips)
        else:
            raise Exception("Not a good way to create a Partition")
Ejemplo n.º 22
0
def test_make_graph_from_dataframe_creates_graph(geodataframe):
    graph = Graph.from_geodataframe(geodataframe)
    assert isinstance(graph, Graph)
Ejemplo n.º 23
0
def test_can_ignore_errors_while_making_graph(shapefile):
    with patch("gerrychain.graph.geo.explain_validity") as explain:
        explain.return_value = "Invalid geometry"
        assert Graph.from_file(shapefile, ignore_errors=True)
Ejemplo n.º 24
0
def test_raises_geometry_error_if_invalid_geometry(shapefile):
    with patch("gerrychain.graph.geo.explain_validity") as explain:
        explain.return_value = "Invalid geometry"
        with pytest.raises(GeometryError):
            Graph.from_file(shapefile, ignore_errors=False)
Ejemplo n.º 25
0
def test_graph_raises_if_crs_is_missing_when_reprojecting(geodataframe):
    geodataframe.crs = None

    with pytest.raises(ValueError):
        Graph.from_geodataframe(geodataframe, reproject=True)
Ejemplo n.º 26
0
def test_graph_warns_for_islands():
    graph = Graph()
    graph.add_node(0)

    with pytest.warns(Warning):
        graph.warn_for_islands()
Ejemplo n.º 27
0
def test_Partition_unlabelled_vertices_raises_keyerror():
    graph = Graph.from_networkx(networkx.complete_graph(3))
    assignment = {0: 1, 2: 2}
    with pytest.raises(KeyError):
        Partition(graph, assignment, {"cut_edges": cut_edges})
Ejemplo n.º 28
0
def test_graph_raises_if_crs_is_missing(geodataframe):
    del geodataframe.crs

    with pytest.raises(ValueError):
        Graph.from_geodataframe(geodataframe)
Ejemplo n.º 29
0
def test_from_file_adds_all_data_by_default(shapefile):
    graph = Graph.from_file(shapefile)

    assert all("data" in node_data for node_data in graph.nodes.values())
    assert all("data2" in node_data for node_data in graph.nodes.values())
Ejemplo n.º 30
0
def test_make_graph_from_dataframe_preserves_df_index(geodataframe):
    df = geodataframe.set_index("ID")
    graph = Graph.from_geodataframe(df)
    assert set(graph.nodes) == {"a", "b", "c", "d"}