Example #1
0
def from_biadjacency_matrix(A, create_using=None, edge_attribute='weight'):
    r"""Creates a new bipartite graph from a biadjacency matrix given as a
    SciPy sparse matrix.

    Parameters
    ----------
    A: scipy sparse matrix
      A biadjacency matrix representation of a graph

    create_using: NetworkX graph
       Use specified graph for result.  The default is Graph()

    edge_attribute: string
       Name of edge attribute to store matrix numeric value. The data will
       have the same type as the matrix entry (int, float, (real,imag)).

    Notes
    -----
    The nodes are labeled with the attribute `bipartite` set to an integer
    0 or 1 representing membership in part 0 or part 1 of the bipartite graph.

    If `create_using` is an instance of :class:`networkx.MultiGraph` or
    :class:`networkx.MultiDiGraph` and the entries of `A` are of
    type :class:`int`, then this function returns a multigraph (of the same
    type as `create_using`) with parallel edges. In this case, `edge_attribute`
    will be ignored.

    See Also
    --------
    biadjacency_matrix
    from_numpy_matrix

    References
    ----------
    [1] http://en.wikipedia.org/wiki/Adjacency_matrix#Adjacency_matrix_of_a_bipartite_graph
    """
    G = _prep_create_using(create_using)
    n, m = A.shape
    # Make sure we get even the isolated nodes of the graph.
    G.add_nodes_from(range(n), bipartite=0)
    G.add_nodes_from(range(n,n+m), bipartite=1)
    # Create an iterable over (u, v, w) triples and for each triple, add an
    # edge from u to v with weight w.
    triples = ((u, n+v, d) for (u, v, d) in _generate_weighted_edges(A))
    # If the entries in the adjacency matrix are integers and the graph is a
    # multigraph, then create parallel edges, each with weight 1, for each
    # entry in the adjacency matrix. Otherwise, create one edge for each
    # positive entry in the adjacency matrix and set the weight of that edge to
    # be the entry in the matrix.
    if A.dtype.kind in ('i', 'u') and G.is_multigraph():
        chain = itertools.chain.from_iterable
        triples = chain(((u, v, 1) for d in range(w)) for (u, v, w) in triples)
    G.add_weighted_edges_from(triples, weight=edge_attribute)
    return G
Example #2
0
def from_biadjacency_matrix(A, create_using=None, edge_attribute='weight'):
    r"""Creates a new bipartite graph from a biadjacency matrix given as a
    SciPy sparse matrix.

    Parameters
    ----------
    A: scipy sparse matrix
      A biadjacency matrix representation of a graph

    create_using: NetworkX graph
       Use specified graph for result.  The default is Graph()

    edge_attribute: string
       Name of edge attribute to store matrix numeric value. The data will
       have the same type as the matrix entry (int, float, (real,imag)).

    Notes
    -----
    The nodes are labeled with the attribute `bipartite` set to an integer
    0 or 1 representing membership in part 0 or part 1 of the bipartite graph.

    If `create_using` is an instance of :class:`networkx.MultiGraph` or
    :class:`networkx.MultiDiGraph` and the entries of `A` are of
    type :class:`int`, then this function returns a multigraph (of the same
    type as `create_using`) with parallel edges. In this case, `edge_attribute`
    will be ignored.

    See Also
    --------
    biadjacency_matrix
    from_numpy_matrix

    References
    ----------
    [1] https://en.wikipedia.org/wiki/Adjacency_matrix#Adjacency_matrix_of_a_bipartite_graph
    """
    G = _prep_create_using(create_using)
    n, m = A.shape
    # Make sure we get even the isolated nodes of the graph.
    G.add_nodes_from(range(n), bipartite=0)
    G.add_nodes_from(range(n, n + m), bipartite=1)
    # Create an iterable over (u, v, w) triples and for each triple, add an
    # edge from u to v with weight w.
    triples = ((u, n + v, d) for (u, v, d) in _generate_weighted_edges(A))
    # If the entries in the adjacency matrix are integers and the graph is a
    # multigraph, then create parallel edges, each with weight 1, for each
    # entry in the adjacency matrix. Otherwise, create one edge for each
    # positive entry in the adjacency matrix and set the weight of that edge to
    # be the entry in the matrix.
    if A.dtype.kind in ('i', 'u') and G.is_multigraph():
        chain = itertools.chain.from_iterable
        triples = chain(((u, v, 1) for d in range(w)) for (u, v, w) in triples)
    G.add_weighted_edges_from(triples, weight=edge_attribute)
    return G
Example #3
0
def from_scipy_sparse_matrix(A, create_using=None, edge_attribute='weight'):
    """Return a graph from scipy sparse matrix adjacency list.

    Parameters
    ----------
    A: scipy sparse matrix
      An adjacency matrix representation of a graph

    create_using: NetworkX graph
       Use specified graph for result.  The default is Graph()

    edge_attribute: string
       Name of edge attrbute to store matrix numeric value. The data will
       have the same type as the matrix entry (int, float, (real,imag)).

    Examples
    --------
    >>> import scipy.sparse
    >>> A = scipy.sparse.eye(2,2,1)
    >>> G = nx.from_scipy_sparse_matrix(A)
    """
    G = _prep_create_using(create_using)
    n, m = A.shape
    if n != m:
        raise nx.NetworkXError(\
              "Adjacency matrix is not square. nx,ny=%s"%(A.shape,))
    G.add_nodes_from(range(n))  # make sure we get isolated nodes

    if A.format == 'coo':
        for i, j, d in zip(A.row, A.col, A.data):
            G.add_edge(i, j, **{edge_attribute: d})
    elif A.format == 'dia':
        # make a copy - could be done more efficiently
        B = A.tocoo()
        for i, j, d in zip(B.row, B.col, B.data):
            G.add_edge(i, j, **{edge_attribute: d})
    else:
        for i, j in zip(*A.nonzero()):
            G.add_edge(i, j, **{edge_attribute: A[i, j]})
    return G
