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

A Simple Undirected Graph


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

Problem Summary
Number of Nodes6
Number of Links7
Graph DirectionUndirected


The solution summary output from this action is presented in Output 16.8.2.

Output 16.8.2: Solution Summary

Solution Summary
Problem TypeNode Similarity
Solution StatusOK
CPU Time3.26
Real Time1.05


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_sourcesinkjaccardvector
1AA11
2AC10.9993204255
3AE0.66666666670.8037895053
4BB11
5BD10.9940500316
6BF0.33333333330.8331655259
7CA10.9993204255
8CC11
9CE0.66666666670.8097860746
10DB10.9940500316
11DD11
12DF0.33333333330.7726934599
13EA0.66666666670.8037895053
14EC0.66666666670.8097860746
15EE11
16FB0.33333333330.8331655259
17FD0.33333333330.7726934599
18FF11


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

Selected Rows from Table NODESIM
_Index_sourcesinkvector
1AA1
2AB0.757278421
3AD0.9800626127
4BA0.757278421
5BB1
6BC0.9307237114
7BD0.7741068941
8CB0.9307237114
9CC1
10CD0.7627349306
11DA0.9800626127
12DB0.7741068941
13DC0.7627349306
14DD1
15EE1
16EF0.9999504081
17FE0.9999504081
18FF1


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_nodevec_0vec_1vec_2vec_3vec_4vec_5vec_6vec_7vec_8vec_9
1A-0.070340221-0.400010105-0.214410412-0.32031008-0.169383330.151205169-0.092025920.0249410893-0.335962398-0.716211227
2B0.1625498556-0.373941288-0.3727768410.0559580090.36976690080.26216121060.28378730960.3557387832-0.443461713-0.287144414
3C0.3693920902-0.298292118-0.3216361190.05860878470.15851555270.40084132820.26595507060.5952034098-0.132761869-0.198143004
4D0.0621557611-0.395800609-0.226534926-0.308582922-0.240366340.2573960761-0.0677432370.1965312881-0.213483577-0.692906487
5E-0.1340470620.43510211990.3210940880.18303172190.0098514615-0.304418697-0.083028316-0.3165102890.32286885990.5933219585
6F-0.133223790.43473045180.32329780950.17843369720.0000792602-0.304805824-0.087913821-0.3162804480.32916808250.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.

Last updated: June 07, 2018