Skip to content

Commit 4a5d2cb

Browse files
committed
fixup! Add graph visualizations to anomaly detection
1 parent 48febf2 commit 4a5d2cb

File tree

6 files changed

+86
-38
lines changed

6 files changed

+86
-38
lines changed

domains/anomaly-detection/graphs/TopAuthority.cypher

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,55 +17,69 @@
1717
,coalesce(central.fqn, central.globalFqn, central.fileName, central.signature, central.name) AS targetName
1818
,[] AS graphVizOutput
1919
WITH *, graphVizOutput + ["graph [label=\"" + graphLabel + targetName + "\\n\\n\"];"] AS graphVizOutput
20-
WITH *, "🏛️ authority #" + central.anomalyAuthorityRank + "\\n" + central.name AS centralNodeLabel
20+
WITH *, "🏛️ authority #" + central.anomalyAuthorityRank + "\\n" + central.name AS centralNodeLabel
2121
WITH *, graphVizOutput + ["central [label=\"" + centralNodeLabel + "\"];"] AS graphVizOutput
2222
// Step 3: Query direct incoming dependencies to the central node
2323
MATCH (source)-[dependency:DEPENDS_ON]->(central)
2424
WHERE $projection_node_label IN labels(source)
2525
AND source.outgoingDependencies > 0
2626
ORDER BY dependency.weight DESC, source.name ASC
27-
LIMIT 60
28-
WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight
29-
WITH *, round((toFloat(weight) / toFloat(maxWeight) * 2.5) + 0.4, 1.0) AS penWidth
30-
WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=" + penWidth AS edgeAttributes
31-
WITH *, CASE WHEN source.centralityPageRankToArticleRankDifference > pageToArticleRankThreshold THEN 5 ELSE 2 END AS scaledNodeBorder
32-
WITH *, round(source.centralityPageRankNormalized * 0.66 + 0.2, 3) AS scaledNodeSize
33-
WITH *, coalesce(scaledNodeSize, 0.5) AS scaledNodeSize
34-
WITH *, "penwidth = " + scaledNodeBorder + "; " AS directInBorder
35-
WITH *, "height = " + scaledNodeSize + "; " AS directInSize
36-
WITH *, "\"" + source.name + "\" [" + directInBorder + directInSize + "]; " AS directInNode
27+
LIMIT 40
28+
WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight
29+
WITH *, round((toFloat(weight) / toFloat(maxWeight) * 2.5) + 0.4, 1.0) AS penWidth
30+
WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=" + penWidth AS edgeAttributes
31+
WITH *, CASE WHEN source.centralityPageRankToArticleRankDifference > pageToArticleRankThreshold
32+
THEN 5 ELSE 2 END AS scaledNodeBorder
33+
WITH *, round(source.centralityPageRankNormalized * 1.0 + 1.0, 3) AS scaledNodeSize
34+
WITH *, round(source.centralityPageRankNormalized * 100.0, 2) + "%" AS labelValue
35+
// Add the last part of the element id to make the node name unique, even if the name itself isn't.
36+
WITH *, source.name + "_" + split(elementId(source), ':')[-1] AS sourceId
37+
WITH *, "penwidth = " + scaledNodeBorder + "; " AS directInBorder
38+
WITH *, "height = " + scaledNodeSize + "; " AS directInSize
39+
// Split long names like inner classes identified by a dollar sign ($)
40+
WITH *, replace(source.name, '$', '$\\n') AS sourceNameSplit
41+
WITH *, "label = \"" + sourceNameSplit + "\\n(" + labelValue + ")\"; " AS directInLabel
42+
WITH *, " [" + directInLabel + directInBorder + directInSize + "]; " AS directInNodeProperties
43+
WITH *, "\"" + sourceId + "\" " + directInNodeProperties AS directInNode
3744
WITH maxWeight
3845
,pageToArticleRankThreshold
3946
,central
4047
,graphVizOutput
4148
,collect(source) AS incomingDependencyNodes
42-
,collect(directInNode + "\"" + source.name + "\" -> central [" + edgeAttributes + "];") AS directInEdges
49+
,collect(directInNode + "\"" + sourceId + "\" -> central [" + edgeAttributes + "];") AS directInEdges
4350
WITH *, graphVizOutput + directInEdges AS graphVizOutput
4451
// Step 4: Query direct outgoing dependencies from the central node
4552
MATCH (source)<-[dependency:DEPENDS_ON]-(central)
4653
WHERE $projection_node_label IN labels(source)
4754
AND source.incomingDependencies > 0
4855
ORDER BY dependency.weight DESC, source.name ASC
49-
LIMIT 60
56+
LIMIT 40
5057
WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight
5158
WITH *, round((toFloat(weight) / toFloat(maxWeight) * 2.5) + 0.4, 1.0) AS penWidth
5259
WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=" + penWidth AS edgeAttributes
5360
// Use a lighter color for the target nodes of outgoing dependencies from the central node and their edges
5461
WITH *, edgeAttributes + "; color = 5" AS edgeAttributes
55-
WITH *, CASE WHEN source.centralityPageRankToArticleRankDifference > pageToArticleRankThreshold THEN 5 ELSE 2 END AS scaledNodeBorder
56-
WITH *, round(source.centralityPageRankNormalized * 0.66 + 0.2, 3) AS scaledNodeSize
57-
WITH *, coalesce(scaledNodeSize, 0.5) AS scaledNodeSize
62+
WITH *, CASE WHEN source.centralityPageRankToArticleRankDifference > pageToArticleRankThreshold
63+
THEN 5 ELSE 2 END AS scaledNodeBorder
64+
WITH *, round(source.centralityPageRankNormalized * 1.0 + 1.0, 3) AS scaledNodeSize
65+
WITH *, round(source.centralityPageRankNormalized * 100.0, 2) + "%" AS labelValue
66+
// Add the last part of the element id to make the node name unique, even if the name itself isn't.
67+
WITH *, source.name + "_" + split(elementId(source), ':')[-1] AS sourceId
5868
WITH *, "penwidth = " + scaledNodeBorder + "; " AS directOutBorder
5969
WITH *, "height = " + scaledNodeSize + "; " AS directOutSize
6070
WITH *, "color = 5; fillcolor = 1; " AS directOutColors
61-
WITH *, "\"" + source.name + "\" [" + directOutBorder + directOutSize + directOutColors + "]; " AS directOutNode
71+
// Split long names like inner classes identified by a dollar sign ($)
72+
WITH *, replace(source.name, '$', '$\\n') AS sourceNameSplit
73+
WITH *, "label = \"" + sourceNameSplit + "\\n(" + labelValue + ")\"; " AS directOutLabel
74+
WITH *, " [" + directOutLabel + directOutBorder + directOutSize + directOutColors + "]; " AS directOutNodeProperties
75+
WITH *, "\"" + sourceId + "\" " + directOutNodeProperties AS directOutNode
6276
WITH maxWeight
6377
,pageToArticleRankThreshold
6478
,central
6579
,graphVizOutput
6680
,incomingDependencyNodes
6781
,collect(source) AS outgoingDependencyNodes
68-
,collect(directOutNode + "central -> \"" + source.name + "\" [" + edgeAttributes + "];") AS directOutEdges
82+
,collect(directOutNode + "central -> \"" + sourceId + "\" [" + edgeAttributes + "];") AS directOutEdges
6983
WITH *, graphVizOutput + directOutEdges AS graphVizOutput
7084
WITH *, incomingDependencyNodes + outgoingDependencyNodes AS directDependentNodes
7185
// Step 5: Query dependencies between direct dependencies outside the central node
@@ -81,13 +95,15 @@
8195
,directDependentNode
8296
,dependency
8397
,collect(anotherDirectDependentNode)[0] AS firstLinkedDependentNode
84-
LIMIT 120
98+
LIMIT 100
8599
WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight
86100
// Use a fixed small pen width for secondary dependencies for better visibility of the more important direct dependency
87101
WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=0.3" AS edgeAttributes
88102
// Use an even lighter color for secondary dependency edges
89103
WITH *, edgeAttributes + "; color = 3" AS edgeAttributes
90-
WITH *, "\"" + directDependentNode.name + "\" -> \"" + firstLinkedDependentNode.name + "\"" AS directDependenciesEdge
104+
WITH *, directDependentNode.name + "_" + split(elementId(directDependentNode), ':')[-1] AS directDependentNodeId
105+
WITH *, firstLinkedDependentNode.name + "_" + split(elementId(firstLinkedDependentNode), ':')[-1] AS firstLinkedDependentNodeId
106+
WITH *, "\"" + directDependentNodeId + "\" -> \"" + firstLinkedDependentNodeId + "\"" AS directDependenciesEdge
91107
WITH *, collect(directDependenciesEdge + " [" + edgeAttributes + "]") AS directDependenciesEdges
92108
WITH *, graphVizOutput + directDependenciesEdges AS graphVizOutput
93109
UNWIND graphVizOutput AS graphVizOutputLine

