Skip to content

Commit

Permalink
Merge pull request #753 from krebslw/grafanalyse
Browse files Browse the repository at this point in the history
Ændringer til netværksanalyse i fire niv
  • Loading branch information
kbevers authored May 24, 2024
2 parents 56d2c7d + 4ee1f17 commit eab18b4
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 109 deletions.
172 changes: 63 additions & 109 deletions fire/cli/niv/_netoversigt.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from collections import Counter
from collections import Counter, deque
from typing import Dict, List, Set, Tuple

import click
Expand Down Expand Up @@ -108,60 +108,39 @@ def netgraf(
net[til].add(fra)

# Undersøg om der er nettet består af flere ikke-sammenhængende subnet.
# Skriv advarsel hvis ikke der er mindste et fastholdt punkt i hvert
# subnet.
subnet = analyser_subnet(net)
if len(subnet) > 1:
fastholdte_i_subnet = [None for _ in subnet]
for i, subnet_ in enumerate(subnet):
for subnetpunkt in subnet_:
if subnetpunkt in fastholdte_punkter:
fastholdte_i_subnet[i] = subnetpunkt
continue

if None in fastholdte_i_subnet:
fire.cli.print(
"ADVARSEL: Manglende fastholdt punkt i mindst et subnet! Forslag til fastholdte punkter i hvert subnet:",
bg="yellow",
fg="black",
)
for i, subnet_ in enumerate(subnet):
if fastholdte_i_subnet[i]:
fire.cli.print(
f" Subnet {i}: {fastholdte_i_subnet[i]}", fg="green"
)
else:
fire.cli.print(f" Subnet {i}: {subnet_[0]}", fg="red")

# Tilføj forbindelser fra alle fastholdte punkter til det første fastholdte punkt
udgangspunkt = fastholdte_punkter[0]
for punkt in fastholdte_punkter:
if punkt != udgangspunkt:
net[udgangspunkt].add(punkt)
net[punkt].add(udgangspunkt)

# Analysér netgraf
forbundne_punkter = set()
ensomme_punkter = set()
for punkt in alle_punkter:
if path_to_origin(net, udgangspunkt, punkt) is None:
ensomme_punkter.add(punkt)
else:
forbundne_punkter.add(punkt)

# Vi vil ikke have de kunstige forbindelser mellem fastholdte punkter med
# i output, så nu genopbygger vi nettet uden dem
net = {}
for punkt in alle_punkter:
net[punkt] = set()
for fra, til in zip(observationer["Fra"], observationer["Til"]):
net[fra].add(til)
net[til].add(fra)
fastholdte_i_subnet = [None for _ in subnet]
for i, subnet_ in enumerate(subnet):
for punkt in fastholdte_punkter:
if punkt in subnet_:
fastholdte_i_subnet[i] = punkt
forbundne_punkter.update(subnet_)
break
else:
ensomme_punkter.update(subnet_)

# De ensomme punkter skal heller ikke med i netgrafen
# De ensomme punkter skal ikke med i netgrafen
for punkt in ensomme_punkter:
net.pop(punkt, None)

# Skriv advarsel hvis ikke der er mindste et fastholdt punkt i hvert
# subnet.

if None in fastholdte_i_subnet:
fire.cli.print(
"ADVARSEL: Manglende fastholdt punkt i mindst et subnet! Forslag til fastholdte punkter i hvert subnet:",
bg="yellow",
fg="black",
)
for i, subnet_ in enumerate(subnet):
if fastholdte_i_subnet[i]:
fire.cli.print(f" Subnet {i}: {fastholdte_i_subnet[i]}", fg="green")
else:
fire.cli.print(f" Subnet {i}: {subnet_[0]}", fg="red")

# Nu kommer der noget grimt...
# Tving alle rækker til at være lige lange, så vi kan lave en dataframe af dem
max_antal_naboer = max([len(net[e]) for e in net])
Expand All @@ -181,72 +160,47 @@ def netgraf(
return netf, ensomme


# ------------------------------------------------------------------------------
# path_to_origin - eksempel:
#
# graph = {
# 'A': {'B', 'C'},
# 'B': {'C', 'D'},
# 'C': {'D'},
# 'D': {'C'},
# 'E': {'F'},
# 'F': {'C'},
# 'G': {}
# }
#
# print (path_to_origin (graph, 'A', 'C'))
# print (path_to_origin (graph, 'A', 'G'))
# ------------------------------------------------------------------------------
def path_to_origin(
graph: Dict[str, Set[str]], start: str, origin: str, path: List[str] = []
):
"""
Mikroskopisk backtracking netkonnektivitetstest. Baseret på et
essay af Pythonstifteren Guido van Rossum, publiceret 1998 på
https://www.python.org/doc/essays/graphs/. Koden er her
moderniseret fra Python 1.5 til 3.7 og modificeret til at
arbejde på dict-over-set (originalen brugte dict-over-list)
"""
path = path + [start]
if start == origin:
return path
if start not in graph:
return None
for node in graph[start]:
if node not in path:
newpath = path_to_origin(graph, node, origin, path)
if newpath:
return newpath
return None


def analyser_subnet(net):
def analyser_subnet(net: dict[set]) -> list[list]:
"""
Find selvstændige net i et større net
Med inspiration fra https://www.geeksforgeeks.org/connected-components-in-an-undirected-graph/
Bruger breadth first search (BFS).
Baseret på materiale fra: https://www.geeksforgeeks.org/breadth-first-search-or-bfs-for-a-graph/
"""

def depth_first_search(net, visited, vertex, subnet):
visited[vertex] = True
subnet.append(vertex)

for adjacent_vertex in net[vertex]:
if not visited[adjacent_vertex]:
net, visited, vertex, subnet = depth_first_search(
net, visited, adjacent_vertex, subnet
)

return net, visited, vertex, subnet

visited = {vertex: False for vertex in net.keys()}
connected_vertices = []
for vertex in net.keys():
if not visited[vertex]:
# Funktion til breadth first search
def bfs(net, besøgt, startpunkt, subnet=[]):
# Initialiser kø
= deque()

# Marker nuværende punkt som besøgt og føj til kø
besøgt[startpunkt] = True
.append(startpunkt)

# Opbyg subnet
subnet.append(startpunkt)

# Loop over køen
while :
# Fjern nuværende punkt fra køen
nuværende_punkt = .popleft()

# Find naboer til nuværende punkt
# Hvis en nabo ikke har været besøgt, marker den da som besøgt og føj til kø
for nabo in net[nuværende_punkt]:
if not besøgt[nabo]:
besøgt[nabo] = True
.append(nabo)

# Opbyg subnet
subnet.append(nabo)

besøgt = {punkt: False for punkt in net.keys()}
liste_af_subnet = []
for punkt in net.keys():
if not besøgt[punkt]:
subnet = []
net, visited, vertex, subnet = depth_first_search(
net, visited, vertex, subnet
)
connected_vertices.append(subnet)
bfs(net, besøgt, punkt, subnet)
liste_af_subnet.append(subnet)

return connected_vertices
return liste_af_subnet
99 changes: 99 additions & 0 deletions test/api/niv/test_netanalyse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import pandas as pd

from fire.cli.niv._netoversigt import (
analyser_subnet,
netgraf,
)


def test_analyser_subnet():
"""Test at analyser_subnet finder det korrekte antal subnet"""

disjunkt_net = {}
subnet = [i for i in range(3)]
for N_subnet in range(1, 5):
disjunkt_net.update({node: list(set(subnet) - {node}) for node in subnet})

liste_af_subnet = analyser_subnet(disjunkt_net)

assert len(liste_af_subnet) == N_subnet

# lav et kopi af disjunkt net og forbind alle subnet.
forbundet_net = {k: v for (k, v) in disjunkt_net.items()}

# Forbind punkt "0" til første punkt i hvert subnet for at forbinde alle subnet
forbundet_net[0] = list(
set(forbundet_net[0] + [subn[0] for subn in liste_af_subnet])
)

liste_af_subnet = analyser_subnet(forbundet_net)
assert len(liste_af_subnet) == 1

subnet = [i + 100 for i in subnet]


def test_netgraf():
"""
Test at netgraf returnerer det forventede antal punkter
Der testes for antal forbundne punkter, antal singulære punkter, og antal
kolonner i Netgeometri-dataframen (svarende til højeste antal naboer + 1).
"""
N = 20
alle_punkter = tuple(str(i) for i in range(N))
fastholdte = (
"3",
"13",
)

# Observationer opbygges som en symmetrisk, rettet, cirkulær graf, for at
# efterligne en lukket nivellementspolygon.
obs = [
{"Fra": str(i % N), "Til": str(j % N)} for i in range(N) for j in (i + 1, i - 1)
]
observationer = pd.DataFrame.from_records(obs)
(net, singulære) = netgraf(observationer, alle_punkter, fastholdte)

# Check ingen singulære
assert len(net) == N
assert len(singulære) == 0
assert len(net.keys()) == 3

# Tilføj disjunkte net (singulære punkter)
obs.append({"Fra": "RMV", "Til": "SKG"})
obs.append({"Fra": "SKG", "Til": "RMV"})
obs.append({"Fra": "Hjemme", "Til": "Ude"})
obs.append({"Fra": "Ude", "Til": "Hjemme"})

# ... og tilføj dem til alle_punkter
alle_punkter += ("RMV", "SKG", "Hjemme", "Ude")

observationer = pd.DataFrame.from_records(obs)
(net, singulære) = netgraf(observationer, alle_punkter, fastholdte)

assert len(net) == N
assert len(singulære) == 4
assert len(net.keys()) == 3

# Fasthold nyt punkt
fastholdte += ("RMV",)
(net, singulære) = netgraf(observationer, alle_punkter, fastholdte)

assert len(net) == N + 2
assert len(singulære) == 2
assert len(net.keys()) == 3

# Fra-Til har ikke en tilsvarende modsatrettet observation
obs.append({"Fra": "SKG", "Til": "Nord"})
obs.append({"Fra": "SKG", "Til": "Syd"})
obs.append({"Fra": "SKG", "Til": "Øst"})
obs.append({"Fra": "SKG", "Til": "Vest"})

alle_punkter += ("Nord", "Syd", "Øst", "Vest")

observationer = pd.DataFrame.from_records(obs)
(net, singulære) = netgraf(observationer, alle_punkter, fastholdte)

assert len(net) == N + 6
assert len(singulære) == 2
assert len(net.keys()) == 6

0 comments on commit eab18b4

Please sign in to comment.