Example #4
0
def from_scipy_sparse_matrix(A, create_using=None, edge_attribute='weight'):
    """Return a graph from scipy sparse matrix adjacency list.

    Parameters
    ----------
    A: scipy sparse matrix
      An adjacency matrix representation of a graph

    create_using: NetworkX graph
       Use specified graph for result.  The default is Graph()

    edge_attribute: string
       Name of edge attrbute to store matrix numeric value. The data will
       have the same type as the matrix entry (int, float, (real,imag)).

    Examples
    --------
    >>> import scipy.sparse
    >>> A = scipy.sparse.eye(2,2,1)
    >>> G = nx.from_scipy_sparse_matrix(A)
    """
    G = _prep_create_using(create_using)
    n,m = A.shape
    if n != m:
        raise nx.NetworkXError(\
              "Adjacency matrix is not square. nx,ny=%s"%(A.shape,))
    G.add_nodes_from(range(n)) # make sure we get isolated nodes

    if A.format == 'coo':
        for i,j,d in zip(A.row, A.col, A.data):
            G.add_edge(i,j,**{edge_attribute:d})
    elif A.format == 'dia':
        # make a copy - could be done more efficiently
        B = A.tocoo()
        for i,j,d in zip(B.row, B.col, B.data):
            G.add_edge(i,j,**{edge_attribute:d})
    else:
        for i,j in zip(*A.nonzero()):
            G.add_edge(i,j,**{edge_attribute:A[i,j]})
    return G
Example #5
0
def from_scipy_sparse_matrix(A, create_using=None, edge_attribute='weight'):
    """Return a graph from scipy sparse matrix adjacency list.

    Parameters
    ----------
    A: scipy sparse matrix
      An adjacency matrix representation of a graph

    create_using: NetworkX graph
       Use specified graph for result.  The default is Graph()

    edge_attribute: string
       Name of edge attribute to store matrix numeric value. The data will
       have the same type as the matrix entry (int, float, (real,imag)).

    Examples
    --------
    >>> import scipy.sparse
    >>> A = scipy.sparse.eye(2,2,1)
    >>> G = nx.from_scipy_sparse_matrix(A)
    """
    G = _prep_create_using(create_using)
    n, m = A.shape
    if n != m:
        raise nx.NetworkXError(\
              "Adjacency matrix is not square. nx,ny=%s"%(A.shape,))
    G.add_nodes_from(range(n))  # make sure we get isolated nodes

    if A.format == 'csr':
        triples = _csr_gen_triples(A)
    elif A.format == 'csc':
        triples = _csc_gen_triples(A)
    elif A.format == 'dok':
        triples = _dok_gen_triples(A)
    else:
        if A.format != 'coo':
            A = A.tocoo()
        triples = _coo_gen_triples(A)
    G.add_weighted_edges_from(triples, weight=edge_attribute)
    return G
Example #6
0
def from_scipy_sparse_matrix(A, create_using=None, edge_attribute='weight'):
    """Return a graph from scipy sparse matrix adjacency list.

    Parameters
    ----------
    A: scipy sparse matrix
      An adjacency matrix representation of a graph

    create_using: NetworkX graph
       Use specified graph for result.  The default is Graph()

    edge_attribute: string
       Name of edge attribute to store matrix numeric value. The data will
       have the same type as the matrix entry (int, float, (real,imag)).

    Examples
    --------
    >>> import scipy.sparse
    >>> A = scipy.sparse.eye(2,2,1)
    >>> G = nx.from_scipy_sparse_matrix(A)
    """
    G = _prep_create_using(create_using)
    n,m = A.shape
    if n != m:
        raise nx.NetworkXError(\
              "Adjacency matrix is not square. nx,ny=%s"%(A.shape,))
    G.add_nodes_from(range(n)) # make sure we get isolated nodes

    if A.format == 'csr':
        triples = _csr_gen_triples(A)
    elif A.format == 'csc':
        triples = _csc_gen_triples(A)
    elif A.format == 'dok':
        triples = _dok_gen_triples(A)
    else:
        if A.format != 'coo':
            A = A.tocoo()
        triples = _coo_gen_triples(A)
    G.add_weighted_edges_from(triples, weight=edge_attribute)
    return G
Example #7
0
def from_scipy_sparse_matrix(A,
                             parallel_edges=False,
                             create_using=None,
                             edge_attribute='weight'):
    """Creates a new graph from an adjacency matrix given as a SciPy sparse
    matrix.

    Parameters
    ----------
    A: scipy sparse matrix
      An adjacency matrix representation of a graph

    parallel_edges : Boolean
      If this is True, `create_using` is a multigraph, and `A` is an
      integer matrix, then entry *(i, j)* in the matrix is interpreted as the
      number of parallel edges joining vertices *i* and *j* in the graph. If it
      is False, then the entries in the adjacency matrix are interpreted as
      the weight of a single edge joining the vertices.

    create_using: NetworkX graph
       Use specified graph for result.  The default is Graph()

    edge_attribute: string
       Name of edge attribute to store matrix numeric value. The data will
       have the same type as the matrix entry (int, float, (real,imag)).

    Notes
    -----

    If `create_using` is an instance of :class:`networkx.MultiGraph` or
    :class:`networkx.MultiDiGraph`, `parallel_edges` is True, and the
    entries of `A` are of type :class:`int`, then this function returns a
    multigraph (of the same type as `create_using`) with parallel edges.
    In this case, `edge_attribute` will be ignored.

    If `create_using` is an undirected multigraph, then only the edges
    indicated by the upper triangle of the matrix `A` will be added to the
    graph.

    Examples
    --------
    >>> import scipy.sparse
    >>> A = scipy.sparse.eye(2,2,1)
    >>> G = nx.from_scipy_sparse_matrix(A)

    If `create_using` is a multigraph and the matrix has only integer entries,
    the entries will be interpreted as weighted edges joining the vertices
    (without creating parallel edges):

    >>> import scipy
    >>> A = scipy.sparse.csr_matrix([[1, 1], [1, 2]])
    >>> G = nx.from_scipy_sparse_matrix(A, create_using=nx.MultiGraph())
    >>> G[1][1]
    AtlasView({0: {'weight': 2}})

    If `create_using` is a multigraph and the matrix has only integer entries
    but `parallel_edges` is True, then the entries will be interpreted as
    the number of parallel edges joining those two vertices:

    >>> import scipy
    >>> A = scipy.sparse.csr_matrix([[1, 1], [1, 2]])
    >>> G = nx.from_scipy_sparse_matrix(A, parallel_edges=True,
    ...                                 create_using=nx.MultiGraph())
    >>> G[1][1]
    AtlasView({0: {'weight': 1}, 1: {'weight': 1}})

    """
    G = _prep_create_using(create_using)
    n, m = A.shape
    if n != m:
        raise nx.NetworkXError("Adjacency matrix is not square. nx,ny=%s" %
                               (A.shape, ))
    # Make sure we get even the isolated nodes of the graph.
    G.add_nodes_from(range(n))
    # Create an iterable over (u, v, w) triples and for each triple, add an
    # edge from u to v with weight w.
    triples = _generate_weighted_edges(A)
    # If the entries in the adjacency matrix are integers, the graph is a
    # multigraph, and parallel_edges is True, then create parallel edges, each
    # with weight 1, for each entry in the adjacency matrix. Otherwise, create
    # one edge for each positive entry in the adjacency matrix and set the
    # weight of that edge to be the entry in the matrix.
    if A.dtype.kind in ('i', 'u') and G.is_multigraph() and parallel_edges:
        chain = itertools.chain.from_iterable
        # The following line is equivalent to:
        #
        #     for (u, v) in edges:
        #         for d in range(A[u, v]):
        #             G.add_edge(u, v, weight=1)
        #
        triples = chain(((u, v, 1) for d in range(w)) for (u, v, w) in triples)
    # If we are creating an undirected multigraph, only add the edges from the
    # upper triangle of the matrix. Otherwise, add all the edges. This relies
    # on the fact that the vertices created in the
    # `_generated_weighted_edges()` function are actually the row/column
    # indices for the matrix `A`.
    #
    # Without this check, we run into a problem where each edge is added twice
    # when `G.add_weighted_edges_from()` is invoked below.
    if G.is_multigraph() and not G.is_directed():
        triples = ((u, v, d) for u, v, d in triples if u <= v)
    G.add_weighted_edges_from(triples, weight=edge_attribute)
    return G
