2025-01-22 16:54:15 +01:00

1845 lines
81 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
<meta name="generator" content="pdoc3 0.11.5">
<title>lang_main.analysis.graphs API documentation</title>
<meta name="description" content="">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/sanitize.min.css" integrity="sha512-y1dtMcuvtTMJc1yPgEqF0ZjQbhnc/bFhyvIyVNb9Zk5mIGtqVaAB1Ttl28su8AvFMOY0EwRbAe+HCLqj6W7/KA==" crossorigin>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/10up-sanitize.css/13.0.0/typography.min.css" integrity="sha512-Y1DYSb995BAfxobCkKepB1BqJJTPrOp3zPL74AWFugHHmmdcvO+C48WLrUOlhGMc0QG7AE3f7gmvvcrmX2fDoA==" crossorigin>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css" crossorigin>
<style>:root{--highlight-color:#fe9}.flex{display:flex !important}body{line-height:1.5em}#content{padding:20px}#sidebar{padding:1.5em;overflow:hidden}#sidebar > *:last-child{margin-bottom:2cm}.http-server-breadcrumbs{font-size:130%;margin:0 0 15px 0}#footer{font-size:.75em;padding:5px 30px;border-top:1px solid #ddd;text-align:right}#footer p{margin:0 0 0 1em;display:inline-block}#footer p:last-child{margin-right:30px}h1,h2,h3,h4,h5{font-weight:300}h1{font-size:2.5em;line-height:1.1em}h2{font-size:1.75em;margin:2em 0 .50em 0}h3{font-size:1.4em;margin:1.6em 0 .7em 0}h4{margin:0;font-size:105%}h1:target,h2:target,h3:target,h4:target,h5:target,h6:target{background:var(--highlight-color);padding:.2em 0}a{color:#058;text-decoration:none;transition:color .2s ease-in-out}a:visited{color:#503}a:hover{color:#b62}.title code{font-weight:bold}h2[id^="header-"]{margin-top:2em}.ident{color:#900;font-weight:bold}pre code{font-size:.8em;line-height:1.4em;padding:1em;display:block}code{background:#f3f3f3;font-family:"DejaVu Sans Mono",monospace;padding:1px 4px;overflow-wrap:break-word}h1 code{background:transparent}pre{border-top:1px solid #ccc;border-bottom:1px solid #ccc;margin:1em 0}#http-server-module-list{display:flex;flex-flow:column}#http-server-module-list div{display:flex}#http-server-module-list dt{min-width:10%}#http-server-module-list p{margin-top:0}.toc ul,#index{list-style-type:none;margin:0;padding:0}#index code{background:transparent}#index h3{border-bottom:1px solid #ddd}#index ul{padding:0}#index h4{margin-top:.6em;font-weight:bold}@media (min-width:200ex){#index .two-column{column-count:2}}@media (min-width:300ex){#index .two-column{column-count:3}}dl{margin-bottom:2em}dl dl:last-child{margin-bottom:4em}dd{margin:0 0 1em 3em}#header-classes + dl > dd{margin-bottom:3em}dd dd{margin-left:2em}dd p{margin:10px 0}.name{background:#eee;font-size:.85em;padding:5px 10px;display:inline-block;min-width:40%}.name:hover{background:#e0e0e0}dt:target .name{background:var(--highlight-color)}.name > span:first-child{white-space:nowrap}.name.class > span:nth-child(2){margin-left:.4em}.inherited{color:#999;border-left:5px solid #eee;padding-left:1em}.inheritance em{font-style:normal;font-weight:bold}.desc h2{font-weight:400;font-size:1.25em}.desc h3{font-size:1em}.desc dt code{background:inherit}.source > summary,.git-link-div{color:#666;text-align:right;font-weight:400;font-size:.8em;text-transform:uppercase}.source summary > *{white-space:nowrap;cursor:pointer}.git-link{color:inherit;margin-left:1em}.source pre{max-height:500px;overflow:auto;margin:0}.source pre code{font-size:12px;overflow:visible;min-width:max-content}.hlist{list-style:none}.hlist li{display:inline}.hlist li:after{content:',\2002'}.hlist li:last-child:after{content:none}.hlist .hlist{display:inline;padding-left:1em}img{max-width:100%}td{padding:0 .5em}.admonition{padding:.1em 1em;margin:1em 0}.admonition-title{font-weight:bold}.admonition.note,.admonition.info,.admonition.important{background:#aef}.admonition.todo,.admonition.versionadded,.admonition.tip,.admonition.hint{background:#dfd}.admonition.warning,.admonition.versionchanged,.admonition.deprecated{background:#fd4}.admonition.error,.admonition.danger,.admonition.caution{background:lightpink}</style>
<style media="screen and (min-width: 700px)">@media screen and (min-width:700px){#sidebar{width:30%;height:100vh;overflow:auto;position:sticky;top:0}#content{width:70%;max-width:100ch;padding:3em 4em;border-left:1px solid #ddd}pre code{font-size:1em}.name{font-size:1em}main{display:flex;flex-direction:row-reverse;justify-content:flex-end}.toc ul ul,#index ul ul{padding-left:1em}.toc > ul > li{margin-top:.5em}}</style>
<style media="print">@media print{#sidebar h1{page-break-before:always}.source{display:none}}@media print{*{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a[href]:after{content:" (" attr(href) ")";font-size:90%}a[href][title]:after{content:none}abbr[title]:after{content:" (" attr(title) ")"}.ir a:after,a[href^="javascript:"]:after,a[href^="#"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}@page{margin:0.5cm}p,h2,h3{orphans:3;widows:3}h1,h2,h3,h4,h5,h6{page-break-after:avoid}}</style>
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js" integrity="sha512-D9gUyxqja7hBtkWpPWGt9wfbfaMGVt9gnyCvYa+jojwwPHLCzUm5i8rpk7vD7wNee9bA35eYIjobYPaQuKS1MQ==" crossorigin></script>
<script>window.addEventListener('DOMContentLoaded', () => {
hljs.configure({languages: ['bash', 'css', 'diff', 'graphql', 'ini', 'javascript', 'json', 'plaintext', 'python', 'python-repl', 'rust', 'shell', 'sql', 'typescript', 'xml', 'yaml']});
hljs.highlightAll();
/* Collapse source docstrings */
setTimeout(() => {
[...document.querySelectorAll('.hljs.language-python > .hljs-string')]
.filter(el => el.innerHTML.length > 200 && ['"""', "'''"].includes(el.innerHTML.substring(0, 3)))
.forEach(el => {
let d = document.createElement('details');
d.classList.add('hljs-string');
d.innerHTML = '<summary>"""</summary>' + el.innerHTML.substring(3);
el.replaceWith(d);
});
}, 100);
})</script>
</head>
<body>
<main>
<article id="content">
<header>
<h1 class="title">Module <code>lang_main.analysis.graphs</code></h1>
</header>
<section id="section-intro">
</section>
<section>
</section>
<section>
</section>
<section>
<h2 class="section-title" id="header-functions">Functions</h2>
<dl>
<dt id="lang_main.analysis.graphs.add_betweenness_centrality"><code class="name flex">
<span>def <span class="ident">add_betweenness_centrality</span></span>(<span>graph: DiGraph | Graph,<br>edge_weight_property: str | None = None,<br>property_name: str = 'betweenness_centrality') > None</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def add_betweenness_centrality(
graph: DiGraph | Graph,
edge_weight_property: str | None = None,
property_name: str = PROPERTY_NAME_BETWEENNESS_CENTRALITY,
) -&gt; None:
&#34;&#34;&#34;adds the betweenness centrality as property to each node of the given graph
Operation is performed inplace.
Parameters
----------
graph : DiGraph | Graph
Graph with betweenness centrality as node property added inplace
edge_weight_property : str | None, optional
property of the edges which contains the weight information,
not necessarily needed, by default &#39;None&#39;
property_name : str, optional
target name for the property containing the betweenness centrality in nodes,
by default PROPERTY_NAME_BETWEENNESS_CENTRALITY
&#34;&#34;&#34;
node_property_mapping = cast(
dict[str, float],
nx.betweenness_centrality(graph, normalized=True, weight=edge_weight_property), # type: ignore
)
nx.set_node_attributes(
graph,
node_property_mapping,
name=property_name,
)</code></pre>
</details>
<div class="desc"><p>adds the betweenness centrality as property to each node of the given graph
Operation is performed inplace.</p>
<h2 id="parameters">Parameters</h2>
<dl>
<dt><strong><code>graph</code></strong> :&ensp;<code>DiGraph | Graph</code></dt>
<dd>Graph with betweenness centrality as node property added inplace</dd>
<dt><strong><code>edge_weight_property</code></strong> :&ensp;<code>str | None</code>, optional</dt>
<dd>property of the edges which contains the weight information,
not necessarily needed, by default 'None'</dd>
<dt><strong><code>property_name</code></strong> :&ensp;<code>str</code>, optional</dt>
<dd>target name for the property containing the betweenness centrality in nodes,
by default PROPERTY_NAME_BETWEENNESS_CENTRALITY</dd>
</dl></div>
</dd>
<dt id="lang_main.analysis.graphs.add_importance_metric"><code class="name flex">
<span>def <span class="ident">add_importance_metric</span></span>(<span>graph: DiGraph | Graph,<br>property_name: str = 'importance',<br>property_name_weighted_degree: str = 'degree_weighted',<br>property_name_betweenness: str = 'betweenness_centrality') > None</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def add_importance_metric(
graph: DiGraph | Graph,
property_name: str = PROPERTY_NAME_IMPORTANCE,
property_name_weighted_degree: str = PROPERTY_NAME_DEGREE_WEIGHTED,
property_name_betweenness: str = PROPERTY_NAME_BETWEENNESS_CENTRALITY,
) -&gt; None:
&#34;&#34;&#34;Adds a custom importance metric as property to each node of the given graph.
Can be used to decide which nodes are of high importance and also to build node size
mappings.
Operation is performed inplace.
Parameters
----------
graph : DiGraph | Graph
Graph with weighted degree as node property added inplace
property_name : str, optional
target name for the property containing the weighted degree in nodes,
by default PROPERTY_NAME_DEGREE_WEIGHTED
property_name_betweenness : str, optional
target name for the property containing the betweenness centrality in nodes,
by default PROPERTY_NAME_BETWEENNESS_CENTRALITY
&#34;&#34;&#34;
# build mapping for importance metric
node_property_mapping: dict[str, float] = {}
for node in cast(Iterable[str], graph.nodes):
node_data = cast(dict[str, float], graph.nodes[node])
if property_name_weighted_degree not in node_data:
raise NodePropertyNotContainedError(
(
f&#39;Node data does not contain weighted degree &#39;
f&#39;with name {property_name_weighted_degree}.&#39;
)
)
elif property_name_betweenness not in node_data:
raise NodePropertyNotContainedError(
(
f&#39;Node data does not contain betweenness centrality &#39;
f&#39;with name {property_name_betweenness}.&#39;
)
)
prio = node_data[property_name_weighted_degree] * node_data[property_name_betweenness]
node_property_mapping[node] = prio
nx.set_node_attributes(
graph,
node_property_mapping,
name=property_name,
)</code></pre>
</details>
<div class="desc"><p>Adds a custom importance metric as property to each node of the given graph.
Can be used to decide which nodes are of high importance and also to build node size
mappings.
Operation is performed inplace.</p>
<h2 id="parameters">Parameters</h2>
<dl>
<dt><strong><code>graph</code></strong> :&ensp;<code>DiGraph | Graph</code></dt>
<dd>Graph with weighted degree as node property added inplace</dd>
<dt><strong><code>property_name</code></strong> :&ensp;<code>str</code>, optional</dt>
<dd>target name for the property containing the weighted degree in nodes,
by default PROPERTY_NAME_DEGREE_WEIGHTED</dd>
<dt><strong><code>property_name_betweenness</code></strong> :&ensp;<code>str</code>, optional</dt>
<dd>target name for the property containing the betweenness centrality in nodes,
by default PROPERTY_NAME_BETWEENNESS_CENTRALITY</dd>
</dl></div>
</dd>
<dt id="lang_main.analysis.graphs.add_weighted_degree"><code class="name flex">
<span>def <span class="ident">add_weighted_degree</span></span>(<span>graph: DiGraph | Graph,<br>edge_weight_property: str = 'weight',<br>property_name: str = 'degree_weighted') > None</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def add_weighted_degree(
graph: DiGraph | Graph,
edge_weight_property: str = &#39;weight&#39;,
property_name: str = PROPERTY_NAME_DEGREE_WEIGHTED,
) -&gt; None:
&#34;&#34;&#34;adds the weighted degree as property to each node of the given graph
Operation is performed inplace.
Parameters
----------
graph : DiGraph | Graph
Graph with weighted degree as node property added inplace
edge_weight_property : str, optional
property of the edges which contains the weight information, by default &#39;weight&#39;
property_name : str, optional
target name for the property containing the weighted degree in nodes,
by default PROPERTY_NAME_DEGREE_WEIGHTED
&#34;&#34;&#34;
node_property_mapping = cast(
dict[str, float],
dict(graph.degree(weight=edge_weight_property)), # type: ignore
)
nx.set_node_attributes(
graph,
node_property_mapping,
name=property_name,
)</code></pre>
</details>
<div class="desc"><p>adds the weighted degree as property to each node of the given graph
Operation is performed inplace.</p>
<h2 id="parameters">Parameters</h2>
<dl>
<dt><strong><code>graph</code></strong> :&ensp;<code>DiGraph | Graph</code></dt>
<dd>Graph with weighted degree as node property added inplace</dd>
<dt><strong><code>edge_weight_property</code></strong> :&ensp;<code>str</code>, optional</dt>
<dd>property of the edges which contains the weight information, by default 'weight'</dd>
<dt><strong><code>property_name</code></strong> :&ensp;<code>str</code>, optional</dt>
<dd>target name for the property containing the weighted degree in nodes,
by default PROPERTY_NAME_DEGREE_WEIGHTED</dd>
</dl></div>
</dd>
<dt id="lang_main.analysis.graphs.convert_graph_to_cytoscape"><code class="name flex">
<span>def <span class="ident">convert_graph_to_cytoscape</span></span>(<span>graph: Graph | DiGraph) > tuple[list[lang_main.types.CytoscapeData], lang_main.types.WeightData]</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def convert_graph_to_cytoscape(
graph: Graph | DiGraph,
) -&gt; tuple[list[CytoscapeData], WeightData]:
cyto_data: list[CytoscapeData] = []
# iterate over nodes
nodes = cast(Iterable[NodeTitle], graph.nodes)
for node in nodes:
node_data: CytoscapeData = {
&#39;data&#39;: {
&#39;id&#39;: node,
&#39;label&#39;: node,
}
}
cyto_data.append(node_data)
# iterate over edges
weights: set[int] = set()
edges = cast(
Iterable[
tuple[
NodeTitle,
NodeTitle,
EdgeWeight,
]
],
graph.edges.data(&#39;weight&#39;, default=1), # type: ignore
)
for source, target, weight in edges:
weights.add(weight)
edge_data: CytoscapeData = {
&#39;data&#39;: {
&#39;source&#39;: source,
&#39;target&#39;: target,
&#39;weight&#39;: weight,
}
}
cyto_data.append(edge_data)
# TODO: add internal behaviour (if edge added check for new min/max)
min_weight: int = 0
max_weight: int = 0
if weights:
min_weight = min(weights)
max_weight = max(weights)
weight_metadata: WeightData = {&#39;min&#39;: min_weight, &#39;max&#39;: max_weight}
return cyto_data, weight_metadata</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="lang_main.analysis.graphs.convert_graph_to_undirected"><code class="name flex">
<span>def <span class="ident">convert_graph_to_undirected</span></span>(<span>graph: DiGraph, logging: bool = False, cast_int: bool = False) > networkx.classes.graph.Graph</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def convert_graph_to_undirected(
graph: DiGraph,
logging: bool = LOGGING_DEFAULT_GRAPHS,
cast_int: bool = False,
) -&gt; Graph:
dtype = np.float32
if cast_int:
dtype = np.uint32
# get adjacency matrix
adj_mat = typing.cast(DataFrame, nx.to_pandas_adjacency(G=graph, dtype=dtype))
arr = typing.cast(npt.NDArray[np.float32 | np.uint32], adj_mat.to_numpy())
if not cast_int:
arr = arr * (10**EDGE_WEIGHT_DECIMALS)
arr = np.round(arr, decimals=0)
arr = arr.astype(np.uint32)
# build undirected array: adding edges of lower triangular matrix to upper one
arr_upper = np.triu(arr)
arr_lower = np.tril(arr)
arr_lower = np.rot90(np.fliplr(arr_lower))
arr_new = arr_upper + arr_lower
if not cast_int:
arr_new = (arr_new / 10**EDGE_WEIGHT_DECIMALS).astype(np.float32)
arr_new = np.round(arr_new, decimals=EDGE_WEIGHT_DECIMALS)
# assign new data and create graph
adj_mat.loc[:] = arr_new # type: ignore
graph_undir = typing.cast(Graph, nx.from_pandas_adjacency(df=adj_mat))
# info about graph
if logging:
logger.info(&#39;Successfully converted graph to one with undirected edges.&#39;)
_ = get_graph_metadata(graph=graph_undir, logging=logging)
return graph_undir</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="lang_main.analysis.graphs.filter_graph_by_edge_weight"><code class="name flex">
<span>def <span class="ident">filter_graph_by_edge_weight</span></span>(<span>graph: <a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a>,<br>bound_lower: int | None,<br>bound_upper: int | None,<br>property: str = 'weight') > <a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def filter_graph_by_edge_weight(
graph: TokenGraph,
bound_lower: int | None,
bound_upper: int | None,
property: str = &#39;weight&#39;,
) -&gt; TokenGraph:
&#34;&#34;&#34;filters all edges which are within the provided bounds
inclusive limits: bound_lower &lt;= edge_weight &lt;= bound_upper are retained
Parameters
----------
bound_lower : int | None
lower bound for edge weights, edges with weight equal to this value are retained
bound_upper : int | None
upper bound for edge weights, edges with weight equal to this value are retained
Returns
-------
TokenGraph
a copy of the graph with filtered edges
&#34;&#34;&#34;
original_graph_edges = copy.deepcopy(graph.edges)
filtered_graph = graph.copy()
if not any((bound_lower, bound_upper)):
logger.warning(&#39;No bounds provided, returning original graph.&#39;)
return filtered_graph
for edge in original_graph_edges:
weight = typing.cast(int, filtered_graph[edge[0]][edge[1]][property])
if bound_lower is not None and weight &lt; bound_lower:
filtered_graph.remove_edge(edge[0], edge[1])
if bound_upper is not None and weight &gt; bound_upper:
filtered_graph.remove_edge(edge[0], edge[1])
filtered_graph.to_undirected(inplace=True, logging=False)
filtered_graph.update_metadata(logging=False)
return filtered_graph</code></pre>
</details>
<div class="desc"><p>filters all edges which are within the provided bounds
inclusive limits: bound_lower &lt;= edge_weight &lt;= bound_upper are retained</p>
<h2 id="parameters">Parameters</h2>
<dl>
<dt><strong><code>bound_lower</code></strong> :&ensp;<code>int | None</code></dt>
<dd>lower bound for edge weights, edges with weight equal to this value are retained</dd>
<dt><strong><code>bound_upper</code></strong> :&ensp;<code>int | None</code></dt>
<dd>upper bound for edge weights, edges with weight equal to this value are retained</dd>
</dl>
<h2 id="returns">Returns</h2>
<dl>
<dt><code><a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a></code></dt>
<dd>a copy of the graph with filtered edges</dd>
</dl></div>
</dd>
<dt id="lang_main.analysis.graphs.filter_graph_by_node_degree"><code class="name flex">
<span>def <span class="ident">filter_graph_by_node_degree</span></span>(<span>graph: <a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a>,<br>bound_lower: int | None,<br>bound_upper: int | None) > <a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def filter_graph_by_node_degree(
graph: TokenGraph,
bound_lower: int | None,
bound_upper: int | None,
) -&gt; TokenGraph:
&#34;&#34;&#34;filters all nodes which are within the provided bounds by their degree,
inclusive limits: bound_lower &lt;= node_degree &lt;= bound_upper are retained
Parameters
----------
bound_lower : int | None
lower bound for node degree, nodes with degree equal to this value are retained
bound_upper : int | None
upper bound for node degree, nodes with degree equal to this value are retained
Returns
-------
TokenGraph
a copy of the graph with filtered nodes
&#34;&#34;&#34;
# filter nodes by degree
original_graph_nodes = copy.deepcopy(graph.nodes)
filtered_graph = graph.copy()
filtered_graph_degree = copy.deepcopy(filtered_graph.degree)
if not any([bound_lower, bound_upper]):
logger.warning(&#39;No bounds provided, returning original graph.&#39;)
return filtered_graph
for node in original_graph_nodes:
degree = cast(int, filtered_graph_degree[node]) # type: ignore
if bound_lower is not None and degree &lt; bound_lower:
filtered_graph.remove_node(node)
if bound_upper is not None and degree &gt; bound_upper:
filtered_graph.remove_node(node)
filtered_graph.to_undirected(inplace=True, logging=False)
filtered_graph.update_metadata(logging=False)
return filtered_graph</code></pre>
</details>
<div class="desc"><p>filters all nodes which are within the provided bounds by their degree,
inclusive limits: bound_lower &lt;= node_degree &lt;= bound_upper are retained</p>
<h2 id="parameters">Parameters</h2>
<dl>
<dt><strong><code>bound_lower</code></strong> :&ensp;<code>int | None</code></dt>
<dd>lower bound for node degree, nodes with degree equal to this value are retained</dd>
<dt><strong><code>bound_upper</code></strong> :&ensp;<code>int | None</code></dt>
<dd>upper bound for node degree, nodes with degree equal to this value are retained</dd>
</dl>
<h2 id="returns">Returns</h2>
<dl>
<dt><code><a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a></code></dt>
<dd>a copy of the graph with filtered nodes</dd>
</dl></div>
</dd>
<dt id="lang_main.analysis.graphs.filter_graph_by_number_edges"><code class="name flex">
<span>def <span class="ident">filter_graph_by_number_edges</span></span>(<span>graph: <a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a>,<br>limit: int | None,<br>property: str = 'weight',<br>descending: bool = True) > <a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def filter_graph_by_number_edges(
graph: TokenGraph,
limit: int | None,
property: str = &#39;weight&#39;,
descending: bool = True,
) -&gt; TokenGraph:
graph = graph.copy()
# edges
original = set(graph.edges(data=property)) # type: ignore
original_sorted = sorted(original, key=lambda tup: tup[2], reverse=descending)
if limit is not None:
chosen = set(original_sorted[:limit])
else:
chosen = set(original_sorted)
edges_to_drop = original.difference(chosen)
graph.remove_edges_from(edges_to_drop)
return graph</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="lang_main.analysis.graphs.get_graph_metadata"><code class="name flex">
<span>def <span class="ident">get_graph_metadata</span></span>(<span>graph: Graph | DiGraph, logging: bool = False) > dict[str, float]</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def get_graph_metadata(
graph: Graph | DiGraph,
logging: bool = LOGGING_DEFAULT_GRAPHS,
) -&gt; dict[str, float]:
# info about graph
graph_info: dict[str, float] = {}
# nodes and edges
num_nodes = len(graph.nodes)
num_edges = len(graph.edges)
# edge weights
min_edge_weight: int = 1_000_000
max_edge_weight: int = 0
for edge in graph.edges:
weight = typing.cast(int, graph[edge[0]][edge[1]][&#39;weight&#39;])
if weight &lt; min_edge_weight:
min_edge_weight = weight
if weight &gt; max_edge_weight:
max_edge_weight = weight
# memory
edge_mem = sum([sys.getsizeof(e) for e in graph.edges])
node_mem = sum([sys.getsizeof(n) for n in graph.nodes])
total_mem = edge_mem + node_mem
graph_info.update(
num_nodes=num_nodes,
num_edges=num_edges,
min_edge_weight=min_edge_weight,
max_edge_weight=max_edge_weight,
node_memory=node_mem,
edge_memory=edge_mem,
total_memory=total_mem,
)
if logging:
logger.info(&#39;Graph properties: %d Nodes, %d Edges&#39;, num_nodes, num_edges)
logger.info(&#39;Node memory: %.2f KB&#39;, (node_mem / 1024))
logger.info(&#39;Edge memory: %.2f KB&#39;, (edge_mem / 1024))
logger.info(&#39;Total memory: %.2f KB&#39;, (total_mem / 1024))
return graph_info</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="lang_main.analysis.graphs.normalise_array_linear"><code class="name flex">
<span>def <span class="ident">normalise_array_linear</span></span>(<span>array: npt.NDArray[np.float32]) > numpy.ndarray[typing.Any, numpy.dtype[numpy.float32]]</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def normalise_array_linear(
array: npt.NDArray[np.float32],
) -&gt; npt.NDArray[np.float32]:
&#34;&#34;&#34;apply standard linear normalisation
Parameters
----------
array : npt.NDArray[np.float_]
array which shall be normalised
Returns
-------
npt.NDArray[np.float32]
min/max normalised array
&#34;&#34;&#34;
div = array.max() - array.min()
if div != 0:
arr_norm = (array - array.min()) / div
return arr_norm.astype(np.float32)
else:
return np.zeros(shape=array.shape, dtype=np.float32)</code></pre>
</details>
<div class="desc"><p>apply standard linear normalisation</p>
<h2 id="parameters">Parameters</h2>
<dl>
<dt><strong><code>array</code></strong> :&ensp;<code>npt.NDArray[np.float_]</code></dt>
<dd>array which shall be normalised</dd>
</dl>
<h2 id="returns">Returns</h2>
<dl>
<dt><code>npt.NDArray[np.float32]</code></dt>
<dd>min/max normalised array</dd>
</dl></div>
</dd>
<dt id="lang_main.analysis.graphs.pipe_add_graph_metrics"><code class="name flex">
<span>def <span class="ident">pipe_add_graph_metrics</span></span>(<span>*graphs: DiGraph | Graph) > tuple[networkx.classes.digraph.DiGraph | networkx.classes.graph.Graph, ...]</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def pipe_add_graph_metrics(
*graphs: DiGraph | Graph,
) -&gt; tuple[DiGraph | Graph, ...]:
collection: list[DiGraph | Graph] = []
for graph in graphs:
graph_copy = copy.deepcopy(graph)
add_weighted_degree(graph_copy)
add_betweenness_centrality(graph_copy)
add_importance_metric(graph_copy)
collection.append(graph_copy)
return tuple(collection)</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="lang_main.analysis.graphs.pipe_rescale_graph_edge_weights"><code class="name flex">
<span>def <span class="ident">pipe_rescale_graph_edge_weights</span></span>(<span>graph: <a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a>) > tuple[<a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a>, networkx.classes.graph.Graph]</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def pipe_rescale_graph_edge_weights(
graph: TokenGraph,
) -&gt; tuple[TokenGraph, Graph]:
&#34;&#34;&#34;helper function to allow calls in pipelines
Parameters
----------
graph : TokenGraph
token graph pushed through pipeline
Returns
-------
tuple[TokenGraph, Graph]
token graph (directed) and undirected version with rescaled edge weights
&#34;&#34;&#34;
graph = graph.copy()
return graph.rescale_edge_weights()</code></pre>
</details>
<div class="desc"><p>helper function to allow calls in pipelines</p>
<h2 id="parameters">Parameters</h2>
<dl>
<dt><strong><code>graph</code></strong> :&ensp;<code><a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a></code></dt>
<dd>token graph pushed through pipeline</dd>
</dl>
<h2 id="returns">Returns</h2>
<dl>
<dt><code>tuple[<a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a>, Graph]</code></dt>
<dd>token graph (directed) and undirected version with rescaled edge weights</dd>
</dl></div>
</dd>
<dt id="lang_main.analysis.graphs.rescale_edge_weights"><code class="name flex">
<span>def <span class="ident">rescale_edge_weights</span></span>(<span>graph: Graph | DiGraph | <a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a>,<br>weight_property: str = 'weight') > networkx.classes.graph.Graph | networkx.classes.digraph.DiGraph | <a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a></span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def rescale_edge_weights(
graph: Graph | DiGraph | TokenGraph,
weight_property: str = &#39;weight&#39;,
) -&gt; Graph | DiGraph | TokenGraph:
graph = graph.copy()
# check non-emptiness
verify_non_empty_graph(graph, including_edges=True)
# check if all edges contain weight property
verify_property(graph, property=weight_property)
weights = cast(list[int], [data[&#39;weight&#39;] for data in graph.edges.values()])
w_log = cast(npt.NDArray[np.float32], np.log(weights, dtype=np.float32))
weights_norm = normalise_array_linear(w_log)
weights_adjusted = weight_scaling(weights_norm)
# assign new weight values
for idx, (node_1, node_2) in enumerate(graph.edges):
graph[node_1][node_2][&#39;weight&#39;] = weights_adjusted[idx]
return graph</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="lang_main.analysis.graphs.save_to_GraphML"><code class="name flex">
<span>def <span class="ident">save_to_GraphML</span></span>(<span>graph: DiGraph | Graph, saving_path: Path, filename: str | None = None) > None</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def save_to_GraphML(
graph: DiGraph | Graph,
saving_path: Path,
filename: str | None = None,
) -&gt; None:
if filename is not None:
saving_path = saving_path.joinpath(filename)
saving_path = saving_path.with_suffix(&#39;.graphml&#39;)
nx.write_graphml(G=graph, path=saving_path)
logger.info(&#39;Successfully saved graph as GraphML file under %s.&#39;, saving_path)</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="lang_main.analysis.graphs.static_graph_analysis"><code class="name flex">
<span>def <span class="ident">static_graph_analysis</span></span>(<span>graph: <a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a>) > tuple[<a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a>]</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def static_graph_analysis(
graph: TokenGraph,
) -&gt; tuple[TokenGraph]:
&#34;&#34;&#34;helper function to allow the calculation of static metrics in pipelines
Parameters
----------
tk_graph_directed : TokenGraph
token graph (directed)
Returns
-------
tuple[TokenGraph]
token graph (directed) with included undirected version and calculated KPIs
&#34;&#34;&#34;
graph = graph.copy()
graph.perform_static_analysis()
return (graph,)</code></pre>
</details>
<div class="desc"><p>helper function to allow the calculation of static metrics in pipelines</p>
<h2 id="parameters">Parameters</h2>
<dl>
<dt><strong><code>tk_graph_directed</code></strong> :&ensp;<code><a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a></code></dt>
<dd>token graph (directed)</dd>
</dl>
<h2 id="returns">Returns</h2>
<dl>
<dt><code>tuple[<a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a>]</code></dt>
<dd>token graph (directed) with included undirected version and calculated KPIs</dd>
</dl></div>
</dd>
<dt id="lang_main.analysis.graphs.update_graph"><code class="name flex">
<span>def <span class="ident">update_graph</span></span>(<span>graph: Graph | DiGraph,<br>*,<br>batch: Iterable[tuple[Hashable, Hashable]] | None = None,<br>parent: Hashable | None = None,<br>child: Hashable | None = None,<br>weight_connection: int | None = None) > None</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def update_graph(
graph: Graph | DiGraph,
*,
batch: Iterable[tuple[Hashable, Hashable]] | None = None,
parent: Hashable | None = None,
child: Hashable | None = None,
weight_connection: int | None = None,
) -&gt; None:
if weight_connection is None:
weight_connection = 1
# check if edge not in Graph
if batch is not None:
graph.add_edges_from(batch, weight=weight_connection)
elif not graph.has_edge(parent, child):
# create new edge, nodes will be created if not already present
graph.add_edge(parent, child, weight=weight_connection)
else:
# update edge
graph[parent][child][&#39;weight&#39;] += weight_connection</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="lang_main.analysis.graphs.verify_non_empty_graph"><code class="name flex">
<span>def <span class="ident">verify_non_empty_graph</span></span>(<span>graph: DiGraph | Graph, including_edges: bool = True) > None</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def verify_non_empty_graph(
graph: DiGraph | Graph,
including_edges: bool = True,
) -&gt; None:
&#34;&#34;&#34;check if the given graph is empty, presence of nodes is checked first,
then of edges
Parameters
----------
graph : DiGraph | Graph
graph to check for emptiness
including_edges : bool, optional
whether to check for non-existence of edges, by default True
Raises
------
EmptyGraphError
if graph does not contain any nodes and therefore edges
EmptyEdgesError
if graph does not contain any edges
&#34;&#34;&#34;
if not tuple(graph.nodes):
raise EmptyGraphError(f&#39;Graph object &gt;&gt;{graph}&lt;&lt; does not contain any nodes.&#39;)
elif including_edges and not tuple(graph.edges):
raise EmptyEdgesError(f&#39;Graph object &gt;&gt;{graph}&lt;&lt; does not contain any edges.&#39;)</code></pre>
</details>
<div class="desc"><p>check if the given graph is empty, presence of nodes is checked first,
then of edges</p>
<h2 id="parameters">Parameters</h2>
<dl>
<dt><strong><code>graph</code></strong> :&ensp;<code>DiGraph | Graph</code></dt>
<dd>graph to check for emptiness</dd>
<dt><strong><code>including_edges</code></strong> :&ensp;<code>bool</code>, optional</dt>
<dd>whether to check for non-existence of edges, by default True</dd>
</dl>
<h2 id="raises">Raises</h2>
<dl>
<dt><code>EmptyGraphError</code></dt>
<dd>if graph does not contain any nodes and therefore edges</dd>
<dt><code>EmptyEdgesError</code></dt>
<dd>if graph does not contain any edges</dd>
</dl></div>
</dd>
<dt id="lang_main.analysis.graphs.verify_property"><code class="name flex">
<span>def <span class="ident">verify_property</span></span>(<span>graph: Graph | DiGraph, property: str) > None</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def verify_property(
graph: Graph | DiGraph,
property: str,
) -&gt; None:
for node_1, node_2 in graph.edges:
if property not in graph[node_1][node_2]:
raise EdgePropertyNotContainedError(
(
f&#39;Edge property &gt;&gt;{property}&lt;&lt; not &#39;
f&#39;available for edge &gt;&gt;({node_1}, {node_2})&lt;&lt;&#39;
)
)</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="lang_main.analysis.graphs.weight_scaling"><code class="name flex">
<span>def <span class="ident">weight_scaling</span></span>(<span>weights: npt.NDArray[np.float32], a: float = 1.1, b: float = 0.05) > numpy.ndarray[typing.Any, numpy.dtype[numpy.float32]]</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def weight_scaling(
weights: npt.NDArray[np.float32],
a: float = 1.1,
b: float = 0.05,
) -&gt; npt.NDArray[np.float32]:
&#34;&#34;&#34;non-linear scaling of already normalised edge weights [0;1]: bigger weights
have smaller weight delta than smaller weights. Bigger values für parameter
`b` reinforce this effect.
Based on:
https://math.stackexchange.com/questions/4297805/exponential-function-that-passes-through-0-0-and-1-1-with-variable-slope
With default values the range of edge weights lies approximately between [0.1; 1]
Parameters
----------
weights : npt.NDArray[np.float32]
pre-normalised edge weights as 1D array
a : float, optional
factor to determine the value for edge weights with value 0
with default approx. 0.1, by default 1.1
b : float, optional
adjust the curvature, smaller values increase it, by default 0.05
Returns
-------
npt.NDArray[np.float32]
non-linear adjusted edge weights as 1D array
&#34;&#34;&#34;
adjusted_weights = (b**weights - a) / (b - a)
return np.round(adjusted_weights, decimals=EDGE_WEIGHT_DECIMALS)</code></pre>
</details>
<div class="desc"><p>non-linear scaling of already normalised edge weights [0;1]: bigger weights
have smaller weight delta than smaller weights. Bigger values für parameter
<code>b</code> reinforce this effect.
Based on:
<a href="https://math.stackexchange.com/questions/4297805/exponential-function-that-passes-through-0-0-and-1-1-with-variable-slope">https://math.stackexchange.com/questions/4297805/exponential-function-that-passes-through-0-0-and-1-1-with-variable-slope</a></p>
<p>With default values the range of edge weights lies approximately between [0.1; 1]</p>
<h2 id="parameters">Parameters</h2>
<dl>
<dt><strong><code>weights</code></strong> :&ensp;<code>npt.NDArray[np.float32]</code></dt>
<dd>pre-normalised edge weights as 1D array</dd>
<dt><strong><code>a</code></strong> :&ensp;<code>float</code>, optional</dt>
<dd>factor to determine the value for edge weights with value 0
with default approx. 0.1, by default 1.1</dd>
<dt><strong><code>b</code></strong> :&ensp;<code>float</code>, optional</dt>
<dd>adjust the curvature, smaller values increase it, by default 0.05</dd>
</dl>
<h2 id="returns">Returns</h2>
<dl>
<dt><code>npt.NDArray[np.float32]</code></dt>
<dd>non-linear adjusted edge weights as 1D array</dd>
</dl></div>
</dd>
</dl>
</section>
<section>
<h2 class="section-title" id="header-classes">Classes</h2>
<dl>
<dt id="lang_main.analysis.graphs.TokenGraph"><code class="flex name class">
<span>class <span class="ident">TokenGraph</span></span>
<span>(</span><span>name: str = 'TokenGraph',<br>enable_logging: bool = True,<br>incoming_graph_data: Any | None = None,<br>**attr)</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">class TokenGraph(DiGraph):
def __init__(
self,
name: str = &#39;TokenGraph&#39;,
enable_logging: bool = True,
incoming_graph_data: Any | None = None,
**attr,
) -&gt; None:
super().__init__(incoming_graph_data, **attr)
# logging of different actions
self.logging = enable_logging
# properties
self._name = name
# directed and undirected graph data
self._directed = self
self._metadata_directed: dict[str, float] = {}
self._undirected: Graph | None = None
self._metadata_undirected: dict[str, float] = {}
# indicate rescaled weights
self.rescaled_weights: bool = False
def __repr__(self) -&gt; str:
return self.__str__()
def __str__(self) -&gt; str:
return (
f&#39;TokenGraph(name: {self.name}, number of nodes: &#39;
f&#39;{len(self.nodes)}, number of edges: &#39;
f&#39;{len(self.edges)})&#39;
)
def disable_logging(self) -&gt; None:
self.logging = False
# !! only used to verify that saving was done correctly
&#34;&#34;&#34;
def __key(self) -&gt; tuple[Hashable, ...]:
return (self.name, tuple(self.nodes), tuple(self.edges))
def __hash__(self) -&gt; int:
return hash(self.__key())
&#34;&#34;&#34;
def copy(self) -&gt; Self:
&#34;&#34;&#34;returns a (deep) copy of the graph
Returns
-------
Self
deep copy of the graph
&#34;&#34;&#34;
return copy.deepcopy(self)
@property
def name(self) -&gt; str:
return self._name
@property
def directed(self) -&gt; Self:
return self._directed
@property
def undirected(self) -&gt; Graph:
if self._undirected is None:
self._undirected = self.to_undirected(inplace=False, logging=False)
return self._undirected
@property
def metadata_directed(self) -&gt; dict[str, float]:
return self._metadata_directed
@property
def metadata_undirected(self) -&gt; dict[str, float]:
return self._metadata_undirected
@overload
def to_undirected(
self,
inplace: Literal[True] = ...,
logging: bool | None = ...,
) -&gt; None: ...
@overload
def to_undirected(
self,
inplace: Literal[False],
logging: bool | None = ...,
) -&gt; Graph: ...
def to_undirected(
self,
inplace: bool = True,
logging: bool | None = None,
) -&gt; Graph | None:
if logging is None:
logging = self.logging
# cast to integer edge weights only if edges were not rescaled previously
cast_int: bool = True
if self.rescaled_weights:
cast_int = False
self._undirected = convert_graph_to_undirected(
graph=self,
logging=logging,
cast_int=cast_int,
)
self._metadata_undirected = get_graph_metadata(graph=self._undirected, logging=False)
if not inplace:
return self._undirected
def update_metadata(
self,
logging: bool | None = None,
) -&gt; None:
if logging is None:
logging = self.logging
self._metadata_directed = get_graph_metadata(graph=self, logging=logging)
if self._undirected is not None:
self._metadata_undirected = get_graph_metadata(
graph=self._undirected, logging=logging
)
def rescale_edge_weights(
self,
) -&gt; tuple[TokenGraph, Graph]:
&#34;&#34;&#34;generate new instances of the directed and undirected TokenGraph with
rescaled edge weights
Only this method ensures that undirected graphs are scaled properly. If
the underlying `to_undirected` method of the directed and rescaled
TokenGraph instance is called the weights are not rescaled again. Thus,
the maximum edge weight can exceed the theoretical maximum value of 1. To
ensure consistent behaviour across different applications of the conversion to
undirected graphs new instances are returned, especially for the undirected
graph.
In contrast, the new directed TokenGraph contains an undirected version without
rescaling of the weights. Therefore, this undirected version differs from the version
returned by this method.
Returns
-------
tuple[TokenGraph, Graph]
directed and undirected instances
&#34;&#34;&#34;
self.to_undirected(inplace=True, logging=False)
token_graph = rescale_edge_weights(self.directed)
token_graph.rescaled_weights = True
token_graph.update_metadata(logging=False)
undirected = rescale_edge_weights(self.undirected)
return token_graph, undirected
def perform_static_analysis(self) -&gt; None:
&#34;&#34;&#34;calculate different metrics directly on the data of the underlying graphs
(directed and undirected)
Current operations:
- adding weighted degree
&#34;&#34;&#34;
add_weighted_degree(self)
add_weighted_degree(self.undirected)
def _save_prepare(
self,
path: Path,
filename: str | None = None,
) -&gt; Path:
if filename is not None:
saving_path = path.joinpath(f&#39;{filename}&#39;)
else:
saving_path = path.joinpath(f&#39;{self.name}&#39;)
return saving_path
def to_GraphML(
self,
path: Path,
filename: str | None = None,
directed: bool = False,
) -&gt; None:
&#34;&#34;&#34;save one of the stored graphs to GraphML format on disk,
Parameters
----------
path : Path
target path for saving the file
filename : str | None, optional
filename to be given, by default None
directed : bool, optional
indicator whether directed or undirected graph
should be exported, by default False (undirected)
Raises
------
ValueError
undirected graph should be exported but is not available
&#34;&#34;&#34;
saving_path = self._save_prepare(path=path, filename=filename)
if directed:
target_graph = self.directed
else:
target_graph = self.undirected
save_to_GraphML(graph=target_graph, saving_path=saving_path)
def to_pickle(
self,
path: Path,
filename: str | None = None,
) -&gt; None:
&#34;&#34;&#34;save whole TokenGraph object as pickle file
Parameters
----------
path : Path
target path for saving the file
filename : str | None, optional
filename to be given, by default None
&#34;&#34;&#34;
saving_path = self._save_prepare(path=path, filename=filename)
saving_path = saving_path.with_suffix(&#39;.pkl&#39;)
save_pickle(obj=self, path=saving_path)
@classmethod
def from_file(
cls,
path: Path,
node_type_graphml: type = str,
) -&gt; Self:
# !! no validity checks for pickle files
# !! GraphML files not correct because not all properties
# !! are parsed correctly
# TODO REWORK
match path.suffix:
case &#39;.graphml&#39;:
graph = typing.cast(Self, nx.read_graphml(path, node_type=node_type_graphml))
logger.info(&#39;Successfully loaded graph from GraphML file %s.&#39;, path)
case &#39;.pkl&#39; | &#39;.pickle&#39;:
graph = typing.cast(Self, load_pickle(path))
logger.info(&#39;Successfully loaded graph from pickle file %s.&#39;, path)
case _:
raise ValueError(&#39;File format not supported.&#39;)
return graph</code></pre>
</details>
<div class="desc"><p>Base class for directed graphs.</p>
<p>A DiGraph stores nodes and edges with optional data, or attributes.</p>
<p>DiGraphs hold directed edges.
Self loops are allowed but multiple
(parallel) edges are not.</p>
<p>Nodes can be arbitrary (hashable) Python objects with optional
key/value attributes. By convention <code>None</code> is not used as a node.</p>
<p>Edges are represented as links between nodes with optional
key/value attributes.</p>
<h2 id="parameters">Parameters</h2>
<dl>
<dt><strong><code>incoming_graph_data</code></strong> :&ensp;<code>input graph (optional</code>, default<code>: None)</code></dt>
<dd>Data to initialize graph. If None (default) an empty
graph is created.
The data can be any format that is supported
by the to_networkx_graph() function, currently including edge list,
dict of dicts, dict of lists, NetworkX graph, 2D NumPy array, SciPy
sparse matrix, or PyGraphviz graph.</dd>
<dt><strong><code>attr</code></strong> :&ensp;<code>keyword arguments</code>, optional <code>(default= no attributes)</code></dt>
<dd>Attributes to add to graph as key=value pairs.</dd>
</dl>
<h2 id="see-also">See Also</h2>
<p><code>Graph</code>
<code>MultiGraph</code>
<code>MultiDiGraph</code></p>
<h2 id="examples">Examples</h2>
<p>Create an empty graph structure (a "null graph") with no nodes and
no edges.</p>
<pre><code class="language-python-repl">&gt;&gt;&gt; G = nx.DiGraph()
</code></pre>
<p>G can be grown in several ways.</p>
<p><strong>Nodes:</strong></p>
<p>Add one node at a time:</p>
<pre><code class="language-python-repl">&gt;&gt;&gt; G.add_node(1)
</code></pre>
<p>Add the nodes from any container (a list, dict, set or
even the lines from a file or the nodes from another graph).</p>
<pre><code class="language-python-repl">&gt;&gt;&gt; G.add_nodes_from([2, 3])
&gt;&gt;&gt; G.add_nodes_from(range(100, 110))
&gt;&gt;&gt; H = nx.path_graph(10)
&gt;&gt;&gt; G.add_nodes_from(H)
</code></pre>
<p>In addition to strings and integers any hashable Python object
(except None) can represent a node, e.g. a customized node object,
or even another Graph.</p>
<pre><code class="language-python-repl">&gt;&gt;&gt; G.add_node(H)
</code></pre>
<p><strong>Edges:</strong></p>
<p>G can also be grown by adding edges.</p>
<p>Add one edge,</p>
<pre><code class="language-python-repl">&gt;&gt;&gt; G.add_edge(1, 2)
</code></pre>
<p>a list of edges,</p>
<pre><code class="language-python-repl">&gt;&gt;&gt; G.add_edges_from([(1, 2), (1, 3)])
</code></pre>
<p>or a collection of edges,</p>
<pre><code class="language-python-repl">&gt;&gt;&gt; G.add_edges_from(H.edges)
</code></pre>
<p>If some edges connect nodes not yet in the graph, the nodes
are added automatically.
There are no errors when adding
nodes or edges that already exist.</p>
<p><strong>Attributes:</strong></p>
<p>Each graph, node, and edge can hold key/value attribute pairs
in an associated attribute dictionary (the keys must be hashable).
By default these are empty, but can be added or changed using
add_edge, add_node or direct manipulation of the attribute
dictionaries named graph, node and edge respectively.</p>
<pre><code class="language-python-repl">&gt;&gt;&gt; G = nx.DiGraph(day=&quot;Friday&quot;)
&gt;&gt;&gt; G.graph
{'day': 'Friday'}
</code></pre>
<p>Add node attributes using add_node(), add_nodes_from() or G.nodes</p>
<pre><code class="language-python-repl">&gt;&gt;&gt; G.add_node(1, time=&quot;5pm&quot;)
&gt;&gt;&gt; G.add_nodes_from([3], time=&quot;2pm&quot;)
&gt;&gt;&gt; G.nodes[1]
{'time': '5pm'}
&gt;&gt;&gt; G.nodes[1][&quot;room&quot;] = 714
&gt;&gt;&gt; del G.nodes[1][&quot;room&quot;] # remove attribute
&gt;&gt;&gt; list(G.nodes(data=True))
[(1, {'time': '5pm'}), (3, {'time': '2pm'})]
</code></pre>
<p>Add edge attributes using add_edge(), add_edges_from(), subscript
notation, or G.edges.</p>
<pre><code class="language-python-repl">&gt;&gt;&gt; G.add_edge(1, 2, weight=4.7)
&gt;&gt;&gt; G.add_edges_from([(3, 4), (4, 5)], color=&quot;red&quot;)
&gt;&gt;&gt; G.add_edges_from([(1, 2, {&quot;color&quot;: &quot;blue&quot;}), (2, 3, {&quot;weight&quot;: 8})])
&gt;&gt;&gt; G[1][2][&quot;weight&quot;] = 4.7
&gt;&gt;&gt; G.edges[1, 2][&quot;weight&quot;] = 4
</code></pre>
<p>Warning: we protect the graph data structure by making <code>G.edges[1, 2]</code> a
read-only dict-like structure. However, you can assign to attributes
in e.g. <code>G.edges[1, 2]</code>. Thus, use 2 sets of brackets to add/change
data attributes: <code>G.edges[1, 2]['weight'] = 4</code>
(For multigraphs: <code>MG.edges[u, v, key][name] = value</code>).</p>
<p><strong>Shortcuts:</strong></p>
<p>Many common graph features allow python syntax to speed reporting.</p>
<pre><code class="language-python-repl">&gt;&gt;&gt; 1 in G # check if node in graph
True
&gt;&gt;&gt; [n for n in G if n &lt; 3] # iterate through nodes
[1, 2]
&gt;&gt;&gt; len(G) # number of nodes in graph
5
</code></pre>
<p>Often the best way to traverse all edges of a graph is via the neighbors.
The neighbors are reported as an adjacency-dict <code>G.adj</code> or <code>G.adjacency()</code></p>
<pre><code class="language-python-repl">&gt;&gt;&gt; for n, nbrsdict in G.adjacency():
... for nbr, eattr in nbrsdict.items():
... if &quot;weight&quot; in eattr:
... # Do something useful with the edges
... pass
</code></pre>
<p>But the edges reporting object is often more convenient:</p>
<pre><code class="language-python-repl">&gt;&gt;&gt; for u, v, weight in G.edges(data=&quot;weight&quot;):
... if weight is not None:
... # Do something useful with the edges
... pass
</code></pre>
<p><strong>Reporting:</strong></p>
<p>Simple graph information is obtained using object-attributes and methods.
Reporting usually provides views instead of containers to reduce memory
usage. The views update as the graph is updated similarly to dict-views.
The objects <code>nodes</code>, <code>edges</code> and <code>adj</code> provide access to data attributes
via lookup (e.g. <code>nodes[n]</code>, <code>edges[u, v]</code>, <code>adj[u][v]</code>) and iteration
(e.g. <code>nodes.items()</code>, <code>nodes.data('color')</code>,
<code>nodes.data('color', default='blue')</code> and similarly for <code>edges</code>)
Views exist for <code>nodes</code>, <code>edges</code>, <code>neighbors()</code>/<code>adj</code> and <code>degree</code>.</p>
<p>For details on these and other miscellaneous methods, see below.</p>
<p><strong>Subclasses (Advanced):</strong></p>
<p>The Graph class uses a dict-of-dict-of-dict data structure.
The outer dict (node_dict) holds adjacency information keyed by node.
The next dict (adjlist_dict) represents the adjacency information and holds
edge data keyed by neighbor.
The inner dict (edge_attr_dict) represents
the edge data and holds edge attribute values keyed by attribute names.</p>
<p>Each of these three dicts can be replaced in a subclass by a user defined
dict-like object. In general, the dict-like features should be
maintained but extra features can be added. To replace one of the
dicts create a new graph class by changing the class(!) variable
holding the factory for that dict-like structure. The variable names are
node_dict_factory, node_attr_dict_factory, adjlist_inner_dict_factory,
adjlist_outer_dict_factory, edge_attr_dict_factory and graph_attr_dict_factory.</p>
<p>node_dict_factory : function, (default: dict)
Factory function to be used to create the dict containing node
attributes, keyed by node id.
It should require no arguments and return a dict-like object</p>
<p>node_attr_dict_factory: function, (default: dict)
Factory function to be used to create the node attribute
dict which holds attribute values keyed by attribute name.
It should require no arguments and return a dict-like object</p>
<p>adjlist_outer_dict_factory : function, (default: dict)
Factory function to be used to create the outer-most dict
in the data structure that holds adjacency info keyed by node.
It should require no arguments and return a dict-like object.</p>
<p>adjlist_inner_dict_factory : function, optional (default: dict)
Factory function to be used to create the adjacency list
dict which holds edge data keyed by neighbor.
It should require no arguments and return a dict-like object</p>
<p>edge_attr_dict_factory : function, optional (default: dict)
Factory function to be used to create the edge attribute
dict which holds attribute values keyed by attribute name.
It should require no arguments and return a dict-like object.</p>
<p>graph_attr_dict_factory : function, (default: dict)
Factory function to be used to create the graph attribute
dict which holds attribute values keyed by attribute name.
It should require no arguments and return a dict-like object.</p>
<p>Typically, if your extension doesn't impact the data structure all
methods will inherited without issue except: <code>to_directed/to_undirected</code>.
By default these methods create a DiGraph/Graph class and you probably
want them to create your extension of a DiGraph/Graph. To facilitate
this we define two class variables that you can set in your subclass.</p>
<p>to_directed_class : callable, (default: DiGraph or MultiDiGraph)
Class to create a new graph structure in the <code>to_directed</code> method.
If <code>None</code>, a NetworkX class (DiGraph or MultiDiGraph) is used.</p>
<p>to_undirected_class : callable, (default: Graph or MultiGraph)
Class to create a new graph structure in the <code>to_undirected</code> method.
If <code>None</code>, a NetworkX class (Graph or MultiGraph) is used.</p>
<p><strong>Subclassing Example</strong></p>
<p>Create a low memory graph class that effectively disallows edge
attributes by using a single attribute dict for all edges.
This reduces the memory used, but you lose edge attributes.</p>
<pre><code class="language-python-repl">&gt;&gt;&gt; class ThinGraph(nx.Graph):
... all_edge_dict = {&quot;weight&quot;: 1}
...
... def single_edge_dict(self):
... return self.all_edge_dict
...
... edge_attr_dict_factory = single_edge_dict
&gt;&gt;&gt; G = ThinGraph()
&gt;&gt;&gt; G.add_edge(2, 1)
&gt;&gt;&gt; G[2][1]
{'weight': 1}
&gt;&gt;&gt; G.add_edge(2, 2)
&gt;&gt;&gt; G[2][1] is G[2][2]
True
</code></pre>
<p>Initialize a graph with edges, name, or graph attributes.</p>
<h2 id="parameters_1">Parameters</h2>
<dl>
<dt><strong><code>incoming_graph_data</code></strong> :&ensp;<code>input graph (optional</code>, default<code>: None)</code></dt>
<dd>Data to initialize graph.
If None (default) an empty
graph is created.
The data can be an edge list, or any
NetworkX graph object.
If the corresponding optional Python
packages are installed the data can also be a 2D NumPy array, a
SciPy sparse array, or a PyGraphviz graph.</dd>
<dt><strong><code>attr</code></strong> :&ensp;<code>keyword arguments</code>, optional <code>(default= no attributes)</code></dt>
<dd>Attributes to add to graph as key=value pairs.</dd>
</dl>
<h2 id="see-also_1">See Also</h2>
<p><code>convert</code></p>
<h2 id="examples_1">Examples</h2>
<pre><code class="language-python-repl">&gt;&gt;&gt; G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc
&gt;&gt;&gt; G = nx.Graph(name=&quot;my graph&quot;)
&gt;&gt;&gt; e = [(1, 2), (2, 3), (3, 4)] # list of edges
&gt;&gt;&gt; G = nx.Graph(e)
</code></pre>
<p>Arbitrary graph attribute pairs (key=value) may be assigned</p>
<pre><code class="language-python-repl">&gt;&gt;&gt; G = nx.Graph(e, day=&quot;Friday&quot;)
&gt;&gt;&gt; G.graph
{'day': 'Friday'}
</code></pre></div>
<h3>Ancestors</h3>
<ul class="hlist">
<li>networkx.classes.digraph.DiGraph</li>
<li>networkx.classes.graph.Graph</li>
</ul>
<h3>Static methods</h3>
<dl>
<dt id="lang_main.analysis.graphs.TokenGraph.from_file"><code class="name flex">
<span>def <span class="ident">from_file</span></span>(<span>path: Path, node_type_graphml: type = builtins.str) > Self</span>
</code></dt>
<dd>
<div class="desc"></div>
</dd>
</dl>
<h3>Instance variables</h3>
<dl>
<dt id="lang_main.analysis.graphs.TokenGraph.directed"><code class="name">prop <span class="ident">directed</span> : Self</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@property
def directed(self) -&gt; Self:
return self._directed</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="lang_main.analysis.graphs.TokenGraph.metadata_directed"><code class="name">prop <span class="ident">metadata_directed</span> : dict[str, float]</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@property
def metadata_directed(self) -&gt; dict[str, float]:
return self._metadata_directed</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="lang_main.analysis.graphs.TokenGraph.metadata_undirected"><code class="name">prop <span class="ident">metadata_undirected</span> : dict[str, float]</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@property
def metadata_undirected(self) -&gt; dict[str, float]:
return self._metadata_undirected</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="lang_main.analysis.graphs.TokenGraph.name"><code class="name">prop <span class="ident">name</span> : str</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@property
def name(self) -&gt; str:
return self._name</code></pre>
</details>
<div class="desc"><p>String identifier of the graph.</p>
<p>This graph attribute appears in the attribute dict G.graph
keyed by the string <code>"name"</code>. as well as an attribute (technically
a property) <code>G.name</code>. This is entirely user controlled.</p></div>
</dd>
<dt id="lang_main.analysis.graphs.TokenGraph.undirected"><code class="name">prop <span class="ident">undirected</span> : Graph</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">@property
def undirected(self) -&gt; Graph:
if self._undirected is None:
self._undirected = self.to_undirected(inplace=False, logging=False)
return self._undirected</code></pre>
</details>
<div class="desc"></div>
</dd>
</dl>
<h3>Methods</h3>
<dl>
<dt id="lang_main.analysis.graphs.TokenGraph.copy"><code class="name flex">
<span>def <span class="ident">copy</span></span>(<span>self) > Self</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def copy(self) -&gt; Self:
&#34;&#34;&#34;returns a (deep) copy of the graph
Returns
-------
Self
deep copy of the graph
&#34;&#34;&#34;
return copy.deepcopy(self)</code></pre>
</details>
<div class="desc"><p>returns a (deep) copy of the graph</p>
<h2 id="returns">Returns</h2>
<dl>
<dt><code>Self</code></dt>
<dd>deep copy of the graph</dd>
</dl></div>
</dd>
<dt id="lang_main.analysis.graphs.TokenGraph.disable_logging"><code class="name flex">
<span>def <span class="ident">disable_logging</span></span>(<span>self) > None</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def disable_logging(self) -&gt; None:
self.logging = False</code></pre>
</details>
<div class="desc"></div>
</dd>
<dt id="lang_main.analysis.graphs.TokenGraph.perform_static_analysis"><code class="name flex">
<span>def <span class="ident">perform_static_analysis</span></span>(<span>self) > None</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def perform_static_analysis(self) -&gt; None:
&#34;&#34;&#34;calculate different metrics directly on the data of the underlying graphs
(directed and undirected)
Current operations:
- adding weighted degree
&#34;&#34;&#34;
add_weighted_degree(self)
add_weighted_degree(self.undirected)</code></pre>
</details>
<div class="desc"><p>calculate different metrics directly on the data of the underlying graphs
(directed and undirected)</p>
<p>Current operations:
- adding weighted degree</p></div>
</dd>
<dt id="lang_main.analysis.graphs.TokenGraph.rescale_edge_weights"><code class="name flex">
<span>def <span class="ident">rescale_edge_weights</span></span>(<span>self) > tuple[<a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a>, networkx.classes.graph.Graph]</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def rescale_edge_weights(
self,
) -&gt; tuple[TokenGraph, Graph]:
&#34;&#34;&#34;generate new instances of the directed and undirected TokenGraph with
rescaled edge weights
Only this method ensures that undirected graphs are scaled properly. If
the underlying `to_undirected` method of the directed and rescaled
TokenGraph instance is called the weights are not rescaled again. Thus,
the maximum edge weight can exceed the theoretical maximum value of 1. To
ensure consistent behaviour across different applications of the conversion to
undirected graphs new instances are returned, especially for the undirected
graph.
In contrast, the new directed TokenGraph contains an undirected version without
rescaling of the weights. Therefore, this undirected version differs from the version
returned by this method.
Returns
-------
tuple[TokenGraph, Graph]
directed and undirected instances
&#34;&#34;&#34;
self.to_undirected(inplace=True, logging=False)
token_graph = rescale_edge_weights(self.directed)
token_graph.rescaled_weights = True
token_graph.update_metadata(logging=False)
undirected = rescale_edge_weights(self.undirected)
return token_graph, undirected</code></pre>
</details>
<div class="desc"><p>generate new instances of the directed and undirected TokenGraph with
rescaled edge weights
Only this method ensures that undirected graphs are scaled properly. If
the underlying <code>to_undirected</code> method of the directed and rescaled
TokenGraph instance is called the weights are not rescaled again. Thus,
the maximum edge weight can exceed the theoretical maximum value of 1. To
ensure consistent behaviour across different applications of the conversion to
undirected graphs new instances are returned, especially for the undirected
graph.
In contrast, the new directed TokenGraph contains an undirected version without
rescaling of the weights. Therefore, this undirected version differs from the version
returned by this method.</p>
<h2 id="returns">Returns</h2>
<dl>
<dt><code>tuple[<a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a>, Graph]</code></dt>
<dd>directed and undirected instances</dd>
</dl></div>
</dd>
<dt id="lang_main.analysis.graphs.TokenGraph.to_GraphML"><code class="name flex">
<span>def <span class="ident">to_GraphML</span></span>(<span>self, path: Path, filename: str | None = None, directed: bool = False) > None</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def to_GraphML(
self,
path: Path,
filename: str | None = None,
directed: bool = False,
) -&gt; None:
&#34;&#34;&#34;save one of the stored graphs to GraphML format on disk,
Parameters
----------
path : Path
target path for saving the file
filename : str | None, optional
filename to be given, by default None
directed : bool, optional
indicator whether directed or undirected graph
should be exported, by default False (undirected)
Raises
------
ValueError
undirected graph should be exported but is not available
&#34;&#34;&#34;
saving_path = self._save_prepare(path=path, filename=filename)
if directed:
target_graph = self.directed
else:
target_graph = self.undirected
save_to_GraphML(graph=target_graph, saving_path=saving_path)</code></pre>
</details>
<div class="desc"><p>save one of the stored graphs to GraphML format on disk,</p>
<h2 id="parameters">Parameters</h2>
<dl>
<dt><strong><code>path</code></strong> :&ensp;<code>Path</code></dt>
<dd>target path for saving the file</dd>
<dt><strong><code>filename</code></strong> :&ensp;<code>str | None</code>, optional</dt>
<dd>filename to be given, by default None</dd>
<dt><strong><code>directed</code></strong> :&ensp;<code>bool</code>, optional</dt>
<dd>indicator whether directed or undirected graph
should be exported, by default False (undirected)</dd>
</dl>
<h2 id="raises">Raises</h2>
<dl>
<dt><code>ValueError</code></dt>
<dd>undirected graph should be exported but is not available</dd>
</dl></div>
</dd>
<dt id="lang_main.analysis.graphs.TokenGraph.to_pickle"><code class="name flex">
<span>def <span class="ident">to_pickle</span></span>(<span>self, path: Path, filename: str | None = None) > None</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def to_pickle(
self,
path: Path,
filename: str | None = None,
) -&gt; None:
&#34;&#34;&#34;save whole TokenGraph object as pickle file
Parameters
----------
path : Path
target path for saving the file
filename : str | None, optional
filename to be given, by default None
&#34;&#34;&#34;
saving_path = self._save_prepare(path=path, filename=filename)
saving_path = saving_path.with_suffix(&#39;.pkl&#39;)
save_pickle(obj=self, path=saving_path)</code></pre>
</details>
<div class="desc"><p>save whole TokenGraph object as pickle file</p>
<h2 id="parameters">Parameters</h2>
<dl>
<dt><strong><code>path</code></strong> :&ensp;<code>Path</code></dt>
<dd>target path for saving the file</dd>
<dt><strong><code>filename</code></strong> :&ensp;<code>str | None</code>, optional</dt>
<dd>filename to be given, by default None</dd>
</dl></div>
</dd>
<dt id="lang_main.analysis.graphs.TokenGraph.to_undirected"><code class="name flex">
<span>def <span class="ident">to_undirected</span></span>(<span>self, inplace: bool = True, logging: bool | None = None) > networkx.classes.graph.Graph | None</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def to_undirected(
self,
inplace: bool = True,
logging: bool | None = None,
) -&gt; Graph | None:
if logging is None:
logging = self.logging
# cast to integer edge weights only if edges were not rescaled previously
cast_int: bool = True
if self.rescaled_weights:
cast_int = False
self._undirected = convert_graph_to_undirected(
graph=self,
logging=logging,
cast_int=cast_int,
)
self._metadata_undirected = get_graph_metadata(graph=self._undirected, logging=False)
if not inplace:
return self._undirected</code></pre>
</details>
<div class="desc"><p>Returns an undirected representation of the digraph.</p>
<h2 id="parameters">Parameters</h2>
<dl>
<dt><strong><code>reciprocal</code></strong> :&ensp;<code>bool (optional)</code></dt>
<dd>&nbsp;</dd>
<dt>If True only keep edges that appear in both directions</dt>
<dt>in the original digraph.</dt>
<dt><strong><code>as_view</code></strong> :&ensp;<code>bool (optional</code>, default=<code>False)</code></dt>
<dd>&nbsp;</dd>
</dl>
<p>If True return an undirected view of the original directed graph.</p>
<h2 id="returns">Returns</h2>
<dl>
<dt><strong><code>G</code></strong> :&ensp;<code>Graph</code></dt>
<dd>An undirected graph with the same name and nodes and
with edge (u, v, data) if either (u, v, data) or (v, u, data)
is in the digraph.
If both edges exist in digraph and
their edge data is different, only one edge is created
with an arbitrary choice of which edge data to use.
You must check and correct for this manually if desired.</dd>
</dl>
<h2 id="see-also">See Also</h2>
<p><code>Graph</code>, <code>copy</code>, <code>add_edge</code>, <code>add_edges_from</code></p>
<h2 id="notes">Notes</h2>
<p>If edges in both directions (u, v) and (v, u) exist in the
graph, attributes for the new undirected edge will be a combination of
the attributes of the directed edges.
The edge data is updated
in the (arbitrary) order that the edges are encountered.
For
more customized control of the edge attributes use add_edge().</p>
<p>This returns a "deepcopy" of the edge, node, and
graph attributes which attempts to completely copy
all of the data and references.</p>
<p>This is in contrast to the similar G=DiGraph(D) which returns a
shallow copy of the data.</p>
<p>See the Python copy module for more information on shallow
and deep copies, <a href="https://docs.python.org/3/library/copy.html.">https://docs.python.org/3/library/copy.html.</a></p>
<p>Warning: If you have subclassed DiGraph to use dict-like objects
in the data structure, those changes do not transfer to the
Graph created by this method.</p>
<h2 id="examples">Examples</h2>
<pre><code class="language-python-repl">&gt;&gt;&gt; G = nx.path_graph(2) # or MultiGraph, etc
&gt;&gt;&gt; H = G.to_directed()
&gt;&gt;&gt; list(H.edges)
[(0, 1), (1, 0)]
&gt;&gt;&gt; G2 = H.to_undirected()
&gt;&gt;&gt; list(G2.edges)
[(0, 1)]
</code></pre></div>
</dd>
<dt id="lang_main.analysis.graphs.TokenGraph.update_metadata"><code class="name flex">
<span>def <span class="ident">update_metadata</span></span>(<span>self, logging: bool | None = None) > None</span>
</code></dt>
<dd>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def update_metadata(
self,
logging: bool | None = None,
) -&gt; None:
if logging is None:
logging = self.logging
self._metadata_directed = get_graph_metadata(graph=self, logging=logging)
if self._undirected is not None:
self._metadata_undirected = get_graph_metadata(
graph=self._undirected, logging=logging
)</code></pre>
</details>
<div class="desc"></div>
</dd>
</dl>
</dd>
</dl>
</section>
</article>
<nav id="sidebar">
<div class="toc">
<ul></ul>
</div>
<ul id="index">
<li><h3>Super-module</h3>
<ul>
<li><code><a title="lang_main.analysis" href="index.html">lang_main.analysis</a></code></li>
</ul>
</li>
<li><h3><a href="#header-functions">Functions</a></h3>
<ul class="">
<li><code><a title="lang_main.analysis.graphs.add_betweenness_centrality" href="#lang_main.analysis.graphs.add_betweenness_centrality">add_betweenness_centrality</a></code></li>
<li><code><a title="lang_main.analysis.graphs.add_importance_metric" href="#lang_main.analysis.graphs.add_importance_metric">add_importance_metric</a></code></li>
<li><code><a title="lang_main.analysis.graphs.add_weighted_degree" href="#lang_main.analysis.graphs.add_weighted_degree">add_weighted_degree</a></code></li>
<li><code><a title="lang_main.analysis.graphs.convert_graph_to_cytoscape" href="#lang_main.analysis.graphs.convert_graph_to_cytoscape">convert_graph_to_cytoscape</a></code></li>
<li><code><a title="lang_main.analysis.graphs.convert_graph_to_undirected" href="#lang_main.analysis.graphs.convert_graph_to_undirected">convert_graph_to_undirected</a></code></li>
<li><code><a title="lang_main.analysis.graphs.filter_graph_by_edge_weight" href="#lang_main.analysis.graphs.filter_graph_by_edge_weight">filter_graph_by_edge_weight</a></code></li>
<li><code><a title="lang_main.analysis.graphs.filter_graph_by_node_degree" href="#lang_main.analysis.graphs.filter_graph_by_node_degree">filter_graph_by_node_degree</a></code></li>
<li><code><a title="lang_main.analysis.graphs.filter_graph_by_number_edges" href="#lang_main.analysis.graphs.filter_graph_by_number_edges">filter_graph_by_number_edges</a></code></li>
<li><code><a title="lang_main.analysis.graphs.get_graph_metadata" href="#lang_main.analysis.graphs.get_graph_metadata">get_graph_metadata</a></code></li>
<li><code><a title="lang_main.analysis.graphs.normalise_array_linear" href="#lang_main.analysis.graphs.normalise_array_linear">normalise_array_linear</a></code></li>
<li><code><a title="lang_main.analysis.graphs.pipe_add_graph_metrics" href="#lang_main.analysis.graphs.pipe_add_graph_metrics">pipe_add_graph_metrics</a></code></li>
<li><code><a title="lang_main.analysis.graphs.pipe_rescale_graph_edge_weights" href="#lang_main.analysis.graphs.pipe_rescale_graph_edge_weights">pipe_rescale_graph_edge_weights</a></code></li>
<li><code><a title="lang_main.analysis.graphs.rescale_edge_weights" href="#lang_main.analysis.graphs.rescale_edge_weights">rescale_edge_weights</a></code></li>
<li><code><a title="lang_main.analysis.graphs.save_to_GraphML" href="#lang_main.analysis.graphs.save_to_GraphML">save_to_GraphML</a></code></li>
<li><code><a title="lang_main.analysis.graphs.static_graph_analysis" href="#lang_main.analysis.graphs.static_graph_analysis">static_graph_analysis</a></code></li>
<li><code><a title="lang_main.analysis.graphs.update_graph" href="#lang_main.analysis.graphs.update_graph">update_graph</a></code></li>
<li><code><a title="lang_main.analysis.graphs.verify_non_empty_graph" href="#lang_main.analysis.graphs.verify_non_empty_graph">verify_non_empty_graph</a></code></li>
<li><code><a title="lang_main.analysis.graphs.verify_property" href="#lang_main.analysis.graphs.verify_property">verify_property</a></code></li>
<li><code><a title="lang_main.analysis.graphs.weight_scaling" href="#lang_main.analysis.graphs.weight_scaling">weight_scaling</a></code></li>
</ul>
</li>
<li><h3><a href="#header-classes">Classes</a></h3>
<ul>
<li>
<h4><code><a title="lang_main.analysis.graphs.TokenGraph" href="#lang_main.analysis.graphs.TokenGraph">TokenGraph</a></code></h4>
<ul class="">
<li><code><a title="lang_main.analysis.graphs.TokenGraph.copy" href="#lang_main.analysis.graphs.TokenGraph.copy">copy</a></code></li>
<li><code><a title="lang_main.analysis.graphs.TokenGraph.directed" href="#lang_main.analysis.graphs.TokenGraph.directed">directed</a></code></li>
<li><code><a title="lang_main.analysis.graphs.TokenGraph.disable_logging" href="#lang_main.analysis.graphs.TokenGraph.disable_logging">disable_logging</a></code></li>
<li><code><a title="lang_main.analysis.graphs.TokenGraph.from_file" href="#lang_main.analysis.graphs.TokenGraph.from_file">from_file</a></code></li>
<li><code><a title="lang_main.analysis.graphs.TokenGraph.metadata_directed" href="#lang_main.analysis.graphs.TokenGraph.metadata_directed">metadata_directed</a></code></li>
<li><code><a title="lang_main.analysis.graphs.TokenGraph.metadata_undirected" href="#lang_main.analysis.graphs.TokenGraph.metadata_undirected">metadata_undirected</a></code></li>
<li><code><a title="lang_main.analysis.graphs.TokenGraph.name" href="#lang_main.analysis.graphs.TokenGraph.name">name</a></code></li>
<li><code><a title="lang_main.analysis.graphs.TokenGraph.perform_static_analysis" href="#lang_main.analysis.graphs.TokenGraph.perform_static_analysis">perform_static_analysis</a></code></li>
<li><code><a title="lang_main.analysis.graphs.TokenGraph.rescale_edge_weights" href="#lang_main.analysis.graphs.TokenGraph.rescale_edge_weights">rescale_edge_weights</a></code></li>
<li><code><a title="lang_main.analysis.graphs.TokenGraph.to_GraphML" href="#lang_main.analysis.graphs.TokenGraph.to_GraphML">to_GraphML</a></code></li>
<li><code><a title="lang_main.analysis.graphs.TokenGraph.to_pickle" href="#lang_main.analysis.graphs.TokenGraph.to_pickle">to_pickle</a></code></li>
<li><code><a title="lang_main.analysis.graphs.TokenGraph.to_undirected" href="#lang_main.analysis.graphs.TokenGraph.to_undirected">to_undirected</a></code></li>
<li><code><a title="lang_main.analysis.graphs.TokenGraph.undirected" href="#lang_main.analysis.graphs.TokenGraph.undirected">undirected</a></code></li>
<li><code><a title="lang_main.analysis.graphs.TokenGraph.update_metadata" href="#lang_main.analysis.graphs.TokenGraph.update_metadata">update_metadata</a></code></li>
</ul>
</li>
</ul>
</li>
</ul>
</nav>
</main>
<footer id="footer">
<p>Generated by <a href="https://pdoc3.github.io/pdoc" title="pdoc: Python API documentation generator"><cite>pdoc</cite> 0.11.5</a>.</p>
</footer>
</body>
</html>