Jonno Bourne
Jonno Bourne

Reputation: 1991

Maintain attribute names when merging Igraphs

Problem

I have two separate networks with no overlapping nodes or edges, they both have the same attributes. I want to combine these two networks into a single network which would then be made up of two distinct components.

However when I try to merge them using the union command the attributes are renamed from "attribute" to "attribute_1" and "attribute_2". That this will happen is stated in the command help file, but I cannot find an obvious way to merge these two networks.

The situation is shown in the below code block

library(igraph)

#create a 4 node network of two components
adjmat <- rep(0, 16)
adjmat[c(2,5,12,15)] <- 1
g <-  graph.adjacency(matrix(adjmat, nrow = 4) , mode = "undirected") 

#give attributes naming the nodes and the edges
g <- set_vertex_attr(g, "name", value = paste0("Node_", 1:4))
g <- set_edge_attr(g, "name", value = paste0("Edge_",1:2))

#I am interested in the type attribute
g <- set_edge_attr(g, "type", value = c("foo", "bar"))

plot(g)

#Decompose into seperate networks
gList <- decompose(g)

g2 <-union(gList[[1]], gList[[2]])

#vertices are fine but edges have been renamed as stated in the helpfile for union.
get.edge.attribute(g2)
get.vertex.attribute(g2)

Work around

Currently the two separate networks originate from the same original network so I have been able to make a hack however this isn't always the case and I would like a more igraph way of merging the two.

The hack is below

#To solve this problem I do the following

#Create two dataframes from the edge characteristics of the network and combine into a single dataframe
P <- rbind(as_data_frame(gList[[1]]),
               as_data_frame(gList[[2]]))

g3 <- set.edge.attribute(g, "type", value = P$type[match(P$name,  get.edge.attribute(g, "name"))]) 

#Edges are now correct
get.edge.attribute(g3)matrix(adjmat, nrow = 4) 
get.vertex.attribute(g3)

Is there a function in igraph that would merge the two seperate networks into a single network whilst maintaining the attributes as is?

Upvotes: 4

Views: 450

Answers (1)

Jonno Bourne
Jonno Bourne

Reputation: 1991

I have made the below version of union, which accepts two graphs with an arbitrary number of overlapping attributes and merges them into a single graph where the attributes do not have the "_x" suffix. The graphs can be entirely independent or have overlapping nodes. In the case of overlapping nodes the attributes of graph 1 take precedence

library(dplyr)
library(igraph)

union2<-function(g1, g2){

 #Internal function that cleans the names of a given attribute
 CleanNames <- function(g, target){
  #get target names
  gNames <- parse(text = (paste0(target,"_attr_names(g)"))) %>% eval 
  #find names that have a "_1" or "_2" at the end
  AttrNeedsCleaning <- grepl("(_\\d)$", gNames )
  #remove the _x ending
  StemName <- gsub("(_\\d)$", "", gNames)

  NewnNames <- unique(StemName[AttrNeedsCleaning])
  #replace attribute name for all attributes
  for( i in NewnNames){

   attr1 <- parse(text = (paste0(target,"_attr(g,'", paste0(i, "_1"),"')"))) %>% eval
   attr2 <- parse(text = (paste0(target,"_attr(g,'", paste0(i, "_2"),"')"))) %>% eval

   g <- parse(text = (paste0("set_",target,"_attr(g, i, value = ifelse(is.na(attr1), attr2, attr1))"))) %>%
             eval

   g <- parse(text = (paste0("delete_",target,"_attr(g,'", paste0(i, "_1"),"')"))) %>% eval
   g <- parse(text = (paste0("delete_",target,"_attr(g,'", paste0(i, "_2"),"')"))) %>% eval

   }

  return(g)
 }


 g <- igraph::union(g1, g2) 
 #loop through each attribute type in the graph and clean
 for(i in c("graph", "edge", "vertex")){
 g <- CleanNames(g, i)
 }

 return(g)

}

Using the previous example

g4 <-union2(gList[[1]], gList[[2]])

#As we would like
get.edge.attribute(g4)
get.vertex.attribute(g4)

Upvotes: 3

Related Questions