Example #8
0
def from_numpy_matrix(A, parallel_edges=False, create_using=None):
    """Return a graph from numpy matrix.

    The numpy matrix is interpreted as an adjacency matrix for the graph.

    Parameters
    ----------
    A : numpy matrix
        An adjacency matrix representation of a graph

    parallel_edges : Boolean
        If this is True, `create_using` is a multigraph, and `A` is an
        integer matrix, then entry *(i, j)* in the matrix is interpreted as the
        number of parallel edges joining vertices *i* and *j* in the graph. If it
        is False, then the entries in the adjacency matrix are interpreted as
        the weight of a single edge joining the vertices.

    create_using : NetworkX graph
        Use specified graph for result. The default is Graph()

    Notes
    -----
    If `create_using` is an instance of :class:`networkx.MultiGraph` or
    :class:`networkx.MultiDiGraph`, `parallel_edges` is True, and the
    entries of `A` are of type :class:`int`, then this function returns a
    multigraph (of the same type as `create_using`) with parallel edges.

    If `create_using` is an undirected multigraph, then only the edges
    indicated by the upper triangle of the matrix `A` will be added to the
    graph.

    If the numpy matrix has a single data type for each matrix entry it
    will be converted to an appropriate Python data type.

    If the numpy matrix has a user-specified compound data type the names
    of the data fields will be used as attribute keys in the resulting
    NetworkX graph.

    See Also
    --------
    to_numpy_matrix, to_numpy_recarray

    Examples
    --------
    Simple integer weights on edges:

    >>> import numpy
    >>> A=numpy.matrix([[1, 1], [2, 1]])
    >>> G=nx.from_numpy_matrix(A)

    If `create_using` is a multigraph and the matrix has only integer entries,
    the entries will be interpreted as weighted edges joining the vertices
    (without creating parallel edges):

    >>> import numpy
    >>> A = numpy.matrix([[1, 1], [1, 2]])
    >>> G = nx.from_numpy_matrix(A, create_using = nx.MultiGraph())
    >>> G[1][1]
    AtlasView({0: {'weight': 2}})

    If `create_using` is a multigraph and the matrix has only integer entries
    but `parallel_edges` is True, then the entries will be interpreted as
    the number of parallel edges joining those two vertices:

    >>> import numpy
    >>> A = numpy.matrix([[1, 1], [1, 2]])
    >>> temp = nx.MultiGraph()
    >>> G = nx.from_numpy_matrix(A, parallel_edges = True, create_using = temp)
    >>> G[1][1]
    AtlasView({0: {'weight': 1}, 1: {'weight': 1}})

    User defined compound data type on edges:

    >>> import numpy
    >>> dt = [('weight', float), ('cost', int)]
    >>> A = numpy.matrix([[(1.0, 2)]], dtype = dt)
    >>> G = nx.from_numpy_matrix(A)
    >>> list(G.edges())
    [(0, 0)]
    >>> G[0][0]['cost']
    2
    >>> G[0][0]['weight']
    1.0

    """
    # This should never fail if you have created a numpy matrix with numpy...
    import numpy as np
    kind_to_python_type = {
        'f': float,
        'i': int,
        'u': int,
        'b': bool,
        'c': complex,
        'S': str,
        'V': 'void'
    }
    try:  # Python 3.x
        blurb = chr(1245)  # just to trigger the exception
        kind_to_python_type['U'] = str
    except ValueError:  # Python 2.6+
        kind_to_python_type['U'] = unicode
    G = _prep_create_using(create_using)
    n, m = A.shape
    if n != m:
        raise nx.NetworkXError("Adjacency matrix is not square.",
                               "nx,ny=%s" % (A.shape, ))
    dt = A.dtype
    try:
        python_type = kind_to_python_type[dt.kind]
    except:
        raise TypeError("Unknown numpy data type: %s" % dt)

    # Make sure we get even the isolated nodes of the graph.
    G.add_nodes_from(range(n))
    # Get a list of all the entries in the matrix with nonzero entries. These
    # coordinates will become the edges in the graph.
    edges = zip(*(np.asarray(A).nonzero()))
    # handle numpy constructed data type
    if python_type is 'void':
        # Sort the fields by their offset, then by dtype, then by name.
        fields = sorted((offset, dtype, name)
                        for name, (dtype, offset) in A.dtype.fields.items())
        triples = ((u, v, {
            name: kind_to_python_type[dtype.kind](val)
            for (_, dtype, name), val in zip(fields, A[u, v])
        }) for u, v in edges)
    # If the entries in the adjacency matrix are integers, the graph is a
    # multigraph, and parallel_edges is True, then create parallel edges, each
    # with weight 1, for each entry in the adjacency matrix. Otherwise, create
    # one edge for each positive entry in the adjacency matrix and set the
    # weight of that edge to be the entry in the matrix.
    elif python_type is int and G.is_multigraph() and parallel_edges:
        chain = itertools.chain.from_iterable
        # The following line is equivalent to:
        #
        #     for (u, v) in edges:
        #         for d in range(A[u, v]):
        #             G.add_edge(u, v, weight=1)
        #
        triples = chain(((u, v, dict(weight=1)) for d in range(A[u, v]))
                        for (u, v) in edges)
    else:  # basic data type
        triples = ((u, v, dict(weight=python_type(A[u, v]))) for u, v in edges)
    # If we are creating an undirected multigraph, only add the edges from the
    # upper triangle of the matrix. Otherwise, add all the edges. This relies
    # on the fact that the vertices created in the
    # `_generated_weighted_edges()` function are actually the row/column
    # indices for the matrix `A`.
    #
    # Without this check, we run into a problem where each edge is added twice
    # when `G.add_edges_from()` is invoked below.
    if G.is_multigraph() and not G.is_directed():
        triples = ((u, v, d) for u, v, d in triples if u <= v)
    G.add_edges_from(triples)
    return G
