The Network Action Set
Example 16.8 Node Similarity in an Undirected Graph
Node Similarity in an Undirected Graph
This section contains PROC CAS code.
Note: Input data must be in a CAS table that is accessible in your CAS session. This table has a two-level name; the first level is your CAS engine libref and the second level is the table name. You refer to this table in the CAS procedure by specifying only the second level. For more information about two-level names, see Chapter 3: Shared Concepts in SAS Visual Data Mining and Machine Learning 8.3: Procedures Guide. For more information about PROC CAS and programming in CASL, see Getting Started with CASL, SAS Cloud Analytic Services: CAS Procedure Programming Guide and Reference, and SAS Viya: System Programming Guide.
This section provides a simple example of calculating node similarity in the undirected graph G that is shown in Figure 16.9.
Figure 9: A Simple Undirected Graph G

The undirected graph G can be represented by the links data set, LinkSetIn, that is created by the following DATA step:
data LinkSetIn; input from $ to $ weight; datalines; A B 1 A D 1 C B 1 C D 1 E B 1 E D 1 E F 1 ;
The following DATA step loads the LinkSetIn data set into a CAS data table named mycas.LinkSetIn. These statements assume that the CAS engine libref is named mycas, but you can substitute any appropriately defined CAS engine libref.
data mycas.LinkSetIn; set LinkSetIn; run;
The following statements find all node pairs that have a similarity score greater than or equal to 0.7:
proc cas;
loadactionset "network";
action nodeSimilarity result=r status=s /
links = {name = "LinkSetIn"}
vector = true
nDimensions = 10
proximityOrder = "second"
minScore = 0.7
outSimilarity = {name = "NodeSim", replace=true};
run;
print r.ProblemSummary; run;
print r.SolutionSummary; run;
action table.fetch / table = "NodeSim" sortBy = {"source","sink"}; run;
quit;
The problem summary output from this action is presented in Output 16.8.1.
Output 16.8.1: Problem Summary
The solution summary output from this action is presented in Output 16.8.2.
Output 16.8.2: Solution Summary
The output data table NodeSim, as shown in Output 16.8.3, contains the pairs of nodes that have either a Jaccard or vector similarity score at or above the specified minScore parameter value of 0.7.
Output 16.8.3: Node Pairs with Similarity Scores Greater Than 0.7
| Selected Rows from Table NODESIM | ||||
|---|---|---|---|---|
| _Index_ | source | sink | jaccard | vector |
| 1 | A | A | 1 | 1 |
| 2 | A | C | 1 | 0.9993204255 |
| 3 | A | E | 0.6666666667 | 0.8037895053 |
| 4 | B | B | 1 | 1 |
| 5 | B | D | 1 | 0.9940500316 |
| 6 | B | F | 0.3333333333 | 0.8331655259 |
| 7 | C | A | 1 | 0.9993204255 |
| 8 | C | C | 1 | 1 |
| 9 | C | E | 0.6666666667 | 0.8097860746 |
| 10 | D | B | 1 | 0.9940500316 |
| 11 | D | D | 1 | 1 |
| 12 | D | F | 0.3333333333 | 0.7726934599 |
| 13 | E | A | 0.6666666667 | 0.8037895053 |
| 14 | E | C | 0.6666666667 | 0.8097860746 |
| 15 | E | E | 1 | 1 |
| 16 | F | B | 0.3333333333 | 0.8331655259 |
| 17 | F | D | 0.3333333333 | 0.7726934599 |
| 18 | F | F | 1 | 1 |
This example illustrates the intuition behind node similarity values. The nodes A and C have the same set of nearest neighbors (see Figure 16.9), B and D, resulting in the Jaccard similarity score of 1 and a vector similarity score very close to 1; thus the (A,C) pair is present in the output. The pair (B,F) appears in the output because the vector similarity score is greater than 0.7, whereas the Jaccard similarity score is lower than the minScore parameter threhold value. In this case, although B and F share only one neighbor out of four total, they do have common neighbors of the nearest neighbors; this results in a greater vector similarity value but not in a greater value of Jaccard similarity, which captures only the similarity of nearest-neighbor structures.
The first-order proximity can be calculated by the following statements:
proc cas;
loadactionset "network";
action nodeSimilarity result=r status=s /
links = {name = "LinkSetIn"}
jaccard = false
vector = true
nDimensions = 10
proximityOrder = "first"
minScore = 0.7
outSimilarity = {name = "NodeSim", replace=true}
outNodes = {name = "NodeVecs", replace=true};
run;
action table.fetch / table = "NodeSim" sortBy = {"source","sink"}; run;
action table.fetch / table = "NodeVecs" sortBy = "node"; run;
quit;
The output data table NodeSim, as shown in Output 16.8.4, contains the pairs of nodes and similarity scores for the first-order proximity. The example also demonstrates the intuition behind similarity based on first-order proximity and how it is different from the Jaccard-based proximity and the vector for the second-order proximity. Intuitively, the first-order vector similarity value between the nodes that are directly connected is higher than the first-order vector similarity value between the nodes that are separated by a longer path in a graph, whereas the Jaccard similarity and second-order vector similarity values are higher between the nodes that share more neighbors. The first-order vector similarity is more closely related to results that are produced by label propagation community detection algorithms, in the sense that the pairs of nodes that have high first-order vector similarity scores between them can be placed in one community.
Output 16.8.4: Node Pairs with Similarity Scores Greater Than 0.7
The output data table NodeVecs contains node-embedding vectors, as shown in Output 16.8.5.
Output 16.8.5: Node-Embedding Vectors
| Selected Rows from Table NODEVECS | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| _Index_ | node | vec_0 | vec_1 | vec_2 | vec_3 | vec_4 | vec_5 | vec_6 | vec_7 | vec_8 | vec_9 |
| 1 | A | -0.070340221 | -0.400010105 | -0.214410412 | -0.32031008 | -0.16938333 | 0.151205169 | -0.09202592 | 0.0249410893 | -0.335962398 | -0.716211227 |
| 2 | B | 0.1625498556 | -0.373941288 | -0.372776841 | 0.055958009 | 0.3697669008 | 0.2621612106 | 0.2837873096 | 0.3557387832 | -0.443461713 | -0.287144414 |
| 3 | C | 0.3693920902 | -0.298292118 | -0.321636119 | 0.0586087847 | 0.1585155527 | 0.4008413282 | 0.2659550706 | 0.5952034098 | -0.132761869 | -0.198143004 |
| 4 | D | 0.0621557611 | -0.395800609 | -0.226534926 | -0.308582922 | -0.24036634 | 0.2573960761 | -0.067743237 | 0.1965312881 | -0.213483577 | -0.692906487 |
| 5 | E | -0.134047062 | 0.4351021199 | 0.321094088 | 0.1830317219 | 0.0098514615 | -0.304418697 | -0.083028316 | -0.316510289 | 0.3228688599 | 0.5933219585 |
| 6 | F | -0.13322379 | 0.4347304518 | 0.3232978095 | 0.1784336972 | 0.0000792602 | -0.304805824 | -0.087913821 | -0.316280448 | 0.3291680825 | 0.5898138807 |
Node Similarity in an Undirected Graph
This section contains Lua code for the analysis in the CASL version of this example, which contains details about the results.
Note: In order to run this code, the data that are described in the CASL version need to be loaded into CAS. One way to do this is to convert the LinkSetIn data to the comma-separated-value (CSV) file LinkSetIn.csv and then use the following code to load the CSV file into CAS:
s:loadtable{casLib="casuser", path="LinkSetIn.csv"}
For more information about coding in Lua, see Getting Started with SAS Viya for Lua and SAS Viya: System Programming Guide.
The following statements find all node pairs that have a similarity score greater than or equal to 0.7:
s:network_nodeSimilarity{
links = {name = "LinkSetIn"},
vector = true,
nDimensions = 10,
proximityOrder = "second",
minScore = 0.7,
outSimilarity = {name = "NodeSim", replace=true}
The first-order proximity can be calculated by the following statements:
s:network_nodeSimilarity{
links = {name = "LinkSetIn"},
jaccard = false,
vector = true,
nDimensions = 10,
proximityOrder = "first",
minScore = 0.7,
outSimilarity = {name = "NodeSim", replace=true},
outNodes = {name = "NodeVecs", replace=true}
Node Similarity in an Undirected Graph
This section contains Python code for the analysis in the CASL version of this example, which contains details about the results.
Note: In order to run this code, the data that are described in the CASL version need to be loaded into CAS. One way to do this is to convert the LinkSetIn data to the comma-separated-value (CSV) file LinkSetIn.csv and then use the following code to load the CSV file into CAS:
s.upload_file('LinkSetIn.csv')
For more information about coding in Python, see Getting Started with SAS Viya for Python and SAS Viya: System Programming Guide.
The following statements find all node pairs that have a similarity score greater than or equal to 0.7:
s.network.nodeSimilarity(
links = {"name":"LinkSetIn"},
vector = true,
nDimensions = 10,
proximityOrder = "second",
minScore = 0.7,
outSimilarity = {"name":"NodeSim", "replace":True})
The first-order proximity can be calculated by the following statements:
s.network.nodeSimilarity(
links = {"name":"LinkSetIn"},
jaccard = false,
vector = true,
nDimensions = 10,
proximityOrder = "first",
minScore = 0.7,
outSimilarity = {"name":"NodeSim", "replace":True},
outNodes = {"name":"NodeVecs", "replace":True})
Node Similarity in an Undirected Graph
This example is not available for the R programming language.