1845 lines
81 KiB
HTML
1845 lines
81 KiB
HTML
<!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,
|
||
) -> None:
|
||
"""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 'None'
|
||
property_name : str, optional
|
||
target name for the property containing the betweenness centrality in nodes,
|
||
by default PROPERTY_NAME_BETWEENNESS_CENTRALITY
|
||
"""
|
||
|
||
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> : <code>DiGraph | Graph</code></dt>
|
||
<dd>Graph with betweenness centrality as node property added inplace</dd>
|
||
<dt><strong><code>edge_weight_property</code></strong> : <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> : <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,
|
||
) -> None:
|
||
"""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
|
||
"""
|
||
# 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'Node data does not contain weighted degree '
|
||
f'with name {property_name_weighted_degree}.'
|
||
)
|
||
)
|
||
elif property_name_betweenness not in node_data:
|
||
raise NodePropertyNotContainedError(
|
||
(
|
||
f'Node data does not contain betweenness centrality '
|
||
f'with name {property_name_betweenness}.'
|
||
)
|
||
)
|
||
|
||
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> : <code>DiGraph | Graph</code></dt>
|
||
<dd>Graph with weighted degree as node property added inplace</dd>
|
||
<dt><strong><code>property_name</code></strong> : <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> : <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 = 'weight',
|
||
property_name: str = PROPERTY_NAME_DEGREE_WEIGHTED,
|
||
) -> None:
|
||
"""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 'weight'
|
||
property_name : str, optional
|
||
target name for the property containing the weighted degree in nodes,
|
||
by default PROPERTY_NAME_DEGREE_WEIGHTED
|
||
"""
|
||
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> : <code>DiGraph | Graph</code></dt>
|
||
<dd>Graph with weighted degree as node property added inplace</dd>
|
||
<dt><strong><code>edge_weight_property</code></strong> : <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> : <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,
|
||
) -> tuple[list[CytoscapeData], WeightData]:
|
||
cyto_data: list[CytoscapeData] = []
|
||
# iterate over nodes
|
||
nodes = cast(Iterable[NodeTitle], graph.nodes)
|
||
for node in nodes:
|
||
node_data: CytoscapeData = {
|
||
'data': {
|
||
'id': node,
|
||
'label': node,
|
||
}
|
||
}
|
||
cyto_data.append(node_data)
|
||
# iterate over edges
|
||
weights: set[int] = set()
|
||
|
||
edges = cast(
|
||
Iterable[
|
||
tuple[
|
||
NodeTitle,
|
||
NodeTitle,
|
||
EdgeWeight,
|
||
]
|
||
],
|
||
graph.edges.data('weight', default=1), # type: ignore
|
||
)
|
||
for source, target, weight in edges:
|
||
weights.add(weight)
|
||
edge_data: CytoscapeData = {
|
||
'data': {
|
||
'source': source,
|
||
'target': target,
|
||
'weight': 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 = {'min': min_weight, 'max': 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,
|
||
) -> 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('Successfully converted graph to one with undirected edges.')
|
||
_ = 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 = 'weight',
|
||
) -> TokenGraph:
|
||
"""filters all edges which are within the provided bounds
|
||
inclusive limits: bound_lower <= edge_weight <= 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
|
||
"""
|
||
original_graph_edges = copy.deepcopy(graph.edges)
|
||
filtered_graph = graph.copy()
|
||
|
||
if not any((bound_lower, bound_upper)):
|
||
logger.warning('No bounds provided, returning original graph.')
|
||
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 < bound_lower:
|
||
filtered_graph.remove_edge(edge[0], edge[1])
|
||
if bound_upper is not None and weight > 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 <= edge_weight <= bound_upper are retained</p>
|
||
<h2 id="parameters">Parameters</h2>
|
||
<dl>
|
||
<dt><strong><code>bound_lower</code></strong> : <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> : <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,
|
||
) -> TokenGraph:
|
||
"""filters all nodes which are within the provided bounds by their degree,
|
||
inclusive limits: bound_lower <= node_degree <= 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
|
||
"""
|
||
# 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('No bounds provided, returning original graph.')
|
||
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 < bound_lower:
|
||
filtered_graph.remove_node(node)
|
||
if bound_upper is not None and degree > 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 <= node_degree <= bound_upper are retained</p>
|
||
<h2 id="parameters">Parameters</h2>
|
||
<dl>
|
||
<dt><strong><code>bound_lower</code></strong> : <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> : <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 = 'weight',
|
||
descending: bool = True,
|
||
) -> 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,
|
||
) -> 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]]['weight'])
|
||
if weight < min_edge_weight:
|
||
min_edge_weight = weight
|
||
if weight > 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('Graph properties: %d Nodes, %d Edges', num_nodes, num_edges)
|
||
logger.info('Node memory: %.2f KB', (node_mem / 1024))
|
||
logger.info('Edge memory: %.2f KB', (edge_mem / 1024))
|
||
logger.info('Total memory: %.2f KB', (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],
|
||
) -> npt.NDArray[np.float32]:
|
||
"""apply standard linear normalisation
|
||
|
||
Parameters
|
||
----------
|
||
array : npt.NDArray[np.float_]
|
||
array which shall be normalised
|
||
|
||
Returns
|
||
-------
|
||
npt.NDArray[np.float32]
|
||
min/max normalised array
|
||
"""
|
||
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> : <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,
|
||
) -> 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,
|
||
) -> tuple[TokenGraph, Graph]:
|
||
"""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
|
||
"""
|
||
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> : <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 = 'weight',
|
||
) -> 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['weight'] 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]['weight'] = 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,
|
||
) -> None:
|
||
if filename is not None:
|
||
saving_path = saving_path.joinpath(filename)
|
||
saving_path = saving_path.with_suffix('.graphml')
|
||
nx.write_graphml(G=graph, path=saving_path)
|
||
logger.info('Successfully saved graph as GraphML file under %s.', 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,
|
||
) -> tuple[TokenGraph]:
|
||
"""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
|
||
"""
|
||
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> : <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,
|
||
) -> 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]['weight'] += 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,
|
||
) -> None:
|
||
"""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
|
||
"""
|
||
if not tuple(graph.nodes):
|
||
raise EmptyGraphError(f'Graph object >>{graph}<< does not contain any nodes.')
|
||
elif including_edges and not tuple(graph.edges):
|
||
raise EmptyEdgesError(f'Graph object >>{graph}<< does not contain any edges.')</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> : <code>DiGraph | Graph</code></dt>
|
||
<dd>graph to check for emptiness</dd>
|
||
<dt><strong><code>including_edges</code></strong> : <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,
|
||
) -> None:
|
||
for node_1, node_2 in graph.edges:
|
||
if property not in graph[node_1][node_2]:
|
||
raise EdgePropertyNotContainedError(
|
||
(
|
||
f'Edge property >>{property}<< not '
|
||
f'available for edge >>({node_1}, {node_2})<<'
|
||
)
|
||
)</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,
|
||
) -> npt.NDArray[np.float32]:
|
||
"""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
|
||
"""
|
||
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> : <code>npt.NDArray[np.float32]</code></dt>
|
||
<dd>pre-normalised edge weights as 1D array</dd>
|
||
<dt><strong><code>a</code></strong> : <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> : <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 = 'TokenGraph',
|
||
enable_logging: bool = True,
|
||
incoming_graph_data: Any | None = None,
|
||
**attr,
|
||
) -> 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) -> str:
|
||
return self.__str__()
|
||
|
||
def __str__(self) -> str:
|
||
return (
|
||
f'TokenGraph(name: {self.name}, number of nodes: '
|
||
f'{len(self.nodes)}, number of edges: '
|
||
f'{len(self.edges)})'
|
||
)
|
||
|
||
def disable_logging(self) -> None:
|
||
self.logging = False
|
||
|
||
# !! only used to verify that saving was done correctly
|
||
"""
|
||
def __key(self) -> tuple[Hashable, ...]:
|
||
return (self.name, tuple(self.nodes), tuple(self.edges))
|
||
|
||
def __hash__(self) -> int:
|
||
return hash(self.__key())
|
||
"""
|
||
|
||
def copy(self) -> Self:
|
||
"""returns a (deep) copy of the graph
|
||
|
||
Returns
|
||
-------
|
||
Self
|
||
deep copy of the graph
|
||
"""
|
||
return copy.deepcopy(self)
|
||
|
||
@property
|
||
def name(self) -> str:
|
||
return self._name
|
||
|
||
@property
|
||
def directed(self) -> Self:
|
||
return self._directed
|
||
|
||
@property
|
||
def undirected(self) -> Graph:
|
||
if self._undirected is None:
|
||
self._undirected = self.to_undirected(inplace=False, logging=False)
|
||
|
||
return self._undirected
|
||
|
||
@property
|
||
def metadata_directed(self) -> dict[str, float]:
|
||
return self._metadata_directed
|
||
|
||
@property
|
||
def metadata_undirected(self) -> dict[str, float]:
|
||
return self._metadata_undirected
|
||
|
||
@overload
|
||
def to_undirected(
|
||
self,
|
||
inplace: Literal[True] = ...,
|
||
logging: bool | None = ...,
|
||
) -> None: ...
|
||
|
||
@overload
|
||
def to_undirected(
|
||
self,
|
||
inplace: Literal[False],
|
||
logging: bool | None = ...,
|
||
) -> Graph: ...
|
||
|
||
def to_undirected(
|
||
self,
|
||
inplace: bool = True,
|
||
logging: bool | None = None,
|
||
) -> 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,
|
||
) -> 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,
|
||
) -> tuple[TokenGraph, Graph]:
|
||
"""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
|
||
"""
|
||
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) -> None:
|
||
"""calculate different metrics directly on the data of the underlying graphs
|
||
(directed and undirected)
|
||
|
||
Current operations:
|
||
- adding weighted degree
|
||
"""
|
||
add_weighted_degree(self)
|
||
add_weighted_degree(self.undirected)
|
||
|
||
def _save_prepare(
|
||
self,
|
||
path: Path,
|
||
filename: str | None = None,
|
||
) -> Path:
|
||
if filename is not None:
|
||
saving_path = path.joinpath(f'{filename}')
|
||
else:
|
||
saving_path = path.joinpath(f'{self.name}')
|
||
|
||
return saving_path
|
||
|
||
def to_GraphML(
|
||
self,
|
||
path: Path,
|
||
filename: str | None = None,
|
||
directed: bool = False,
|
||
) -> None:
|
||
"""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
|
||
"""
|
||
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,
|
||
) -> None:
|
||
"""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
|
||
"""
|
||
saving_path = self._save_prepare(path=path, filename=filename)
|
||
saving_path = saving_path.with_suffix('.pkl')
|
||
save_pickle(obj=self, path=saving_path)
|
||
|
||
@classmethod
|
||
def from_file(
|
||
cls,
|
||
path: Path,
|
||
node_type_graphml: type = str,
|
||
) -> Self:
|
||
# !! no validity checks for pickle files
|
||
# !! GraphML files not correct because not all properties
|
||
# !! are parsed correctly
|
||
# TODO REWORK
|
||
match path.suffix:
|
||
case '.graphml':
|
||
graph = typing.cast(Self, nx.read_graphml(path, node_type=node_type_graphml))
|
||
logger.info('Successfully loaded graph from GraphML file %s.', path)
|
||
case '.pkl' | '.pickle':
|
||
graph = typing.cast(Self, load_pickle(path))
|
||
logger.info('Successfully loaded graph from pickle file %s.', path)
|
||
case _:
|
||
raise ValueError('File format not supported.')
|
||
|
||
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> : <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> : <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">>>> 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">>>> 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">>>> G.add_nodes_from([2, 3])
|
||
>>> G.add_nodes_from(range(100, 110))
|
||
>>> H = nx.path_graph(10)
|
||
>>> 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">>>> 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">>>> G.add_edge(1, 2)
|
||
</code></pre>
|
||
<p>a list of edges,</p>
|
||
<pre><code class="language-python-repl">>>> G.add_edges_from([(1, 2), (1, 3)])
|
||
</code></pre>
|
||
<p>or a collection of edges,</p>
|
||
<pre><code class="language-python-repl">>>> 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">>>> G = nx.DiGraph(day="Friday")
|
||
>>> 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">>>> G.add_node(1, time="5pm")
|
||
>>> G.add_nodes_from([3], time="2pm")
|
||
>>> G.nodes[1]
|
||
{'time': '5pm'}
|
||
>>> G.nodes[1]["room"] = 714
|
||
>>> del G.nodes[1]["room"] # remove attribute
|
||
>>> 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">>>> G.add_edge(1, 2, weight=4.7)
|
||
>>> G.add_edges_from([(3, 4), (4, 5)], color="red")
|
||
>>> G.add_edges_from([(1, 2, {"color": "blue"}), (2, 3, {"weight": 8})])
|
||
>>> G[1][2]["weight"] = 4.7
|
||
>>> G.edges[1, 2]["weight"] = 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">>>> 1 in G # check if node in graph
|
||
True
|
||
>>> [n for n in G if n < 3] # iterate through nodes
|
||
[1, 2]
|
||
>>> 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">>>> for n, nbrsdict in G.adjacency():
|
||
... for nbr, eattr in nbrsdict.items():
|
||
... if "weight" 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">>>> for u, v, weight in G.edges(data="weight"):
|
||
... 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">>>> class ThinGraph(nx.Graph):
|
||
... all_edge_dict = {"weight": 1}
|
||
...
|
||
... def single_edge_dict(self):
|
||
... return self.all_edge_dict
|
||
...
|
||
... edge_attr_dict_factory = single_edge_dict
|
||
>>> G = ThinGraph()
|
||
>>> G.add_edge(2, 1)
|
||
>>> G[2][1]
|
||
{'weight': 1}
|
||
>>> G.add_edge(2, 2)
|
||
>>> 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> : <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> : <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">>>> G = nx.Graph() # or DiGraph, MultiGraph, MultiDiGraph, etc
|
||
>>> G = nx.Graph(name="my graph")
|
||
>>> e = [(1, 2), (2, 3), (3, 4)] # list of edges
|
||
>>> G = nx.Graph(e)
|
||
</code></pre>
|
||
<p>Arbitrary graph attribute pairs (key=value) may be assigned</p>
|
||
<pre><code class="language-python-repl">>>> G = nx.Graph(e, day="Friday")
|
||
>>> 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) -> 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) -> 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) -> 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) -> 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) -> 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) -> Self:
|
||
"""returns a (deep) copy of the graph
|
||
|
||
Returns
|
||
-------
|
||
Self
|
||
deep copy of the graph
|
||
"""
|
||
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) -> 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) -> None:
|
||
"""calculate different metrics directly on the data of the underlying graphs
|
||
(directed and undirected)
|
||
|
||
Current operations:
|
||
- adding weighted degree
|
||
"""
|
||
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,
|
||
) -> tuple[TokenGraph, Graph]:
|
||
"""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
|
||
"""
|
||
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,
|
||
) -> None:
|
||
"""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
|
||
"""
|
||
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> : <code>Path</code></dt>
|
||
<dd>target path for saving the file</dd>
|
||
<dt><strong><code>filename</code></strong> : <code>str | None</code>, optional</dt>
|
||
<dd>filename to be given, by default None</dd>
|
||
<dt><strong><code>directed</code></strong> : <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,
|
||
) -> None:
|
||
"""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
|
||
"""
|
||
saving_path = self._save_prepare(path=path, filename=filename)
|
||
saving_path = saving_path.with_suffix('.pkl')
|
||
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> : <code>Path</code></dt>
|
||
<dd>target path for saving the file</dd>
|
||
<dt><strong><code>filename</code></strong> : <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,
|
||
) -> 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> : <code>bool (optional)</code></dt>
|
||
<dd> </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> : <code>bool (optional</code>, default=<code>False)</code></dt>
|
||
<dd> </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> : <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">>>> G = nx.path_graph(2) # or MultiGraph, etc
|
||
>>> H = G.to_directed()
|
||
>>> list(H.edges)
|
||
[(0, 1), (1, 0)]
|
||
>>> G2 = H.to_undirected()
|
||
>>> 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,
|
||
) -> 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>
|