Example #9
0
def from_pandas_dataframe(df,
                          source='source',
                          target='target',
                          edge_attr=None,
                          create_using=None):
    """Return a graph from Pandas DataFrame containing an edge list.

    The Pandas DataFrame should contain at least two columns of node names and
    zero or more columns of node attributes. Each row will be processed as one
    edge instance.

    Note: This function iterates over DataFrame.values, which is not
    guaranteed to retain the data type across columns in the row. This is only
    a problem if your row is entirely numeric and a mix of ints and floats. In
    that case, all values will be returned as floats. See the
    DataFrame.iterrows documentation for an example.

    Parameters
    ----------
    df : Pandas DataFrame
        An edge list representation of a graph

    source : str or int
        A valid column name (string or iteger) for the source nodes (for the
        directed case).

    target : str or int
        A valid column name (string or iteger) for the target nodes (for the
        directed case).

    edge_attr : str or int, iterable, True
        A valid column name (str or integer) or list of column names that will
        be used to retrieve items from the row and add them to the graph as edge
        attributes. If `True`, all of the remaining columns will be added.

    create_using : NetworkX graph
        Use specified graph for result.  The default is Graph()

    See Also
    --------
    to_pandas_dataframe

    Examples
    --------
    Simple integer weights on edges:

    >>> import pandas as pd
    >>> import numpy as np
    >>> r = np.random.RandomState(seed=5)
    >>> ints = r.random_integers(1, 10, size=(3,2))
    >>> a = ['A', 'B', 'C']
    >>> b = ['D', 'A', 'E']
    >>> df = pd.DataFrame(ints, columns=['weight', 'cost'])
    >>> df[0] = a
    >>> df['b'] = b
    >>> df
       weight  cost  0  b
    0       4     7  A  D
    1       7     1  B  A
    2      10     9  C  E
    >>> G=nx.from_pandas_dataframe(df, 0, 'b', ['weight', 'cost'])
    >>> G['E']['C']['weight']
    10
    >>> G['E']['C']['cost']
    9
    >>> edges = pd.DataFrame({'source': [0, 1, 2],
    ...                       'target': [2, 2, 3],
    ...                       'weight': [3, 4, 5],
    ...                       'color': ['red', 'blue', 'blue']})
    >>> G = nx.from_pandas_dataframe(edges, edge_attr=True)
    >>> G[0][2]['color']
    'red'
    """

    g = _prep_create_using(create_using)

    # Index of source and target
    src_i = df.columns.get_loc(source)
    tar_i = df.columns.get_loc(target)
    if edge_attr:
        # If all additional columns requested, build up a list of tuples
        # [(name, index),...]
        if edge_attr is True:
            # Create a list of all columns indices, ignore nodes
            edge_i = []
            for i, col in enumerate(df.columns):
                if col is not source and col is not target:
                    edge_i.append((col, i))
        # If a list or tuple of name is requested
        elif isinstance(edge_attr, (list, tuple)):
            edge_i = [(i, df.columns.get_loc(i)) for i in edge_attr]
        # If a string or int is passed
        else:
            edge_i = [
                (edge_attr, df.columns.get_loc(edge_attr)),
            ]

        # Iteration on values returns the rows as Numpy arrays
        for row in df.values:
            s, t = row[src_i], row[tar_i]
            if g.is_multigraph():
                g.add_edge(s, t)
                key = max(
                    g[s][t])  # default keys just count, so max is most recent
                g[s][t][key].update((i, row[j]) for i, j in edge_i)
            else:
                g.add_edge(s, t)
                g[s][t].update((i, row[j]) for i, j in edge_i)

    # If no column names are given, then just return the edges.
    else:
        for row in df.values:
            g.add_edge(row[src_i], row[tar_i])

    return g
