Beispiel #1
0
    def _(self, *edge: Union[str, Node], **kwargs: Any) -> None:

        # get additional parameters
        uid: Optional[str] = kwargs.pop('uid', None)
        nodes: bool = kwargs.pop('nodes', True)

        # check if all objects are node or str
        if not all(isinstance(arg, (Node, str)) for arg in edge):
            LOG.error('All objects have to be Node objects or str uids!')
            raise TypeError

        # if nodes is true add nodes
        if nodes:
            _nodes: tuple = ()
            for node in edge:
                if node not in self.nodes:
                    self.nodes.add(node)
                _nodes += (self.nodes[node], )

            # create new edge object and add it to the network
            if _nodes not in self or self.multiedges:
                self.add(self._edge_class(*_nodes, uid=uid, **kwargs))
            # raise error if edge already exists
            else:
                self._if_exist(_nodes, **kwargs)

        # add edge with unknown nodes
        else:
            self.add(self._edge_class(Node(), Node(), uid=edge[0], **kwargs))
Beispiel #2
0
    def __init__(self,
                 *args: Union[Node, Edge],
                 uid: Optional[str] = None,
                 **kwargs: Any) -> None:

        # initializing the parent classes
        Node.__init__(self, uid, **kwargs)
        Path.__init__(self, *args, uid=uid, **kwargs)
Beispiel #3
0
 def _add_edge_from_str(self, edge: str, **kwargs: Any) -> None:
     """Helper function to add an edge from nodes."""
     # check if edge with given uid str exists already
     if edge not in self:
         # if not add new node with provided uid str
         self._add_edge(Edge(Node(), Node(), uid=edge, **kwargs))
     else:
         # raise error if node already exists
         self._if_edge_exists(edge, **kwargs)
    def __init__(self,
                 *args: Union[Node, Edge],
                 uid: Optional[str] = None,
                 **kwargs: Any) -> None:

        # initializing the parent classes
        Node.__init__(self, uid, **kwargs)
        Path.__init__(self, *args, uid=uid, **kwargs)

        self['label'] = '-'.join([n.uid for n in self.nodes])
Beispiel #5
0
    def __init__(self,
                 *node: Union[str, PathPyObject],
                 uid: Optional[str] = None,
                 **kwargs: Any) -> None:
        """Initialize the node object."""

        # initializing the parent classes
        kwargs.pop('directed', None)
        kwargs.pop('ordered', None)
        Node.__init__(self, *node, uid=uid, **kwargs)
        TemporalPathPyObject.__init__(self, uid=self.uid, **kwargs)
Beispiel #6
0
def to_network(frame: pd.DataFrame,
               loops: bool = True,
               directed: bool = True,
               multiedges: bool = False,
               **kwargs: Any) -> Network:
    """Read network from a pandas data frame."""

    # if no v/w columns are included, pick first synonym
    frame = _check_column_name(frame, 'v', config['edge']['v_synonyms'])
    frame = _check_column_name(frame, 'w', config['edge']['w_synonyms'])

    LOG.debug('Creating %s network', directed)

    node_set = set(frame['v']).union(set(frame['w']))

    if None in node_set:
        LOG.error('DataFrame minimally needs columns \'v\' and \'w\'')
        raise IOError

    nodes = {n: Node(n) for n in node_set}

    edges: list = []
    edge_set: set = set()

    # TODO: Make this for loop faster!
    for row in frame.to_dict(orient='records'):
        v = row.pop('v')
        w = row.pop('w')
        uid = row.pop('uid', None)

        if (v, w) in edge_set and not multiedges:
            LOG.warning(
                'The edge (%s,%s) exist already '
                'and will not be considered. '
                'To capture this edge, please '
                'enalbe multiedges and/or directed!', v, w)
        elif loops or v != w:
            edges.append(Edge(nodes[v], nodes[w], uid=uid, **row))
            edge_set.add((v, w))
            if not directed:
                edge_set.add((w, v))
        else:
            continue

    net = Network(directed=directed, multiedges=multiedges, **kwargs)
    for node in nodes.values():
        net.nodes.add(node)

    for edge in edges:
        net.edges._add(edge)

    net._add_edge_properties()
    return net
Beispiel #7
0
def lattice_network(start: int = 0, stop: int = 10, dims: int = 2):
    """
    Generates a n-dimensional lattice network with coordinates in each dimension 
    ranging from start (inclusive) to stop (exclusive)
    """
    network = Network(directed=False)

    for pos in _multi_dim_range(start, stop, dims):
        network.add_node(
            Node("".join(str(i) + '-' for i in pos).strip('-'),
                 pos=np.array(pos)))

    for v in network.nodes:
        for w in network.nodes:
            if np.sum(np.abs(v['pos'] - w['pos'])) == 1 and (
                    v.uid, w.uid) not in network.edges:
                network.add_edge(v, w)
    return network
