Esempio n. 1
0
    def get_path_graph(
        self,
        graph: nx.Graph,
        source: str,
        target: str,
        enforce_directionality: bool = True,
        try_reverse: bool = True,
    ):  # pylint: disable=too-many-arguments
        """Make a new graph with the shortest paths between two nodes"""
        new_graph = graph if enforce_directionality else self._make_undirected(
            graph)
        try:
            nodes = set(
                sum(
                    map(list, nx.all_shortest_paths(new_graph, source,
                                                    target)), []))
            return graph.__class__(graph.subgraph(nodes))

        except (nx.NetworkXError, nx.NetworkXException):
            warn(traceback.format_exc())
            if try_reverse:
                return self.get_path_graph(
                    graph=graph,
                    source=target,
                    target=source,
                    enforce_directionality=enforce_directionality,
                    try_reverse=False,
                )
            return graph.__class__()
Esempio n. 2
0
def read_geograph_with_coordinates_attributes(graph: nx.Graph,
                                              x_key='x',
                                              y_key='y',
                                              **attr) -> GeoGraph:
    """Parse a `networkx` graph which have node's coordinates as attribute. This method can be useful to parse an output
    graph of the `osmnx` package.

    Parameters
    ----------
    graph : nx.Graph
        Given graph to parse. All nodes must have the ``x_key`` and ``y_key`` attributes.
    x_key :
        x-coordinates attribute to parse (Default value = 'x')
    y_key :
        y-coordinates attribute to parse (Default value = 'y')
    **attr :
        Optional geograph spatial keys.

    Returns
    -------
    GeoGraph, GeoDiGraph, GeoMultiGraph, GeoMultiDiGraph
        The parsed geograph (shallow copy of the input graph).

    """
    graph = graph.__class__(graph)
    x_coords = nx.get_node_attributes(graph, x_key)
    y_coords = nx.get_node_attributes(graph, y_key)
    nodes_geometry_key = attr.pop("nodes_geometry_key",
                                  settings.NODES_GEOMETRY_DEFAULT_KEY)
    edges_geometry_key = attr.pop("edges_geometry_key",
                                  settings.EDGES_GEOMETRY_DEFAULT_KEY)
    for n in graph.nodes:
        if n not in x_coords or n not in y_coords:
            raise ValueError("Unable to find coordinates for node : '%s'" %
                             str(n))
        point = Point([x_coords[n], y_coords[n]])
        graph.nodes[n][nodes_geometry_key] = point
    graph.nodes_geometry_key = nodes_geometry_key
    graph.edges_geometry_key = edges_geometry_key
    return parse_graph_as_geograph(graph, **attr)
Esempio n. 3
0
    def get_spanning_graph(
        self,
        graph: nx.Graph,
        seeds: ty.Union[list, set, tuple],
        max_distance: int = 2,
        enforce_directionality: bool = True,
    ):
        if not enforce_directionality:
            graph = self._make_undirected(graph)
        seed_nodes = {id_: max_distance for id_ in seeds if id_ in graph.nodes}
        seed_elements = [
            self.model.elements[id_] for id_ in seeds
            if id_ not in seed_nodes and id_ in self.model.elements
        ]
        seed_edges = [(element.source, element.target)
                      for element in seed_elements if element._is_relationship]

        distances = {
            node: max_distance - 1
            for node in set(sum(seed_edges, [])) if node in graph
        }
        distances.update(seed_nodes)

        max_iter = 100
        while sum(distances.values()) > 0 and max_iter > 0:
            max_iter -= 1
            for node_id, distance in tuple(distances.items()):
                if distance < 1:
                    continue
                for neighbor in graph.neighbors(node_id):
                    distances[neighbor] = distances.get(neighbor, distance - 1)
                distances[node_id] -= 1

        nodes = tuple(distances)

        return graph.__class__(graph.subgraph(nodes))