Example #10
0
def from_scipy_sparse_matrix(A, parallel_edges=False, create_using=None,
                             edge_attribute='weight'):
    """Creates a new graph from an adjacency matrix given as a SciPy sparse
    matrix.

    Parameters
    ----------
    A: scipy sparse matrix
      An adjacency matrix representation of a graph

    parallel_edges : Boolean
      If this is True, `create_using` is a multigraph, and `A` is an
      integer matrix, then entry *(i, j)* in the matrix is interpreted as the
      number of parallel edges joining vertices *i* and *j* in the graph. If it
      is False, then the entries in the adjacency matrix are interpreted as
      the weight of a single edge joining the vertices.

    create_using: NetworkX graph
       Use specified graph for result.  The default is Graph()

    edge_attribute: string
       Name of edge attribute to store matrix numeric value. The data will
       have the same type as the matrix entry (int, float, (real,imag)).

    Notes
    -----

    If `create_using` is an instance of :class:`networkx.MultiGraph` or
    :class:`networkx.MultiDiGraph`, `parallel_edges` is True, and the
    entries of `A` are of type :class:`int`, then this function returns a
    multigraph (of the same type as `create_using`) with parallel edges.
    In this case, `edge_attribute` will be ignored.

    If `create_using` is an undirected multigraph, then only the edges
    indicated by the upper triangle of the matrix `A` will be added to the
    graph.

    Examples
    --------
    >>> import scipy as sp
    >>> A = sp.sparse.eye(2, 2, 1)
    >>> G = nx.from_scipy_sparse_matrix(A)

    If `create_using` is a multigraph and the matrix has only integer entries,
    the entries will be interpreted as weighted edges joining the vertices
    (without creating parallel edges):

    >>> A = sp.sparse.csr_matrix([[1, 1], [1, 2]])
    >>> G = nx.from_scipy_sparse_matrix(A, create_using=nx.MultiGraph())
    >>> G[1][1]
    AtlasView({0: {'weight': 2}})

    If `create_using` is a multigraph and the matrix has only integer entries
    but `parallel_edges` is True, then the entries will be interpreted as
    the number of parallel edges joining those two vertices:

    >>> A = sp.sparse.csr_matrix([[1, 1], [1, 2]])
    >>> G = nx.from_scipy_sparse_matrix(A, parallel_edges=True,
    ...                                 create_using=nx.MultiGraph())
    >>> G[1][1]
    AtlasView({0: {'weight': 1}, 1: {'weight': 1}})

    """
    G = _prep_create_using(create_using)
    n, m = A.shape
    if n != m:
        raise nx.NetworkXError(
            "Adjacency matrix is not square. nx,ny=%s" % (A.shape,))
    # Make sure we get even the isolated nodes of the graph.
    G.add_nodes_from(range(n))
    # Create an iterable over (u, v, w) triples and for each triple, add an
    # edge from u to v with weight w.
    triples = _generate_weighted_edges(A)
    # If the entries in the adjacency matrix are integers, the graph is a
    # multigraph, and parallel_edges is True, then create parallel edges, each
    # with weight 1, for each entry in the adjacency matrix. Otherwise, create
    # one edge for each positive entry in the adjacency matrix and set the
    # weight of that edge to be the entry in the matrix.
    if A.dtype.kind in ('i', 'u') and G.is_multigraph() and parallel_edges:
        chain = itertools.chain.from_iterable
        # The following line is equivalent to:
        #
        #     for (u, v) in edges:
        #         for d in range(A[u, v]):
        #             G.add_edge(u, v, weight=1)
        #
        triples = chain(((u, v, 1) for d in range(w)) for (u, v, w) in triples)
    # If we are creating an undirected multigraph, only add the edges from the
    # upper triangle of the matrix. Otherwise, add all the edges. This relies
    # on the fact that the vertices created in the
    # `_generated_weighted_edges()` function are actually the row/column
    # indices for the matrix `A`.
    #
    # Without this check, we run into a problem where each edge is added twice
    # when `G.add_weighted_edges_from()` is invoked below.
    if G.is_multigraph() and not G.is_directed():
        triples = ((u, v, d) for u, v, d in triples if u <= v)
    G.add_weighted_edges_from(triples, weight=edge_attribute)
    return G
Example #11
0
def from_numpy_matrix(A, parallel_edges=False, create_using=None):
    """Return a graph from numpy matrix.

    The numpy matrix is interpreted as an adjacency matrix for the graph.

    Parameters
    ----------
    A : numpy matrix
        An adjacency matrix representation of a graph

    parallel_edges : Boolean
        If this is True, `create_using` is a multigraph, and `A` is an
        integer matrix, then entry *(i, j)* in the matrix is interpreted as the
        number of parallel edges joining vertices *i* and *j* in the graph. If it
        is False, then the entries in the adjacency matrix are interpreted as
        the weight of a single edge joining the vertices.

    create_using : NetworkX graph
        Use specified graph for result. The default is Graph()

    Notes
    -----
    If `create_using` is an instance of :class:`networkx.MultiGraph` or
    :class:`networkx.MultiDiGraph`, `parallel_edges` is True, and the
    entries of `A` are of type :class:`int`, then this function returns a
    multigraph (of the same type as `create_using`) with parallel edges.

    If `create_using` is an undirected multigraph, then only the edges
    indicated by the upper triangle of the matrix `A` will be added to the
    graph.

    If the numpy matrix has a single data type for each matrix entry it
    will be converted to an appropriate Python data type.

    If the numpy matrix has a user-specified compound data type the names
    of the data fields will be used as attribute keys in the resulting
    NetworkX graph.

    See Also
    --------
    to_numpy_matrix, to_numpy_recarray

    Examples
    --------
    Simple integer weights on edges:

    >>> import numpy as np
    >>> A = np.matrix([[1, 1], [2, 1]])
    >>> G = nx.from_numpy_matrix(A)

    If `create_using` is a multigraph and the matrix has only integer entries,
    the entries will be interpreted as weighted edges joining the vertices
    (without creating parallel edges):

    >>> A = np.matrix([[1, 1], [1, 2]])
    >>> G = nx.from_numpy_matrix(A, create_using=nx.MultiGraph())
    >>> G[1][1]
    AtlasView({0: {'weight': 2}})

    If `create_using` is a multigraph and the matrix has only integer entries
    but `parallel_edges` is True, then the entries will be interpreted as
    the number of parallel edges joining those two vertices:

    >>> A = np.matrix([[1, 1], [1, 2]])
    >>> temp = nx.MultiGraph()
    >>> G = nx.from_numpy_matrix(A, parallel_edges=True, create_using=temp)
    >>> G[1][1]
    AtlasView({0: {'weight': 1}, 1: {'weight': 1}})

    User defined compound data type on edges:

    >>> dt = [('weight', float), ('cost', int)]
    >>> A = np.matrix([[(1.0, 2)]], dtype=dt)
    >>> G = nx.from_numpy_matrix(A)
    >>> list(G.edges())
    [(0, 0)]
    >>> G[0][0]['cost']
    2
    >>> G[0][0]['weight']
    1.0

    """
    # This should never fail if you have created a numpy matrix with numpy...
    import numpy as np
    kind_to_python_type = {'f': float,
                           'i': int,
                           'u': int,
                           'b': bool,
                           'c': complex,
                           'S': str,
                           'V': 'void'}
    try:  # Python 3.x
        blurb = chr(1245)  # just to trigger the exception
        kind_to_python_type['U'] = str
    except ValueError:  # Python 2.7
        kind_to_python_type['U'] = unicode
    G = _prep_create_using(create_using)
    n, m = A.shape
    if n != m:
        raise nx.NetworkXError("Adjacency matrix is not square.",
                               "nx,ny=%s" % (A.shape,))
    dt = A.dtype
    try:
        python_type = kind_to_python_type[dt.kind]
    except:
        raise TypeError("Unknown numpy data type: %s" % dt)

    # Make sure we get even the isolated nodes of the graph.
    G.add_nodes_from(range(n))
    # Get a list of all the entries in the matrix with nonzero entries. These
    # coordinates will become the edges in the graph.
    edges = zip(*(np.asarray(A).nonzero()))
    # handle numpy constructed data type
    if python_type is 'void':
        # Sort the fields by their offset, then by dtype, then by name.
        fields = sorted((offset, dtype, name) for name, (dtype, offset) in
                        A.dtype.fields.items())
        triples = ((u, v, {name: kind_to_python_type[dtype.kind](val)
                           for (_, dtype, name), val in zip(fields, A[u, v])})
                   for u, v in edges)
    # If the entries in the adjacency matrix are integers, the graph is a
    # multigraph, and parallel_edges is True, then create parallel edges, each
    # with weight 1, for each entry in the adjacency matrix. Otherwise, create
    # one edge for each positive entry in the adjacency matrix and set the
    # weight of that edge to be the entry in the matrix.
    elif python_type is int and G.is_multigraph() and parallel_edges:
        chain = itertools.chain.from_iterable
        # The following line is equivalent to:
        #
        #     for (u, v) in edges:
        #         for d in range(A[u, v]):
        #             G.add_edge(u, v, weight=1)
        #
        triples = chain(((u, v, dict(weight=1)) for d in range(A[u, v]))
                        for (u, v) in edges)
    else:  # basic data type
        triples = ((u, v, dict(weight=python_type(A[u, v])))
                   for u, v in edges)
    # If we are creating an undirected multigraph, only add the edges from the
    # upper triangle of the matrix. Otherwise, add all the edges. This relies
    # on the fact that the vertices created in the
    # `_generated_weighted_edges()` function are actually the row/column
    # indices for the matrix `A`.
    #
    # Without this check, we run into a problem where each edge is added twice
    # when `G.add_edges_from()` is invoked below.
    if G.is_multigraph() and not G.is_directed():
        triples = ((u, v, d) for u, v, d in triples if u <= v)
    G.add_edges_from(triples)
    return G