Beispiel #8
0
    def _add_path_from_edges(self, *edges: Union[str, Edge],
                             uid: Optional[str] = None, **kwargs: Any) -> None:
        """Helper function to add a path from edges."""
        _edges: list = []
        for edge in edges:
            if edge not in self.edges or self.multiedges:
                if isinstance(edge, str) and len(_edges) > 0:
                    self.edges.add(Edge(_edges[-1].w, Node(), uid=edge))
                else:
                    self.edges.add(edge, nodes=False)
            _edges.append(self.edges[edge])

        _path = _edges
        if _path not in self or self.multipaths:
            self._add_path(self._path_class(*_path, uid=uid, **kwargs))
        else:
            # raise error if node already exists
            self._if_exist(_path, **kwargs)
Beispiel #9
0
def read_graphml(filename: str):
    """Reads a pathyp.Network from a graphml file. This function supports typed Node and Edge attributes 
    including default values. 
    
    Warnings are issued if the type of Node or Edge attributes are undeclared,  in which case the attribute type will fall back to string.

    Parameters
    ----------

    filename: str
        The graphml file to read the graph from
    
    """
    root = ET.parse(filename).getroot()

    graph = root.find('{http://graphml.graphdrawing.org/xmlns}graph')
    directed = graph.attrib['edgedefault'] != 'undirected'
    uid = graph.attrib['id']
    n = Network(directed=directed, uid=uid)

    node_attributes = {}
    edge_attributes = {}

    # read attribute types and default values
    for a in root.findall('{http://graphml.graphdrawing.org/xmlns}key'):
        a_id = a.attrib['id']
        a_name = a.attrib['attr.name']
        a_type = a.attrib['attr.type']
        a_for = a.attrib['for']

        # store attribute info and assign data types
        a_data = {'name': a_name}
        if a_type == 'string':
            a_data['type'] = str
        elif a_type == 'float':
            a_data['type'] = float
        elif a_type == 'double':
            a_data['type'] = float
        elif a_type == 'int':
            a_data['type'] = int
        elif a_type == 'long':
            a_data['type'] = int
        elif a_type == 'boolean':
            a_data['type'] = bool
        else:
            a_data['type'] = str

        d = a.find('{http://graphml.graphdrawing.org/xmlns}default')
        if d is not None:
            a_data['default'] = a_data['type'](d.text)

        if a_for == 'node':
            node_attributes[a_name] = a_data
        if a_for == 'edge':
            edge_attributes[a_name] = a_data

    # add nodes with uids and attributes
    for node in graph.findall('{http://graphml.graphdrawing.org/xmlns}node'):
        # create node
        uid = node.attrib['id']
        v = Node(uid=uid)

        # set attribute values
        for a in node.findall('{http://graphml.graphdrawing.org/xmlns}data'):
            key = a.attrib['key']
            val = a.text
            if key not in node_attributes:
                LOG.warning(
                    'Undeclared Node attribute "{}". Defaulting to string type.'
                    .format(key))
                v.attributes[key] = val
            else:
                v.attributes[key] = node_attributes[key]['type'](val)

        # set default values
        for a_name in node_attributes:
            if 'default' in node_attributes[
                    a_name] and v.attributes[a_name] is None:
                v.attributes[a_name] = node_attributes[a_name]['default']
        n.add_node(v)

    # add edges with uids and attributes
    for edge in graph.findall('{http://graphml.graphdrawing.org/xmlns}edge'):
        # create edge
        source = edge.attrib['source']
        target = edge.attrib['target']
        uid = edge.attrib['id']
        e = Edge(n.nodes[source], n.nodes[target], uid=uid)

        # set attribute values
        for a in edge.findall('{http://graphml.graphdrawing.org/xmlns}data'):
            key = a.attrib['key']
            val = a.text
            if key not in edge_attributes:
                LOG.warning(
                    'Warning: Undeclared Edge attribute "{}". Defaulting to string type.'
                    .format(key))
                e.attributes[key] = val
            else:
                e.attributes[key] = edge_attributes[key]['type'](val)
        # set default values
        for a_name in edge_attributes:
            if 'default' in edge_attributes[
                    a_name] and e.attributes[a_name] is None:
                e.attributes[a_name] = edge_attributes[a_name]['default']
        n.add_edge(e)
    return n