Esempio n. 4
0
def nx_copy(from_graph: nx.Graph,
            to_graph: Union[nx.Graph, Type[nx.Graph]],
            *,
            node_transform: Optional[Callable[[NodeGenerator],
                                              NodeGenerator]] = None,
            edge_transform: Optional[Callable[[EdgeGenerator],
                                              EdgeGenerator]] = None,
            global_transform: Optional[Callable[[NodeGenerator],
                                                NodeGenerator]] = None,
            global_attr_key: str = None,
            deepcopy: bool = False) -> nx.Graph:
    """Copies node, edges, node_data, and edge_data from graph `g1` to graph
    `g2`. If `g2` is None, a new graph of type `g1.__class__` is created. If
    `g2` is a class or subclass of `nx.Graph`, a new graph of that type is
    created.

    If `deepcopy` is set to True, the copy will perform a deepcopy of all node
    and edge data. If false, only shallow copies will be created.

    `node_transform` and `edge_transform` can be provided to perform a transform
    on the on `g1.nodes(data=True)` and `g1.edges(data=True)` iterators
    during copy. The node transform should return a `Generator[Tuple[T, dict], None, None]`
    while the edge transform should return a `Generator[Tuple[T, T, dict], None, None]`.
    These transforms may include skipping certain nodes or edges, transforming the node or edge
    data, or transforming the node keys themselves.

    Some example transforms include:

    .. code-block::

        def node_to_str(gen):
            for n, ndata in gen:
                yield (str(n), ndata)

        def remove_self_loops(gen):
            for n1, n2, edata in gen:
                if n1 == n2:
                    yield (n1, n2, edata)

        nx_copy(g1, None, node_transform=node_to_str, edge_transform=remove_self_loops, deepcopy=True)


    :param from_graph: graph to copy from
    :param to_graph: graph to copy to
    :param node_transform: optional transform applied to the `from_graph.nodes(data=True)` iterator
    :param edge_transform: optional transform applied to the `from_graph.edges(data=True)` iterator
    :param literal_transform: if True, node_transform will *not* be applied following the edge_transform.
        This may result in unintentional edges being created.
    :param deepcopy:
    :return:
    """
    if to_graph is None:
        to_graph = from_graph.__class__()
    elif isinstance(to_graph, type) and issubclass(to_graph, nx.Graph):
        to_graph = to_graph()

    node_iter = from_graph.nodes(data=True)
    if node_transform:
        niter1, niter2 = itertools.tee(node_iter)
        node_iter = list(node_transform(niter1))
        _node_mapping = {}
        for x1, x2 in zip(niter2, node_iter):
            _node_mapping[x1[0]] = x2[0]

        def map_node(x):
            return _node_mapping[x]

    else:

        def map_node(x):
            return x

    for n, ndata in node_iter:
        if deepcopy:
            n, ndata = do_deepcopy((n, ndata))
        to_graph.add_node(n, **ndata)

    edge_iter = from_graph.edges(data=True)
    if edge_transform:
        edge_iter = edge_transform(edge_iter)

    for n1, n2, edata in edge_iter:
        if deepcopy:
            n1, n2, edata = do_deepcopy((n1, n2, edata))
        to_graph.add_edge(map_node(n1), map_node(n2), **edata)

    if hasattr(from_graph, GraphWithGlobal.get_global.__name__) and hasattr(
            to_graph, GraphWithGlobal.get_global.__name__):
        giter = from_graph.globals(data=True, global_key=global_attr_key)
        if global_transform:
            giter = global_transform(giter)
        for gkey, gdata in giter:
            if deepcopy:
                gdata = do_deepcopy(gdata)
            else:
                gdata = dict(gdata)
            to_graph.set_global(gdata, gkey)
    return to_graph
Esempio n. 5
0
def edge_weight_percolation(network: nx.Graph,
                            order,
                            connecting='strong') -> np.ndarray:
    '''Percolation with given weight on given network.
    Parameters
    ---------------
    network : `nx.Graph` (or `nx.DiGraph`)
        The network which will be percolated.
    order : `str` or `numpy.ndarray`
        The string name of the weight to be used. If type of `order` is numpy array, 
        percolation will be performed with the same order.

    Return
    ---------------
    `np.ndarray` or `list`
        From each node as each cluster, the return is a merging ordered list of edges and its cluster.
        The form of each element is ('weight', startnode, endnode, cluster1, cluster2).
        If cluster is negative, it means there is no merging of clusters between such a edge.
    
    Examples
    -----------
    ```!python
    >>> network = nx.Graph()
    >>> network.add_edge(1,2, 'value' = 0.2 ) 
    >>> network.add_edge(2,3, 'value' = 0.5 ) 
    >>> network.add_edge(3,4, 'value' = 0.3 ) 
    >>> 
    >>> edge_weight_percolation(network, 'value')   
    ((0.2, 1, 2, 1, 2)                              
    (0.3, 3, 4, 3, 4)                               
    (0.5, 2, 3, 1, 3))                              
    ```
    First state : 1, 2, 3, 4 (each node is a single cluster).
    Edge (1,2) was merged. Current state : 1, 1, 3, 4 (cluster 1 contains node 1 and 2.).
    Edge (3,4) was merged. Current state : 1, 1, 3, 3 (cluster 3 contains node 3 and 4.).
    Finally, edge (2,3) was merged. Current state : 1, 1, 1, 1 (cluster 1 contains all nodes.).
    '''

    strong = True if connecting == 'strong' else False

    if not isinstance(network, nx.DiGraph) and not isinstance(
            network, nx.Graph):
        raise ValueError(
            "'network' must be a instance of networkx `DiGraph` or `Graph`.")

    if strong and not isinstance(network, nx.DiGraph):
        strong = False
        raise Warning(
            "Undirected graph cannot be operated on strongly connected component. Weakly connected component will be calculated."
        )

    edges = np.array([[wt, u, v] for (u, v, wt) in network.edges.data(order)
                      ])  # container for sorting

    #order = np.argsort(edges[:,0])                                                  # sort by weight

    class cluster:
        def __init__(self, data):
            self.head = None  # the representative element of cluster
            self.data = data  # the representative data of cluster

        def leader(self):
            current = self
            while current.head is not None:
                #print(current.data)
                current = current.head
            return current

        def merge(self, other):
            target, cl = (self, other) if self > other else (other, self)
            target.leader().head = cl.leader()

        def __gt__(self, other):
            return self.leader().data > other.leader(
            ).data  # for deciding which is more representative

    #make empty Graph
    percolation = []
    percolnet = network.__class__()
    clusters = {
        i: cluster(i)
        for i in range(len(network.nodes))
    }  # initial cluster state is that each node is a cluster itself.
    for o in order:
        weight, st_node, end_node = edges[
            o]  # weight, start node, end node, respectively.
        cl1, cl2 = clusters[st_node], clusters[
            end_node]  # cl1 and cl2 are cluster1 and cluster2, respectively.

        percolnet.add_edge(
            st_node, end_node)  # Make percolation by adding new edge by edge.

        merged = False
        if strong:
            if isinstance(percolnet[end_node].get(st_node), dict):
                merged = True
        else:
            merged = True

        if merged and cl1.leader().data != cl2.leader().data:
            percolation.append([
                weight, st_node, end_node,
                cl1.leader().data,
                cl2.leader().data
            ])
            cl1.merge(cl2)
        else:
            percolation.append([weight, st_node, end_node, -1, -1])

    return percolation