Example #12
0
def from_pandas_edgelist(df, source='source', target='target', edge_attr=None,
                         create_using=None):
    """Return a graph from Pandas DataFrame containing an edge list.

    The Pandas DataFrame should contain at least two columns of node names and
    zero or more columns of node attributes. Each row will be processed as one
    edge instance.

    Note: This function iterates over DataFrame.values, which is not
    guaranteed to retain the data type across columns in the row. This is only
    a problem if your row is entirely numeric and a mix of ints and floats. In
    that case, all values will be returned as floats. See the
    DataFrame.iterrows documentation for an example.

    Parameters
    ----------
    df : Pandas DataFrame
        An edge list representation of a graph

    source : str or int
        A valid column name (string or iteger) for the source nodes (for the
        directed case).

    target : str or int
        A valid column name (string or iteger) for the target nodes (for the
        directed case).

    edge_attr : str or int, iterable, True
        A valid column name (str or integer) or list of column names that will
        be used to retrieve items from the row and add them to the graph as edge
        attributes. If `True`, all of the remaining columns will be added.

    create_using : NetworkX graph
        Use specified graph for result.  The default is Graph()

    See Also
    --------
    to_pandas_edgelist

    Examples
    --------
    Simple integer weights on edges:

    >>> import pandas as pd
    >>> import numpy as np
    >>> r = np.random.RandomState(seed=5)
    >>> ints = r.random_integers(1, 10, size=(3,2))
    >>> a = ['A', 'B', 'C']
    >>> b = ['D', 'A', 'E']
    >>> df = pd.DataFrame(ints, columns=['weight', 'cost'])
    >>> df[0] = a
    >>> df['b'] = b
    >>> df
       weight  cost  0  b
    0       4     7  A  D
    1       7     1  B  A
    2      10     9  C  E
    >>> G = nx.from_pandas_edgelist(df, 0, 'b', ['weight', 'cost'])
    >>> G['E']['C']['weight']
    10
    >>> G['E']['C']['cost']
    9
    >>> edges = pd.DataFrame({'source': [0, 1, 2],
    ...                       'target': [2, 2, 3],
    ...                       'weight': [3, 4, 5],
    ...                       'color': ['red', 'blue', 'blue']})
    >>> G = nx.from_pandas_edgelist(edges, edge_attr=True)
    >>> G[0][2]['color']
    'red'

    """

    g = _prep_create_using(create_using)

    # Index of source and target
    src_i = df.columns.get_loc(source)
    tar_i = df.columns.get_loc(target)
    if edge_attr:
        # If all additional columns requested, build up a list of tuples
        # [(name, index),...]
        if edge_attr is True:
            # Create a list of all columns indices, ignore nodes
            edge_i = []
            for i, col in enumerate(df.columns):
                if col is not source and col is not target:
                    edge_i.append((col, i))
        # If a list or tuple of name is requested
        elif isinstance(edge_attr, (list, tuple)):
            edge_i = [(i, df.columns.get_loc(i)) for i in edge_attr]
        # If a string or int is passed
        else:
            edge_i = [(edge_attr, df.columns.get_loc(edge_attr)), ]

        # Iteration on values returns the rows as Numpy arrays
        for row in df.values:
            s, t = row[src_i], row[tar_i]
            if g.is_multigraph():
                g.add_edge(s, t)
                key = max(g[s][t])  # default keys just count, so max is most recent
                g[s][t][key].update((i, row[j]) for i, j in edge_i)
            else:
                g.add_edge(s, t)
                g[s][t].update((i, row[j]) for i, j in edge_i)

    # If no column names are given, then just return the edges.
    else:
        for row in df.values:
            g.add_edge(row[src_i], row[tar_i])

    return g