Beispiel #10
0
def Watts_Strogatz(n: int,
                   s: int,
                   p: float = 0.0,
                   loops: bool = False,
                   node_uids: Optional[list] = None) -> Network:
    """Undirected Watts-Strogatz lattice network

    Generates an undirected Watts-Strogatz lattice network with lattice
    dimensionality one.

    Parameters
    ----------
    n : int

        The number of nodes in the generated network

    s : float

        The number of nearest neighbors that will be connected
        in the ring lattice

    p : float

        The rewiring probability

    Examples
    --------
    Generate a Watts-Strogatz network with 100 nodes

    >>> import pathpy as pp
    >>> small_world = pp.algorithms.random_graphs.Watts_Strogatz(n=100, s=2, p=0.1)
    >>> print(small_world.summary())
    ...

    """
    network = Network(directed=False)
    if node_uids is None or len(node_uids) != n:
        LOG.info('No valid node uids given, generating numeric node uids')
        node_uids = []
        for i in range(n):
            network.add_node(Node(str(i)))
            node_uids.append(str(i))
    else:
        for i in range(n):
            network.add_node(node_uids[i])

    # construct a ring lattice (dimension 1)
    for i in range(n):
        if loops:
            x = 0
            y = s
        else:
            x = 1
            y = s + 1
        for j in range(x, y):
            v = network.nodes[node_uids[i]]
            w = network.nodes[node_uids[(i + j) % n]]
            if (v.uid, w.uid) not in network.edges:
                network.add_edge(v, w)

    if p == 0:
        # nothing to do here
        return network

    # Rewire each link with probability p
    for edge in tqdm(list(network.edges.values()), 'generating WS network'):
        if np.random.rand() < p:
            # Delete original link and remember source node
            v = edge.v.uid
            network.remove_edge(edge)

            # Find new random tgt, which is not yet connected to src
            new_target = None

            # This loop repeatedly chooses a random target until we find
            # a target not yet connected to src. Note that this could potentially
            # result in an infinite loop depending on parameters.
            while new_target is None:
                x = str(np.random.randint(n))
                if (x != v or loops) and (v, x) not in network.edges:
                    new_target = x
            network.add_edge(v, new_target)
    return network
Beispiel #11
0
def read_pathcollection(filename: str,
                        separator: str = ',',
                        frequency: bool = False,
                        directed: bool = True,
                        maxlines: int = None) -> PathCollection:
    """Read path in edgelist format

    Reads data from a file containing multiple lines of *edges* of the form
    "v,w,frequency,X" (where frequency is optional and X are arbitrary
    additional columns). The default separating character ',' can be changed.

    Parameters
    ----------
    filename : str
        path to edgelist file
    separator : str
        character separating the nodes
    frequency : bool
        is a frequency given? if ``True`` it is the last element in the
        edge (i.e. ``a,b,2``)
    directed : bool
        are the edges directed or undirected
    maxlines : int
        number of lines to read (useful to test large files).
        None means the entire file is read

    """

    from pathpy.core.path import Path, PathCollection

    nodes: dict = {}
    edges: dict = {}
    paths: dict = {}

    with open(filename, 'r') as csv:
        for n, line in enumerate(csv):
            fields = line.rstrip().split(separator)
            assert len(fields) >= 1, 'Error: empty line: {0}'.format(line)

            if frequency:
                path = tuple(fields[:-1])
                freq = float(fields[-1])
            else:
                path = tuple(fields)
                freq = 1.0

            for node in path:
                if node not in nodes:
                    nodes[node] = Node(node)

            if len(path) == 1 and path not in paths:
                paths[path] = Path(nodes[path[0]], frequency=freq)

            else:
                edge_list = []
                for u, v in zip(path[:-1], path[1:]):
                    if (u, v) not in edges:
                        edges[(u, v)] = Edge(nodes[u], nodes[v])
                    edge_list.append(edges[(u, v)])

                if path not in paths:
                    paths[path] = Path(*edge_list, frequency=freq)

            if maxlines is not None and n >= maxlines:
                break

    ncoll = NodeCollection()
    for node in nodes.values():
        ncoll.add(node)

    ecoll = EdgeCollection(nodes=ncoll)
    for edge in edges.values():
        ecoll._add(edge)

    _paths = PathCollection(directed=directed, nodes=ncoll, edges=ecoll)

    for _path in paths.values():
        _paths._add(_path)

    return _paths