domains/anomaly-detection/graphs/TopCentral.template.gv

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
//
55
strict digraph top_central_template {
66
//Begin-Template
7-
graph [layout = "fdp"; start = "7"; splines = "spline"; beautify = true;];
7+
graph [layout = "fdp"; start = "7"; splines = "spline"; beautify = true; pad = "0.8,0.1";];
88
graph [fontname = "Helvetica,Arial,sans-serif"; labelloc = "t";];
99
node [colorscheme = "bugn9"; color = 6; fillcolor = 3;]; # Alternative: color = "0.58 0.75 0.75"; fillcolor = "0.58 0.15 0.99"
1010
edge [colorscheme = "bugn9"; color = 7; ]; # Alternative: color = "0.58 0.75 0.85";
11-
node [fontsize = 10; style = "filled"; margin = "0.001,0.001"];
11+
node [fontsize = 8; style = "filled"; margin = "0.001,0.001"];
1212
edge [fontsize = 4; arrowsize = "0.4";];
1313

1414
central [shape = "doublecircle"; margin = "0.00001,0.00001";];
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// This is a GraphViz dot template file for the visualization of a anomaly archetype graphs with a selected central node.
2+
// The main part of the template is marked by the comments "Begin-Template" and "End-Template".
3+
// It also contains a simple example graph.
4+
//
5+
strict digraph top_central_template {
6+
//Begin-Template
7+
graph [layout = "fdp"; start = "7"; splines = "spline"; beautify = true; pad = "0.8,0.1"];
8+
graph [fontname = "Helvetica,Arial,sans-serif"; labelloc = "t";];
9+
node [colorscheme = "bugn9"; color = 6; fillcolor = 3;];
10+
edge [colorscheme = "bugn9"; color = 7; ];
11+
node [shape = "circle"; fixedsize = true]
12+
node [fontsize = 8; style = "filled";];
13+
edge [fontsize = 4; arrowsize = "0.4";];
14+
15+
central [shape = "doublecircle"; fixedsize = false; margin = "0.000001,0.000001";];
16+
central [fontsize = 16;];
17+
central [color = 7; fillcolor = 5; penwidth = 3;];
18+
19+
limit_hint [color = 7; fillcolor = 5; penwidth = 2;]
20+
limit_hint [shape = "note"; fixedsize = false; fontsize = 10;]
21+
limit_hint [label = "limited\nnode count";]
22+
limit_hint -> central [dir = "back"; arrowtail = "inv"]; // Signals that the number of edges might have been limited
23+
24+
//End-Template
25+
"A" -> "central" [penwidth = 1.0; label = 1;];
26+
"A" -> "B" [penwidth = 3.0; label = 4;];
27+
"B" -> "central" [penwidth = 2.0; label = 2;];
28+
}

domains/anomaly-detection/graphs/TopOutlier.cypher

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
,coalesce(central.fqn, central.globalFqn, central.fileName, central.signature, central.name) AS targetName
1818
,[] AS graphVizOutput
1919
WITH *, graphVizOutput + ["graph [label=\"" + graphLabel + targetName + "\\n\\n\"];"] AS graphVizOutput
20-
WITH *, "🧩\\noutlier #" + central.anomalyOutlierRank + "\\n" + central.name AS centralNodeLabel
20+
WITH *, "🧩 outlier #" + central.anomalyOutlierRank + "\\n" + central.name AS centralNodeLabel
2121
WITH *, graphVizOutput + ["central [label=\"" + centralNodeLabel + "\"];"] AS graphVizOutput
2222
// Step 3: Query direct incoming dependencies to the central node
2323
MATCH (source)-[dependency:DEPENDS_ON]->(central)
@@ -28,14 +28,17 @@
2828
WITH *, coalesce(dependency.weight25PercentInterfaces, dependency.weight, 1) AS weight
2929
WITH *, round((toFloat(weight) / toFloat(maxWeight) * 2.5) + 0.4, 1.0) AS penWidth
3030
WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=" + penWidth AS edgeAttributes
31-
WITH *, CASE WHEN source.clusteringHDBSCANNormalizedDistanceToMedoid > clusterMedoidDistanceThreshold THEN 5 ELSE 2 END AS scaledNodeBorder
32-
WITH *, round((1.0 - source.clusteringHDBSCANProbability) * 1.8 - 0.2, 3) AS scaledNodeSize
31+
WITH *, CASE WHEN source.clusteringHDBSCANNormalizedDistanceToMedoid > clusterMedoidDistanceThreshold
32+
THEN 5 ELSE 2 END AS scaledNodeBorder
33+
WITH *, round((1.0 - source.clusteringHDBSCANProbability) * 1.0 + 1.0, 3) AS scaledNodeSize
3334
WITH *, round(source.clusteringHDBSCANProbability * 100.0, 2) + "%" AS labelValue
34-
WITH *, coalesce(scaledNodeSize, 0.5) AS scaledNodeSize
35+
// Add the last part of the element id to make the node name unique, even if the name itself isn't.
3536
WITH *, source.name + "_" + split(elementId(source), ':')[-1] AS sourceId
3637
WITH *, "penwidth = " + scaledNodeBorder + "; " AS directInBorder
3738
WITH *, "height = " + scaledNodeSize + "; " AS directInSize
38-
WITH *, "label = \"" + source.name + "\\n(" + labelValue + ")\"; " AS directInLabel
39+
// Split long names like inner classes identified by a dollar sign ($)
40+
WITH *, replace(source.name, '$', '$\\n') AS sourceNameSplit
41+
WITH *, "label = \"" + sourceNameSplit + "\\n(" + labelValue + ")\"; " AS directInLabel
3942
WITH *, " [" + directInLabel + directInBorder + directInSize + "]; " AS directInNodeProperties
4043
WITH *, "\"" + sourceId + "\" " + directInNodeProperties AS directInNode
4144
WITH maxWeight
@@ -56,19 +59,21 @@
5659
WITH *, "label=" + weight + "; weight=" + weight + "; penwidth=" + penWidth AS edgeAttributes
5760
// Use a lighter color for the target nodes of outgoing dependencies from the central node and their edges
5861
WITH *, edgeAttributes + "; color = 5" AS edgeAttributes
59-
WITH *, CASE WHEN source.clusteringHDBSCANNormalizedDistanceToMedoid > clusterMedoidDistanceThreshold THEN 5 ELSE 2 END AS scaledNodeBorder
60-
WITH *, round((1.0 - source.clusteringHDBSCANProbability) * 1.8 - 0.2, 3) AS scaledNodeSize
62+
WITH *, CASE WHEN source.clusteringHDBSCANNormalizedDistanceToMedoid > clusterMedoidDistanceThreshold
63+
THEN 5 ELSE 2 END AS scaledNodeBorder
64+
WITH *, round((1.0 - source.clusteringHDBSCANProbability) * 1.0 + 1.0, 3) AS scaledNodeSize
6165
WITH *, round(source.clusteringHDBSCANProbability * 100.0, 2) + "%" AS labelValue
62-
WITH *, coalesce(scaledNodeSize, 0.5) AS scaledNodeSize
66+
// Add the last part of the element id to make the node name unique, even if the name itself isn't.
6367
WITH *, source.name + "_" + split(elementId(source), ':')[-1] AS sourceId
6468
WITH *, "penwidth = " + scaledNodeBorder + "; " AS directOutBorder
6569
WITH *, "height = " + scaledNodeSize + "; " AS directOutSize
6670
WITH *, "color = 5; fillcolor = 1; " AS directOutColors
67-
WITH *, "label = \"" + source.name + "\\n(" + labelValue + ")\"; " AS directOutLabel
71+
// Split long names like inner classes identified by a dollar sign ($)
72+
WITH *, replace(source.name, '$', '$\\n') AS sourceNameSplit
73+
WITH *, "label = \"" + sourceNameSplit + "\\n(" + labelValue + ")\"; " AS directOutLabel
6874
WITH *, " [" + directOutLabel + directOutBorder + directOutSize + directOutColors + "]; " AS directOutNodeProperties
69-
WITH *, "\"" + sourceId + "\" " + directOutNodeProperties AS directOutNode
75+
WITH *, "\"" + sourceId + "\" " + directOutNodeProperties AS directOutNode
7076
WITH maxWeight
71-
,clusterMedoidDistanceThreshold
7277
,central
7378
,graphVizOutput
7479
,incomingDependencyNodes
@@ -83,7 +88,6 @@
8388
AND anotherDirectDependentNode <> directDependentNode
8489
ORDER BY dependency.weight DESC, directDependentNode.name ASC
8590
WITH maxWeight
86-
,clusterMedoidDistanceThreshold
8791
,central
8892
,graphVizOutput
8993
,directDependentNode

domains/anomaly-detection/graphs/TopSized.template.gv

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
//
55
strict digraph top_central_template {
66
//Begin-Template
7-
graph [layout = "fdp"; start = "7"; splines = "spline"; beautify = true;];
7+
graph [layout = "fdp"; start = "7"; splines = "spline"; beautify = true; pad = "0.8,0.1";];
88
graph [fontname = "Helvetica,Arial,sans-serif"; labelloc = "t";];
99
node [colorscheme = "bugn9"; color = 6; fillcolor = 3;]; # Alternative: color = "0.58 0.75 0.75"; fillcolor = "0.58 0.15 0.99"
1010
edge [colorscheme = "bugn9"; color = 7; ]; # Alternative: color = "0.58 0.75 0.85";
1111
node [shape = "circle";]
12-
node [fontsize = 10; style = "filled"; margin = "0.0001,0.0001"];
12+
node [fontsize = 8; style = "filled"; margin = "0.0001,0.0001"];
1313
edge [fontsize = 4; arrowsize = "0.4";];
1414

1515
central [shape = "doublecircle"; margin = "0.000001,0.000001";];

domains/anomaly-detection/graphs/anomalyDetectionGraphVisualization.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,9 @@ anomaly_detection_graph_visualization() {
143143
set_required_features "${@}"
144144
create_graph_visualization "report_name=TopHub" "template_name=TopCentral" "${@}"
145145
create_graph_visualization "report_name=TopBottleneck" "template_name=TopCentral" "${@}"
146-
create_graph_visualization "report_name=TopAuthority" "template_name=TopSized" "${@}"
146+
create_graph_visualization "report_name=TopAuthority" "template_name=TopFixSized" "${@}"
147147
create_graph_visualization "report_name=TopBridge" "template_name=TopCentral" "${@}"
148-
create_graph_visualization "report_name=TopOutlier" "template_name=TopSized" "${@}"
148+
create_graph_visualization "report_name=TopOutlier" "template_name=TopFixSized" "${@}"
149149
}
150150

151151

0 commit comments

Comments
 (0)