Example #13
0
def from_numpy_matrix(A, create_using=None):
    """Return a graph from numpy matrix.

    The numpy matrix is interpreted as an adjacency matrix for the graph.

    Parameters
    ----------
    A : numpy matrix
      An adjacency matrix representation of a graph

    create_using : NetworkX graph
       Use specified graph for result.  The default is Graph()

    Notes
    -----
    If the numpy matrix has a single data type for each matrix entry it
    will be converted to an appropriate Python data type.

    If the numpy matrix has a user-specified compound data type the names
    of the data fields will be used as attribute keys in the resulting
    NetworkX graph.

    See Also
    --------
    to_numpy_matrix, to_numpy_recarray

    Examples
    --------
    Simple integer weights on edges:

    >>> import numpy
    >>> A=numpy.matrix([[1,1],[2,1]])
    >>> G=nx.from_numpy_matrix(A)

    User defined compound data type on edges:

    >>> import numpy
    >>> dt=[('weight',float),('cost',int)]
    >>> A=numpy.matrix([[(1.0,2)]],dtype=dt)
    >>> G=nx.from_numpy_matrix(A)
    >>> G.edges()
    [(0, 0)]
    >>> G[0][0]['cost']
    2
    >>> G[0][0]['weight']
    1.0
    """
    # This should never fail if you have created a numpy matrix with numpy...
    import numpy as np
    kind_to_python_type = {
        'f': float,
        'i': int,
        'u': int,
        'b': bool,
        'c': complex,
        'S': str,
        'V': 'void'
    }
    try:  # Python 3.x
        blurb = chr(1245)  # just to trigger the exception
        kind_to_python_type['U'] = str
    except ValueError:  # Python 2.6+
        kind_to_python_type['U'] = unicode
    G = _prep_create_using(create_using)
    n, m = A.shape
    if n != m:
        raise nx.NetworkXError("Adjacency matrix is not square.",
                               "nx,ny=%s" % (A.shape, ))
    dt = A.dtype
    try:
        python_type = kind_to_python_type[dt.kind]
    except:
        raise TypeError("Unknown numpy data type: %s" % dt)

    # make sure we get isolated nodes
    G.add_nodes_from(range(n))
    # get a list of edges
    x, y = np.asarray(A).nonzero()

    # handle numpy constructed data type
    if python_type is 'void':
        fields = sorted([(offset, dtype, name)
                         for name, (dtype, offset) in A.dtype.fields.items()])
        for (u, v) in zip(x, y):
            attr = {}
            for (offset, dtype, name), val in zip(fields, A[u, v]):
                attr[name] = kind_to_python_type[dtype.kind](val)
            G.add_edge(u, v, attr)
    else:  # basic data type
        G.add_edges_from(((u, v, {
            'weight': python_type(A[u, v])
        }) for (u, v) in zip(x, y)))
    return G
Example #14
0
def parse_edgelist(lines, comments='#', delimiter=None,
                   create_using=None, nodetype=None, data=True):
    """Parse lines of an edge list representation of a bipartite graph.

    Parameters
    ----------
    lines : list or iterator of strings
        Input data in edgelist format
    comments : string, optional
       Marker for comment lines
    delimiter : string, optional
       Separator for node labels
    create_using: NetworkX graph container, optional
       Use given NetworkX graph for holding nodes or edges.
    nodetype : Python type, optional
       Convert nodes to this type.
    data : bool or list of (label,type) tuples
       If False generate no edge data or if True use a dictionary
       representation of edge data or a list tuples specifying dictionary
       key names and types for edge data.

    Returns
    -------
    G: NetworkX Graph
        The bipartite graph corresponding to lines

    Examples
    --------
    Edgelist with no data:

    >>> from networkx.algorithms import bipartite
    >>> lines = ["1 2",
    ...          "2 3",
    ...          "3 4"]
    >>> G = bipartite.parse_edgelist(lines, nodetype = int)
    >>> sorted(G.nodes())
    [1, 2, 3, 4]
    >>> sorted(G.nodes(data=True))
    [(1, {'bipartite': 0}), (2, {'bipartite': 0}), (3, {'bipartite': 0}), (4, {'bipartite': 1})]
    >>> sorted(G.edges())
    [(1, 2), (2, 3), (3, 4)]

    Edgelist with data in Python dictionary representation:

    >>> lines = ["1 2 {'weight':3}",
    ...          "2 3 {'weight':27}",
    ...          "3 4 {'weight':3.0}"]
    >>> G = bipartite.parse_edgelist(lines, nodetype = int)
    >>> sorted(G.nodes())
    [1, 2, 3, 4]
    >>> sorted(G.edges(data = True))
    [(1, 2, {'weight': 3}), (2, 3, {'weight': 27}), (3, 4, {'weight': 3.0})]

    Edgelist with data in a list:

    >>> lines = ["1 2 3",
    ...          "2 3 27",
    ...          "3 4 3.0"]
    >>> G = bipartite.parse_edgelist(lines, nodetype = int, data=(('weight',float),))
    >>> sorted(G.nodes())
    [1, 2, 3, 4]
    >>> sorted(G.edges(data = True))
    [(1, 2, {'weight': 3.0}), (2, 3, {'weight': 27.0}), (3, 4, {'weight': 3.0})]

    See Also
    --------
    """
    from ast import literal_eval
    G = _prep_create_using(create_using)
    for line in lines:
        p=line.find(comments)
        if p>=0:
            line = line[:p]
        if not len(line):
            continue
        # split line, should have 2 or more
        s=line.strip().split(delimiter)
        if len(s)<2:
            continue
        u=s.pop(0)
        v=s.pop(0)
        d=s
        if nodetype is not None:
            try:
                u=nodetype(u)
                v=nodetype(v)
            except:
                raise TypeError("Failed to convert nodes %s,%s to type %s."
                                %(u,v,nodetype))

        if len(d)==0 or data is False:
            # no data or data type specified
            edgedata={}
        elif data is True:
            # no edge types specified
            try: # try to evaluate as dictionary
                edgedata=dict(literal_eval(' '.join(d)))
            except:
                raise TypeError(
                    "Failed to convert edge data (%s) to dictionary."%(d))
        else:
            # convert edge data to dictionary with specified keys and type
            if len(d)!=len(data):
                raise IndexError(
                    "Edge data %s and data_keys %s are not the same length"%
                    (d, data))
            edgedata={}
            for (edge_key,edge_type),edge_value in zip(data,d):
                try:
                    edge_value=edge_type(edge_value)
                except:
                    raise TypeError(
                        "Failed to convert %s data %s to type %s."
                        %(edge_key, edge_value, edge_type))
                edgedata.update({edge_key:edge_value})
        G.add_node(u, bipartite=0)
        G.add_node(v, bipartite=1)
        G.add_edge(u, v, attr_dict=edgedata)
    return G