Beispiel #12
0
def to_temporal_network(frame: pd.DataFrame,
                        loops: bool = True,
                        directed: bool = True,
                        multiedges: bool = False,
                        **kwargs: Any) -> TemporalNetwork:
    """Read temporal network from a pandas data frame."""

    from pathpy.models.temporal_network import TemporalNetwork

    # if no v/w columns are included, pick first synonym
    frame = _check_column_name(frame, 'v', config['edge']['v_synonyms'])
    frame = _check_column_name(frame, 'w', config['edge']['w_synonyms'])

    _begin = config['temporal']['begin']
    _end = config['temporal']['end']
    _timestamp = config['temporal']['timestamp']
    _duration = config['temporal']['duration']

    _key_words = {
        'begin': _begin,
        'end': _end,
        'timestamp': _timestamp,
        'duration': _duration
    }

    for key, name in _key_words.items():
        frame = _check_column_name(frame, name,
                                   config['temporal'][key + '_synonyms'])

    if _timestamp in frame.columns:
        frame[_begin] = frame[_timestamp]
        if _duration in frame.columns:
            frame[_end] = frame[_timestamp] + frame[_duration]
        else:
            frame[_end] = frame[_timestamp] + \
                config['temporal']['duration_value']

    if _begin and _end not in frame.columns:
        LOG.error(
            'A TemporalNetwork needs "%s" and "%s" (or "%s" and "%s") '
            'attributes!', _begin, _end, _timestamp, _duration)
        raise IOError

    LOG.debug('Creating %s network', directed)

    node_set = set(frame['v']).union(set(frame['w']))

    if None in node_set:
        LOG.error('DataFrame minimally needs columns \'v\' and \'w\'')
        raise IOError

    nodes = {n: Node(n) for n in node_set}

    net = TemporalNetwork(directed=directed, multiedges=multiedges, **kwargs)
    for node in nodes.values():
        net.nodes.add(node)

    # TODO: Make this for loop faster!
    #rows = []
    #edges = {}
    for row in frame.to_dict(orient='records'):
        v = row.pop('v')
        w = row.pop('w')
        uid = row.pop('uid', None)
        # if (v, w) not in edges:
        #     edge = Edge(nodes[v], nodes[w], uid=uid, **row)
        #     net.edges._add(edge)
        #     edges[(v, w)] = edge
        # else:
        #     begin = row.pop(_begin)
        #     end = row.pop(_end)
        #     net.edges._intervals.addi(begin, end, edges[(v, w)])
        #     net.edges._interval_map[edges[(v, w)]].add((begin, end))
        net.add_edge(nodes[v], nodes[w], uid=uid, **row)
    # net.add_edge(nodes[v], nodes[w], uid=uid, **row)
    # net._add_edge_properties()
    return net
Beispiel #13
0
    def read_file(cls,
                  filename: str,
                  separator: str = ',',
                  frequency: bool = False,
                  directed: bool = True,
                  maxlines: int = None) -> None:
        """
        Read path in edgelist format

        Reads data from a file containing multiple lines of *edges* of the
        form "v,w,frequency,X" (where frequency is optional and X are
        arbitrary additional columns). The default separating character ','
        can be changed.

        Parameters
        ----------
        filename : str
            path to edgelist file
        separator : str
            character separating the nodes
        frequency : bool
            is a frequency given? if ``True`` it is the last element in the
            edge (i.e. ``a,b,2``)
        directed : bool
            are the edges directed or undirected
        maxlines : int
            number of lines to read (useful to test large files).
            None means the entire file is read
        """
        nodes = {}
        edges = {}
        paths = {}

        with open(filename, 'r') as f:
            for n, line in enumerate(f):
                fields = line.rstrip().split(separator)
                assert len(fields) >= 2, 'Error: malformed line: {0}'.format(
                    line)

                if frequency:
                    path = tuple(fields[:-1])
                    f = int(fields[-1])
                else:
                    path = tuple(fields)
                    f = 1

                for node in path:
                    if node not in nodes:
                        nodes[node] = Node(node)

                edge_list = []
                for u, v in zip(path[:-1], path[1:]):
                    if (u, v) not in edges:
                        edges[(u, v)] = Edge(nodes[u],
                                             nodes[v],
                                             uid=u + '-' + v)
                    edge_list.append(edges[(u, v)])

                if path not in paths:
                    paths[path] = Path(*edge_list, frequency=f)

                if maxlines is not None and n >= maxlines:
                    break

        nc = NodeCollection()
        nc.add(*nodes.values())

        ec = EdgeCollection(nodes=nc)
        for edge in edges.values():
            ec._add(edge)

        p = PathCollection(nodes=nc, edges=ec)

        for path in paths.values():
            p._add(path)

        return p