def barabasi_albert_graph(n, m, seed=None, initial_graph=None): if m < 1 or m >= n: raise nx.NetworkXError( "Barabási–Albert network must have m >= 1" " and m < n, m = %d, n = %d" % (m, n) ) if initial_graph is None: # Default initial graph : star graph on (m + 1) nodes G = nx.star_graph(m) else: if len(initial_graph) < m or len(initial_graph) > n: raise nx.NetworkXError( f"Barabási–Albert initial graph needs between m={m} and n={n} nodes" ) G = initial_graph.copy() # List of existing nodes, with nodes repeated once for each adjacent edge repeated_nodes = [n for n, d in G.degree() for _ in range(d)] # Start adding the other n - m0 nodes. source = len(G) while source < n: # Now choose m unique nodes from the existing nodes # Pick uniformly from repeated_nodes (preferential attachment) targets = _random_subset(repeated_nodes, m, seed) # Add edges to m nodes from the source. G.add_edges_from(zip([source] * m, targets)) # Add one node to the list for each new edge just created. repeated_nodes.extend(targets) # And the new node "source" has m edges to add to the list. repeated_nodes.extend([source] * m) source += 1 return G
def dual_barabasi_albert_graph(n, m1, m2, p, seed=None, initial_graph=None): if m1 < 1 or m1 >= n: raise nx.NetworkXError( "Dual Barabási–Albert network must have m1 >= 1" " and m1 < n, m1 = %d, n = %d" % (m1, n) ) if m2 < 1 or m2 >= n: raise nx.NetworkXError( "Dual Barabási–Albert network must have m2 >= 1" " and m2 < n, m2 = %d, n = %d" % (m2, n) ) if p < 0 or p > 1: raise nx.NetworkXError( "Dual Barabási–Albert network must have 0 <= p <= 1," "p = %f" % p ) # For simplicity, if p == 0 or 1, just return BA if p == 1: return barabasi_albert_graph(n, m1, seed) if p == 0: return barabasi_albert_graph(n, m2, seed) if initial_graph is None: # Default initial graph : empty graph on max(m1, m2) nodes G = nx.star_graph(max(m1, m2)) else: if len(initial_graph) < max(m1, m2) or len(initial_graph) > n: raise nx.NetworkXError( f"Barabási–Albert initial graph must have between " f"max(m1, m2) = {max(m1, m2)} and n = {n} nodes" ) G = initial_graph.copy() # Target nodes for new edges targets = list(G) # List of existing nodes, with nodes repeated once for each adjacent edge repeated_nodes = [n for n, d in G.degree() for _ in range(d)] # Start adding the remaining nodes. source = len(G) while source < n: # Pick which m to use (m1 or m2) if seed.random() < p: m = m1 else: m = m2 # Now choose m unique nodes from the existing nodes # Pick uniformly from repeated_nodes (preferential attachment) targets = _random_subset(repeated_nodes, m, seed) # Add edges to m nodes from the source. G.add_edges_from(zip([source] * m, targets)) # Add one node to the list for each new edge just created. repeated_nodes.extend(targets) # And the new node "source" has m edges to add to the list. repeated_nodes.extend([source] * m) source += 1 return G
def to_scipy_sparse_array(G, nodelist=None, dtype=None, weight="weight", format="csr"): import scipy as sp import scipy.sparse # call as sp.sparse if len(G) == 0: raise nx.NetworkXError("Graph has no nodes or edges") if nodelist is None: nodelist = sorted(G) nlen = len(G) else: nlen = len(nodelist) if nlen == 0: raise nx.NetworkXError("nodelist has no nodes") nodeset = set(G.nbunch_iter(nodelist)) if nlen != len(nodeset): for n in nodelist: if n not in G: raise nx.NetworkXError(f"Node {n} in nodelist is not in G") raise nx.NetworkXError("nodelist contains duplicates.") if nlen < len(G): G = G.subgraph(nodelist) index = dict(zip(nodelist, range(nlen))) coefficients = zip( *((index[u], index[v], wt) for u, v, wt in G.edges(data=weight, default=1)) ) try: row, col, data = coefficients except ValueError: # there is no edge in the subgraph row, col, data = [], [], [] if G.is_directed(): A = sp.sparse.coo_array((data, (row, col)), shape=(nlen, nlen), dtype=dtype) else: # symmetrize matrix d = data + data r = row + col c = col + row # selfloop entries get double counted when symmetrizing # so we subtract the data on the diagonal selfloops = list(nx.selfloop_edges(G, data=weight, default=1)) if selfloops: diag_index, diag_data = zip(*((index[u], -wt) for u, v, wt in selfloops)) d += diag_data r += diag_index c += diag_index A = sp.sparse.coo_array((d, (r, c)), shape=(nlen, nlen), dtype=dtype) try: return A.asformat(format) except ValueError as err: raise nx.NetworkXError(f"Unknown sparse matrix format: {format}") from err
def union(G, H, rename=(None, None), name=None): if not G.is_multigraph() == H.is_multigraph(): raise nx.NetworkXError("G and H must both be graphs or multigraphs.") # Union is the same type as G R = G.__class__() # add graph attributes, H attributes take precedent over G attributes R.graph.update(G.graph) R.graph.update(H.graph) # rename graph to obtain disjoint node labels def add_prefix(graph, prefix): if prefix is None: return graph def label(x): if isinstance(x, str): name = prefix + x else: name = prefix + repr(x) return name return nx.relabel_nodes(graph, label) G = add_prefix(G, rename[0]) H = add_prefix(H, rename[1]) if set(G) & set(H): raise nx.NetworkXError( "The node sets of G and H are not disjoint.", "Use appropriate rename=(Gprefix,Hprefix)" "or use disjoint_union(G,H).", ) if G.is_multigraph(): G_edges = G.edges(keys=True, data=True) else: G_edges = G.edges(data=True) if H.is_multigraph(): H_edges = H.edges(keys=True, data=True) else: H_edges = H.edges(data=True) # add nodes R.add_nodes_from(G) R.add_nodes_from(H) # add edges R.add_edges_from(G_edges) R.add_edges_from(H_edges) # add node attributes for n in G: R.nodes[n].update(G.nodes[n]) for n in H: R.nodes[n].update(H.nodes[n]) return R
def random_powerlaw_tree_sequence(n, gamma=3, seed=None, tries=100): # get trial sequence z = powerlaw_sequence(n, exponent=gamma, seed=seed) # round to integer values in the range [0,n] zseq = [min(n, max(int(round(s)), 0)) for s in z] # another sequence to swap values from z = powerlaw_sequence(tries, exponent=gamma, seed=seed) # round to integer values in the range [0,n] swap = [min(n, max(int(round(s)), 0)) for s in z] for deg in swap: # If this degree sequence can be the degree sequence of a tree, return # it. It can be a tree if the number of edges is one fewer than the # number of nodes, or in other words, `n - sum(zseq) / 2 == 1`. We # use an equivalent condition below that avoids floating point # operations. if 2 * n - sum(zseq) == 2: return zseq index = seed.randint(0, n - 1) zseq[index] = swap.pop() raise nx.NetworkXError( "Exceeded max (%d) attempts for a valid tree" " sequence." % tries )
def connected_watts_strogatz_graph(n, k, p, tries=100, seed=None): for i in range(tries): # seed is an RNG so should change sequence each call G = watts_strogatz_graph(n, k, p, seed) if nx.is_connected(G): return G raise nx.NetworkXError("Maximum number of tries exceeded")
def gn_graph(n, kernel=None, create_using=None, seed=None): G = empty_graph(1, create_using, default=nx.DiGraph) if not G.is_directed(): raise nx.NetworkXError("create_using must indicate a Directed Graph") if kernel is None: def kernel(x): return x if n == 1: return G G.add_edge(1, 0) # get started ds = [1, 1] # degree sequence for source in range(2, n): # compute distribution from kernel and degree dist = [kernel(d) for d in ds] # choose target from discrete distribution target = discrete_sequence(1, distribution=dist, seed=seed)[0] G.add_edge(source, target) ds.append(1) # the source has only one link (degree one) ds[target] += 1 # add one to the target link degree return G
def watts_strogatz_graph(n, k, p, seed=None): if k > n: raise nx.NetworkXError("k>n, choose smaller k or larger n") # If k == n, the graph is complete not Watts-Strogatz if k == n: return nx.complete_graph(n) G = nx.Graph() nodes = list(range(n)) # nodes are labeled 0 to n-1 # connect each node to k/2 neighbors for j in range(1, k // 2 + 1): targets = nodes[j:] + nodes[0:j] # first j nodes are now last in list G.add_edges_from(zip(nodes, targets)) # rewire edges from each node # loop over all nodes in order (label) and neighbors in order (distance) # no self loops or multiple edges allowed for j in range(1, k // 2 + 1): # outer loop is neighbors targets = nodes[j:] + nodes[0:j] # first j nodes are now last in list # inner loop in node order for u, v in zip(nodes, targets): if seed.random() < p: w = seed.choice(nodes) # Enforce no self-loops or multiple edges while w == u or G.has_edge(u, w): w = seed.choice(nodes) if G.degree(u) >= n - 1: break # skip this rewiring else: G.remove_edge(u, v) G.add_edge(u, w) return G
def newman_watts_strogatz_graph(n, k, p, seed=None): if k > n: raise nx.NetworkXError("k>=n, choose smaller k or larger n") # If k == n the graph return is a complete graph if k == n: return nx.complete_graph(n) G = empty_graph(n) nlist = list(G.nodes()) fromv = nlist # connect the k/2 neighbors for j in range(1, k // 2 + 1): tov = fromv[j:] + fromv[0:j] # the first j are now last for i, value in enumerate(fromv): G.add_edge(value, tov[i]) # for each edge u-v, with probability p, randomly select existing # node w and add new edge u-w e = list(G.edges()) for (u, v) in e: if seed.random() < p: w = seed.choice(nlist) # no self-loops and reject if edge u-w exists # is that the correct NWS model? while w == u or G.has_edge(u, w): w = seed.choice(nlist) if G.degree(u) >= n - 1: break # skip this rewiring else: G.add_edge(u, w) return G
def convert_node_labels_to_integers(G, first_label=0, ordering="default", label_attribute=None): N = G.number_of_nodes() + first_label if ordering == "default": mapping = dict(zip(G.nodes(), range(first_label, N))) elif ordering == "sorted": nlist = sorted(G.nodes()) mapping = dict(zip(nlist, range(first_label, N))) elif ordering == "increasing degree": dv_pairs = [(d, n) for (n, d) in G.degree()] dv_pairs.sort() # in-place sort from lowest to highest degree mapping = dict(zip([n for d, n in dv_pairs], range(first_label, N))) elif ordering == "decreasing degree": dv_pairs = [(d, n) for (n, d) in G.degree()] dv_pairs.sort() # in-place sort from lowest to highest degree dv_pairs.reverse() mapping = dict(zip([n for d, n in dv_pairs], range(first_label, N))) else: raise nx.NetworkXError(f"Unknown node ordering: {ordering}") H = relabel_nodes(G, mapping) # create node attribute with the old label if label_attribute is not None: nx.set_node_attributes(H, {v: k for k, v in mapping.items()}, label_attribute) return H
def powerlaw_cluster_graph(n, m, p, seed=None): if m < 1 or n < m: raise nx.NetworkXError( "NetworkXError must have m>1 and m<n, m=%d,n=%d" % (m, n) ) if p > 1 or p < 0: raise nx.NetworkXError("NetworkXError p must be in [0,1], p=%f" % (p)) G = empty_graph(m) # add m initial nodes (m0 in barabasi-speak) repeated_nodes = list(G.nodes()) # list of existing nodes to sample from # with nodes repeated once for each adjacent edge source = m # next node is m while source < n: # Now add the other n-1 nodes possible_targets = _random_subset(repeated_nodes, m, seed) # do one preferential attachment for new node target = possible_targets.pop() G.add_edge(source, target) repeated_nodes.append(target) # add one node to list for each new link count = 1 while count < m: # add m-1 more new links if seed.random() < p: # clustering step: add triangle neighborhood = [ nbr for nbr in G.neighbors(target) if not G.has_edge(source, nbr) and not nbr == source ] if neighborhood: # if there is a neighbor without a link nbr = seed.choice(neighborhood) G.add_edge(source, nbr) # add triangle repeated_nodes.append(nbr) count = count + 1 continue # go to top of while loop # else do preferential attachment step if above fails target = possible_targets.pop() G.add_edge(source, target) repeated_nodes.append(target) count = count + 1 repeated_nodes.extend([source] * m) # add source node to list m times source += 1 return G
def _init_product_graph(G, H): if not G.is_directed() == H.is_directed(): msg = "G and H must be both directed or both undirected" raise nx.NetworkXError(msg) if G.is_multigraph() or H.is_multigraph(): GH = nx.MultiGraph() else: GH = nx.Graph() if G.is_directed(): GH = GH.to_directed() return GH
def katz_centrality( G, alpha=0.1, beta=1.0, max_iter=100, tol=1e-06, nstart=None, normalized=True, weight=None, ): # TODO(@weibin): raise PowerIterationFailedConvergence if katz fails to converge # within the specified number of iterations. @context_to_dict @project_to_simple def _katz_centrality( G, alpha=0.1, beta=1.0, max_iter=100, tol=1e-06, normalized=True, weight=None, ): return graphscope.katz_centrality( G, alpha=alpha, beta=beta, tolerance=tol, max_round=max_iter, normalized=normalized, ) if nstart is not None or isinstance(beta, dict): # forward the nxa.katz_centrality return nxa.katz_centrality(G, alpha, beta, max_iter, tol, nstart, normalized, weight) if len(G) == 0: return {} if not isinstance(beta, (int, float)): raise nx.NetworkXError("beta should be number, not {}".format( type(beta))) if max_iter == 0: raise nx.PowerIterationFailedConvergence(max_iter) return _katz_centrality( G, alpha=alpha, beta=beta, tol=tol, max_iter=max_iter, normalized=normalized, weight=weight, )
def gnc_graph(n, create_using=None, seed=None): G = empty_graph(1, create_using, default=nx.DiGraph) if not G.is_directed(): raise nx.NetworkXError("create_using must indicate a Directed Graph") if n == 1: return G for source in range(1, n): target = seed.randrange(0, source) for succ in G.successors(target): G.add_edge(source, succ) G.add_edge(source, target) return G
def random_regular_graph(d, n, seed=None): if (n * d) % 2 != 0: raise nx.NetworkXError("n * d must be even") if not 0 <= d < n: raise nx.NetworkXError("the 0 <= d < n inequality must be satisfied") if d == 0: return empty_graph(n) def _suitable(edges, potential_edges): # Helper subroutine to check if there are suitable edges remaining # If False, the generation of the graph has failed if not potential_edges: return True for s1 in potential_edges: for s2 in potential_edges: # Two iterators on the same dictionary are guaranteed # to visit it in the same order if there are no # intervening modifications. if s1 == s2: # Only need to consider s1-s2 pair one time break if s1 > s2: s1, s2 = s2, s1 if (s1, s2) not in edges: return True return False def _try_creation(): # Attempt to create an edge set edges = set() stubs = list(range(n)) * d while stubs: potential_edges = defaultdict(lambda: 0) seed.shuffle(stubs) stubiter = iter(stubs) for s1, s2 in zip(stubiter, stubiter): if s1 > s2: s1, s2 = s2, s1 if s1 != s2 and ((s1, s2) not in edges): edges.add((s1, s2)) else: potential_edges[s1] += 1 potential_edges[s2] += 1 if not _suitable(edges, potential_edges): return None # failed to find suitable edge set stubs = [ node for node, potential in potential_edges.items() for _ in range(potential) ] return edges # Even though a suitable edge set exists, # the generation of such a set is not guaranteed. # Try repeatedly to find one. edges = _try_creation() while edges is None: edges = _try_creation() G = nx.Graph() G.add_edges_from(edges) return G
def extended_barabasi_albert_graph(n, m, p, q, seed=None): if m < 1 or m >= n: msg = "Extended Barabasi-Albert network needs m>=1 and m<n, m=%d, n=%d" raise nx.NetworkXError(msg % (m, n)) if p + q >= 1: msg = "Extended Barabasi-Albert network needs p + q <= 1, p=%d, q=%d" raise nx.NetworkXError(msg % (p, q)) # Add m initial nodes (m0 in barabasi-speak) G = empty_graph(m) # List of nodes to represent the preferential attachment random selection. # At the creation of the graph, all nodes are added to the list # so that even nodes that are not connected have a chance to get selected, # for rewiring and adding of edges. # With each new edge, nodes at the ends of the edge are added to the list. attachment_preference = [] attachment_preference.extend(range(m)) # Start adding the other n-m nodes. The first node is m. new_node = m while new_node < n: a_probability = seed.random() # Total number of edges of a Clique of all the nodes clique_degree = len(G) - 1 clique_size = (len(G) * clique_degree) / 2 # Adding m new edges, if there is room to add them if a_probability < p and G.size() <= clique_size - m: # Select the nodes where an edge can be added elligible_nodes = [nd for nd, deg in G.degree() if deg < clique_degree] for i in range(m): # Choosing a random source node from elligible_nodes src_node = seed.choice(elligible_nodes) # Picking a possible node that is not 'src_node' or # neighbor with 'src_node', with preferential attachment prohibited_nodes = list(G[src_node]) prohibited_nodes.append(src_node) # This will raise an exception if the sequence is empty dest_node = seed.choice( [nd for nd in attachment_preference if nd not in prohibited_nodes] ) # Adding the new edge G.add_edge(src_node, dest_node) # Appending both nodes to add to their preferential attachment attachment_preference.append(src_node) attachment_preference.append(dest_node) # Adjusting the elligible nodes. Degree may be saturated. if G.degree(src_node) == clique_degree: elligible_nodes.remove(src_node) if ( G.degree(dest_node) == clique_degree and dest_node in elligible_nodes ): elligible_nodes.remove(dest_node) # Rewiring m edges, if there are enough edges elif p <= a_probability < (p + q) and m <= G.size() < clique_size: # Selecting nodes that have at least 1 edge but that are not # fully connected to ALL other nodes (center of star). # These nodes are the pivot nodes of the edges to rewire elligible_nodes = [nd for nd, deg in G.degree() if 0 < deg < clique_degree] for i in range(m): # Choosing a random source node node = seed.choice(elligible_nodes) # The available nodes do have a neighbor at least. neighbor_nodes = list(G[node]) # Choosing the other end that will get dettached src_node = seed.choice(neighbor_nodes) # Picking a target node that is not 'node' or # neighbor with 'node', with preferential attachment neighbor_nodes.append(node) dest_node = seed.choice( [nd for nd in attachment_preference if nd not in neighbor_nodes] ) # Rewire G.remove_edge(node, src_node) G.add_edge(node, dest_node) # Adjusting the preferential attachment list attachment_preference.remove(src_node) attachment_preference.append(dest_node) # Adjusting the elligible nodes. # nodes may be saturated or isolated. if G.degree(src_node) == 0 and src_node in elligible_nodes: elligible_nodes.remove(src_node) if dest_node in elligible_nodes: if G.degree(dest_node) == clique_degree: elligible_nodes.remove(dest_node) else: if G.degree(dest_node) == 1: elligible_nodes.append(dest_node) # Adding new node with m edges else: # Select the edges' nodes by preferential attachment targets = _random_subset(attachment_preference, m, seed) G.add_edges_from(zip([new_node] * m, targets)) # Add one node to the list for each new edge just created. attachment_preference.extend(targets) # The new node has m edges to it, plus itself: m + 1 attachment_preference.extend([new_node] * (m + 1)) new_node += 1 return G
def scale_free_graph( n, alpha=0.41, beta=0.54, gamma=0.05, delta_in=0.2, delta_out=0, create_using=None, seed=None, ): def _choose_node(G, distribution, delta, psum): cumsum = 0.0 # normalization r = seed.random() for n, d in distribution: cumsum += (d + delta) / psum if r < cumsum: break return n if create_using is None or not hasattr(create_using, "_adj"): # start with 3-cycle G = nx.empty_graph(3, create_using, default=nx.MultiDiGraph) G.add_edges_from([(0, 1), (1, 2), (2, 0)]) else: G = create_using if not (G.is_directed() and G.is_multigraph()): raise nx.NetworkXError("MultiDiGraph required in create_using") if alpha <= 0: raise ValueError("alpha must be > 0.") if beta <= 0: raise ValueError("beta must be > 0.") if gamma <= 0: raise ValueError("gamma must be > 0.") if abs(alpha + beta + gamma - 1.0) >= 1e-9: raise ValueError("alpha+beta+gamma must equal 1.") number_of_edges = G.number_of_edges() while len(G) < n: psum_in = number_of_edges + delta_in * len(G) psum_out = number_of_edges + delta_out * len(G) r = seed.random() # random choice in alpha,beta,gamma ranges if r < alpha: # alpha # add new node v v = len(G) # choose w according to in-degree and delta_in w = _choose_node(G, G.in_degree(), delta_in, psum_in) elif r < alpha + beta: # beta # choose v according to out-degree and delta_out v = _choose_node(G, G.out_degree(), delta_out, psum_out) # choose w according to in-degree and delta_in w = _choose_node(G, G.in_degree(), delta_in, psum_in) else: # gamma # choose v according to out-degree and delta_out v = _choose_node(G, G.out_degree(), delta_out, psum_out) # add new node w w = len(G) G.add_edge(v, w) number_of_edges += 1 return G
def to_numpy_array( G, nodelist=None, dtype=None, order=None, multigraph_weight=sum, weight="weight", nonedge=0.0, ): import numpy as np if nodelist is None: nodelist = list(G) nodeset = G nlen = len(G) else: nlen = len(nodelist) nodeset = set(G.nbunch_iter(nodelist)) if nlen != len(nodeset): for n in nodelist: if n not in G: raise nx.NetworkXError(f"Node {n} in nodelist is not in G") raise nx.NetworkXError("nodelist contains duplicates.") A = np.full((nlen, nlen), fill_value=nonedge, dtype=dtype, order=order) # Corner cases: empty nodelist or graph without any edges if nlen == 0 or G.number_of_edges() == 0: return A # If dtype is structured and weight is None, use dtype field names as # edge attributes edge_attrs = None # Only single edge attribute by default if A.dtype.names: if weight is None: edge_attrs = dtype.names else: raise ValueError( "Specifying `weight` not supported for structured dtypes\n." "To create adjacency matrices from structured dtypes, use `weight=None`." ) idx = dict(zip(sorted(nodelist), range(nlen))) if len(nodelist) < len(G): G = G.subgraph(nodelist) # A real subgraph, not view # Collect all edge weights and reduce with `multigraph_weights` if G.is_multigraph(): if edge_attrs: raise nx.NetworkXError( "Structured arrays are not supported for MultiGraphs" ) d = defaultdict(list) for u, v, wt in G.edges(data=weight, default=1.0): d[(idx[u], idx[v])].append(wt) i, j = np.array(list(d.keys())).T # indices wts = [multigraph_weight(ws) for ws in d.values()] # reduced weights else: i, j, wts = [], [], [] # Special branch: multi-attr adjacency from structured dtypes if edge_attrs: # Extract edges with all data for u, v, data in G.edges(data=True): i.append(idx[u]) j.append(idx[v]) wts.append(data) # Map each attribute to the appropriate named field in the # structured dtype for attr in edge_attrs: attr_data = [wt.get(attr, 1.0) for wt in wts] A[attr][i, j] = attr_data if not G.is_directed(): A[attr][j, i] = attr_data return A for u, v, wt in G.edges(data=weight, default=1.0): i.append(idx[u]) j.append(idx[v]) wts.append(wt) # Set array values with advanced indexing A[i, j] = wts if not G.is_directed(): A[j, i] = wts return A
def to_networkx_graph(data, create_using=None, multigraph_input=False): # noqa: C901 # graphscope graph if isinstance(data, graphscope.Graph): if create_using is None: raise nx.NetworkXError( "Use None to convert graphscope graph to networkx graph.") # check session and direction compatible if data.session_id != create_using.session_id: raise nx.NetworkXError( "The source graph is not loaded in session {}." % create_using.session_id) if data.is_directed() != create_using.is_directed(): if data.is_directed(): msg = "The source graph is a directed graph, can't be used to init nx.Graph. You may use nx.DiGraph" else: msg = "The source graph is a undirected graph, can't be used to init nx.DiGraph. You may use nx.Graph" raise nx.NetworkXError(msg) create_using._key = data.key create_using._schema = data.schema create_using._op = data.op if create_using._default_label is not None: try: create_using._default_label_id = ( create_using._schema.get_vertex_label_id( create_using._default_label)) except KeyError: raise nx.NetworkXError( "default label {} not existed in graph." % create_using._default_label) create_using._graph_type = data.graph_type return # networkx graph or graphscope.nx graph if hasattr(data, "adj"): try: result = nx.from_dict_of_dicts( data.adj, create_using=create_using, multigraph_input=data.is_multigraph(), ) if hasattr(data, "graph"): # data.graph should be dict-like result.graph.update(data.graph) if hasattr(data, "nodes"): # data.nodes should be dict-like result.add_nodes_from(data.nodes.items()) return result except Exception as err: raise nx.NetworkXError( "Input is not a correct NetworkX-like graph.") from err # dict of dicts/lists if isinstance(data, dict): try: return nx.from_dict_of_dicts(data, create_using=create_using, multigraph_input=multigraph_input) except Exception as err: if multigraph_input is True: raise nx.NetworkXError( f"converting multigraph_input raised:\n{type(err)}: {err}") try: return nx.from_dict_of_lists(data, create_using=create_using) except Exception as err: raise TypeError("Input is not known type.") from err # Pandas DataFrame try: import pandas as pd if isinstance(data, pd.DataFrame): if data.shape[0] == data.shape[1]: try: return nx.from_pandas_adjacency(data, create_using=create_using) except Exception as err: msg = "Input is not a correct Pandas DataFrame adjacency matrix." raise nx.NetworkXError(msg) from err else: try: return nx.from_pandas_edgelist(data, edge_attr=True, create_using=create_using) except Exception as err: msg = "Input is not a correct Pandas DataFrame edge-list." raise nx.NetworkXError(msg) from err except ImportError: msg = "pandas not found, skipping conversion test." warnings.warn(msg, ImportWarning) # numpy matrix or ndarray try: import numpy if isinstance(data, (numpy.matrix, numpy.ndarray)): try: return nx.from_numpy_matrix(data, create_using=create_using) except Exception as err: raise nx.NetworkXError( "Input is not a correct numpy matrix or array.") from err except ImportError: warnings.warn("numpy not found, skipping conversion test.", ImportWarning) # scipy sparse matrix - any format try: import scipy if hasattr(data, "format"): try: return nx.from_scipy_sparse_matrix(data, create_using=create_using) except Exception as err: raise nx.NetworkXError( "Input is not a correct scipy sparse matrix type." ) from err except ImportError: warnings.warn("scipy not found, skipping conversion test.", ImportWarning) # Note: most general check - should remain last in order of execution # Includes containers (e.g. list, set, dict, etc.), generators, and # iterators (e.g. itertools.chain) of edges if isinstance(data, (Collection, Generator, Iterator)): try: return nx.from_edgelist(data, create_using=create_using) except Exception as err: raise nx.NetworkXError("Input is not a valid edge list") from err raise nx.NetworkXError("Input is not a known data type for conversion.")
def to_networkx_graph(data, create_using=None, multigraph_input=False): # noqa: C901 """Make a graph from a known data structure. The preferred way to call this is automatically from the class constructor >>> d = {0: {1: {'weight':1}}} # dict-of-dicts single edge (0,1) >>> G = nx.Graph(d) instead of the equivalent >>> G = nx.from_dict_of_dicts(d) Parameters ---------- data : object to be converted Current known types are: any NetworkX graph dict-of-dicts dict-of-lists container (ie set, list, tuple, iterator) of edges Pandas DataFrame (row per edge) numpy matrix numpy ndarray scipy sparse matrix create_using : nx graph constructor, optional (default=nx.Graph) Graph type to create. If graph instance, then cleared before populated. multigraph_input : bool (default False) If True and data is a dict_of_dicts, try to create a multigraph assuming dict_of_dict_of_lists. If data and create_using are both multigraphs then create a multigraph from a multigraph. """ # networkx graph or graphscope.nx graph if hasattr(data, "adj"): try: result = from_dict_of_dicts( data.adj, create_using=create_using, multigraph_input=data.is_multigraph(), ) if hasattr(data, "graph"): # data.graph should be dict-like result.graph.update(data.graph) if hasattr(data, "nodes"): # data.nodes should be dict-like result.add_nodes_from(data.nodes.items()) return result except Exception as e: raise nx.NetworkXError( "Input is not a correct NetworkX-like graph.") from e # dict of dicts/lists if isinstance(data, dict): try: return from_dict_of_dicts(data, create_using=create_using, multigraph_input=multigraph_input) except Exception: try: return from_dict_of_lists(data, create_using=create_using) except Exception as e: raise TypeError("Input is not known type.") from e # list or generator of edges if isinstance(data, (list, tuple)) or any( hasattr(data, attr) for attr in ["_adjdict", "next", "__next__"]): try: return from_edgelist(data, create_using=create_using) except Exception as e: raise nx.NetworkXError("Input is not a valid edge list") from e # Pandas DataFrame try: import pandas as pd if isinstance(data, pd.DataFrame): if data.shape[0] == data.shape[1]: try: return nx.from_pandas_adjacency(data, create_using=create_using) except Exception as e: msg = "Input is not a correct Pandas DataFrame adjacency matrix." raise nx.NetworkXError(msg) from e else: try: return nx.from_pandas_edgelist(data, edge_attr=True, create_using=create_using) except Exception as e: msg = "Input is not a correct Pandas DataFrame edge-list." raise nx.NetworkXError(msg) from e except ImportError: msg = "pandas not found, skipping conversion test." warnings.warn(msg, ImportWarning) # numpy matrix or ndarray try: import numpy if isinstance(data, (numpy.matrix, numpy.ndarray)): try: return nx.from_numpy_matrix(data, create_using=create_using) except Exception as e: raise nx.NetworkXError( "Input is not a correct numpy matrix or array.") from e except ImportError: warnings.warn("numpy not found, skipping conversion test.", ImportWarning) # scipy sparse matrix - any format try: import scipy if hasattr(data, "format"): try: return nx.from_scipy_sparse_matrix(data, create_using=create_using) except Exception as e: raise nx.NetworkXError( "Input is not a correct scipy sparse matrix type.") from e except ImportError: warnings.warn("scipy not found, skipping conversion test.", ImportWarning) raise nx.NetworkXError("Input is not a known data type for conversion.")
def from_pandas_edgelist( df, source="source", target="target", edge_attr=None, create_using=None, edge_key=None, ): g = nx.empty_graph(0, create_using) if edge_attr is None: g.add_edges_from(zip(df[source], df[target])) return g reserved_columns = [source, target] # Additional columns requested attr_col_headings = [] attribute_data = [] if edge_attr is True: attr_col_headings = [ c for c in df.columns if c not in reserved_columns ] elif isinstance(edge_attr, (list, tuple)): attr_col_headings = edge_attr else: attr_col_headings = [edge_attr] if len(attr_col_headings) == 0: raise nx.NetworkXError( f"Invalid edge_attr argument: No columns found with name: {attr_col_headings}" ) try: attribute_data = zip(*[df[col] for col in attr_col_headings]) except (KeyError, TypeError) as e: msg = f"Invalid edge_attr argument: {edge_attr}" raise nx.NetworkXError(msg) from e if g.is_multigraph(): # => append the edge keys from the df to the bundled data if edge_key is not None: try: multigraph_edge_keys = df[edge_key] attribute_data = zip(attribute_data, multigraph_edge_keys) except (KeyError, TypeError) as e: msg = f"Invalid edge_key argument: {edge_key}" raise nx.NetworkXError(msg) from e for s, t, attrs in zip(df[source], df[target], attribute_data): if edge_key is not None: attrs, multigraph_edge_key = attrs key = g.add_edge(s, t, key=multigraph_edge_key) else: key = g.add_edge(s, t) g[s][t][key].update(zip(attr_col_headings, attrs)) else: edges = [] for s, t, attrs in zip(df[source], df[target], attribute_data): edges.append((s, t, zip(attr_col_headings, attrs))) g.add_edges_from(edges) return g