Example #15
0
def parse_edgelist(lines,
                   comments='#',
                   delimiter=None,
                   create_using=None,
                   nodetype=None,
                   data=True):
    """Parse lines of an edge list representation of a bipartite graph.

    Parameters
    ----------
    lines : list or iterator of strings
        Input data in edgelist format
    comments : string, optional
       Marker for comment lines
    delimiter : string, optional
       Separator for node labels
    create_using: NetworkX graph container, optional
       Use given NetworkX graph for holding nodes or edges.
    nodetype : Python type, optional
       Convert nodes to this type.
    data : bool or list of (label,type) tuples
       If False generate no edge data or if True use a dictionary
       representation of edge data or a list tuples specifying dictionary
       key names and types for edge data.

    Returns
    -------
    G: NetworkX Graph
        The bipartite graph corresponding to lines

    Examples
    --------
    Edgelist with no data:

    >>> from networkx.algorithms import bipartite
    >>> lines = ["1 2",
    ...          "2 3",
    ...          "3 4"]
    >>> G = bipartite.parse_edgelist(lines, nodetype = int)
    >>> sorted(G.nodes())
    [1, 2, 3, 4]
    >>> sorted(G.nodes(data=True))
    [(1, {'bipartite': 0}), (2, {'bipartite': 0}), (3, {'bipartite': 0}), (4, {'bipartite': 1})]
    >>> sorted(G.edges())
    [(1, 2), (2, 3), (3, 4)]

    Edgelist with data in Python dictionary representation:

    >>> lines = ["1 2 {'weight':3}",
    ...          "2 3 {'weight':27}",
    ...          "3 4 {'weight':3.0}"]
    >>> G = bipartite.parse_edgelist(lines, nodetype = int)
    >>> sorted(G.nodes())
    [1, 2, 3, 4]
    >>> sorted(G.edges(data = True))
    [(1, 2, {'weight': 3}), (2, 3, {'weight': 27}), (3, 4, {'weight': 3.0})]

    Edgelist with data in a list:

    >>> lines = ["1 2 3",
    ...          "2 3 27",
    ...          "3 4 3.0"]
    >>> G = bipartite.parse_edgelist(lines, nodetype = int, data=(('weight',float),))
    >>> sorted(G.nodes())
    [1, 2, 3, 4]
    >>> sorted(G.edges(data = True))
    [(1, 2, {'weight': 3.0}), (2, 3, {'weight': 27.0}), (3, 4, {'weight': 3.0})]

    See Also
    --------
    """
    from ast import literal_eval
    G = _prep_create_using(create_using)
    for line in lines:
        p = line.find(comments)
        if p >= 0:
            line = line[:p]
        if not len(line):
            continue
        # split line, should have 2 or more
        s = line.strip().split(delimiter)
        if len(s) < 2:
            continue
        u = s.pop(0)
        v = s.pop(0)
        d = s
        if nodetype is not None:
            try:
                u = nodetype(u)
                v = nodetype(v)
            except:
                raise TypeError("Failed to convert nodes %s,%s to type %s." %
                                (u, v, nodetype))

        if len(d) == 0 or data is False:
            # no data or data type specified
            edgedata = {}
        elif data is True:
            # no edge types specified
            try:  # try to evaluate as dictionary
                edgedata = dict(literal_eval(' '.join(d)))
            except:
                raise TypeError(
                    "Failed to convert edge data (%s) to dictionary." % (d))
        else:
            # convert edge data to dictionary with specified keys and type
            if len(d) != len(data):
                raise IndexError(
                    "Edge data %s and data_keys %s are not the same length" %
                    (d, data))
            edgedata = {}
            for (edge_key, edge_type), edge_value in zip(data, d):
                try:
                    edge_value = edge_type(edge_value)
                except:
                    raise TypeError(
                        "Failed to convert %s data %s to type %s." %
                        (edge_key, edge_value, edge_type))
                edgedata.update({edge_key: edge_value})
        G.add_node(u, bipartite=0)
        G.add_node(v, bipartite=1)
        G.add_edge(u, v, attr_dict=edgedata)
    return G
Example #16
0
def from_numpy_matrix(A,create_using=None):
    """Return a graph from numpy matrix.

    The numpy matrix is interpreted as an adjacency matrix for the graph.

    Parameters
    ----------
    A : numpy matrix
      An adjacency matrix representation of a graph

    create_using : NetworkX graph
       Use specified graph for result.  The default is Graph()

    Notes
    -----
    If the numpy matrix has a single data type for each matrix entry it
    will be converted to an appropriate Python data type.

    If the numpy matrix has a user-specified compound data type the names
    of the data fields will be used as attribute keys in the resulting
    NetworkX graph.

    See Also
    --------
    to_numpy_matrix, to_numpy_recarray

    Examples
    --------
    Simple integer weights on edges:

    >>> import numpy
    >>> A=numpy.matrix([[1,1],[2,1]])
    >>> G=nx.from_numpy_matrix(A)

    User defined compound data type on edges:

    >>> import numpy
    >>> dt=[('weight',float),('cost',int)]
    >>> A=numpy.matrix([[(1.0,2)]],dtype=dt)
    >>> G=nx.from_numpy_matrix(A)
    >>> G.edges()
    [(0, 0)]
    >>> G[0][0]['cost']
    2
    >>> G[0][0]['weight']
    1.0
    """
    # This should never fail if you have created a numpy matrix with numpy...
    import numpy as np
    kind_to_python_type={'f':float,
                         'i':int,
                         'u':int,
                         'b':bool,
                         'c':complex,
                         'S':str,
                         'V':'void'}
    try: # Python 3.x
        blurb = chr(1245) # just to trigger the exception
        kind_to_python_type['U']=str
    except ValueError: # Python 2.6+
        kind_to_python_type['U']=unicode
    G=_prep_create_using(create_using)
    n,m=A.shape
    if n!=m:
        raise nx.NetworkXError("Adjacency matrix is not square.",
                               "nx,ny=%s"%(A.shape,))
    dt=A.dtype
    try:
        python_type=kind_to_python_type[dt.kind]
    except:
        raise TypeError("Unknown numpy data type: %s"%dt)

    # make sure we get isolated nodes
    G.add_nodes_from(range(n))
    # get a list of edges
    x,y=np.asarray(A).nonzero()

    # handle numpy constructed data type
    if python_type is 'void':
        fields=sorted([(offset,dtype,name) for name,(dtype,offset) in
                       A.dtype.fields.items()])
        for (u,v) in zip(x,y):
            attr={}
            for (offset,dtype,name),val in zip(fields,A[u,v]):
                attr[name]=kind_to_python_type[dtype.kind](val)
            G.add_edge(u,v,attr)
    else: # basic data type
        G.add_edges_from( ((u,v,{'weight':python_type(A[u,v])})
                           for (u,v) in zip(x,y)) )
    return G