mbojan / intergraph

R package for converting network data objects between different classes
https://mbojan.github.io/intergraph
21 stars 4 forks source link

bipartite #30

Open knapply opened 3 years ago

knapply commented 3 years ago

Moving https://github.com/statnet/network/issues/21#issuecomment-693513311 over here...

@mbojan, friendly FYI: converting bipartite objects should now be simpler to implement.

southern_women_affil_mat <- structure(
  c(1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 
    1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 
    1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 
    0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
    1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 
    1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 
    0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 
    0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 
    1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 
    1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0,
    0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 
    1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0), 
  .Dim = c(14L, 18L), 
  .Dimnames = list(
    sprintf("e%d", 1:14),
    c("Evelyn", "Laura", "Theresa", "Brenda", "Charlotte", "Frances", "Eleanor", "Pearl", 
      "Ruth", "Verne", "Myrna", "Katherine", "Sylvia", "Nora", "Helen", "Dorothy",
      "Olivia", "Flora")
  )
)

ig <- igraph::graph_from_incidence_matrix(southern_women_affil_mat)

ig_coords <- igraph::layout_with_graphopt(ig, niter = 5000)
igraph::vertex_attr(ig, "x") <- ig_coords[, 1L]
igraph::vertex_attr(ig, "y") <- ig_coords[, 2L]
igraph::vertex_attr(ig, "color") <- ifelse(igraph::vertex_attr(ig, "type"), 
                                           "salmon", "lightblue")
ig
#> IGRAPH daed723 UN-B 32 89 -- 
#> + attr: type (v/l), name (v/c), x (v/n), y (v/n), color (v/c)
#> + edges from daed723 (vertex names):
#>  [1] e1--Evelyn    e1--Laura     e1--Brenda    e2--Evelyn    e2--Laura    
#>  [6] e2--Theresa   e3--Evelyn    e3--Laura     e3--Theresa   e3--Brenda   
#> [11] e3--Charlotte e3--Frances   e4--Evelyn    e4--Theresa   e4--Brenda   
#> [16] e4--Charlotte e5--Evelyn    e5--Laura     e5--Theresa   e5--Brenda   
#> [21] e5--Charlotte e5--Frances   e5--Eleanor   e5--Ruth      e6--Evelyn   
#> [26] e6--Laura     e6--Theresa   e6--Brenda    e6--Frances   e6--Eleanor  
#> [31] e6--Pearl     e6--Nora      e7--Laura     e7--Theresa   e7--Brenda   
#> [36] e7--Charlotte e7--Eleanor   e7--Ruth      e7--Verne     e7--Sylvia   
#> + ... omitted several edges

intergraph::asNetwork(ig)
#>  Network attributes:
#>   vertices = 32 
#>   directed = FALSE 
#>   hyper = FALSE 
#>   loops = FALSE 
#>   multiple = FALSE 
#>   bipartite = FALSE 
#>   total edges= 89 
#>     missing edges= 0 
#>     non-missing edges= 89 
#> 
#>  Vertex attribute names: 
#>     color type vertex.names x y 
#> 
#> No edge attributes

asNetwork2 <- function(ig) {
  proto_net <- igraph::as_data_frame(ig, what = "both")
  if (!igraph::is_named(ig)) {
    proto_net$vertices$name <- as.double(seq_len(igraph::vcount(ig)))
  }
  col_order <- c("name", setdiff(names(proto_net$vertices), "name"))
  proto_net$vertices <- proto_net$vertices[col_order]

  network::as.network(
    x = proto_net$edges,
    directed = igraph::is_directed(ig),
    vertices = proto_net$vertices,
    hyper = FALSE,
    loops = any(igraph::which_loop(ig)),
    multiple = igraph::any_multiple(ig),
    bipartite = igraph::is_bipartite(ig),
    bipartite_col = "type"
  )
}

nw <- asNetwork2(ig)
#> Warning: `vertices` were not provided in the order required for bipartite networks. Reordering.
#> 
#> This is the first and last time you will be warned during this session.
nw
#>  Network attributes:
#>   vertices = 32 
#>   directed = FALSE 
#>   hyper = FALSE 
#>   loops = FALSE 
#>   multiple = FALSE 
#>   bipartite = 14 
#>   total edges= 89 
#>     missing edges= 0 
#>     non-missing edges= 89 
#> 
#>  Vertex attribute names: 
#>     color type vertex.names x y 
#> 
#> No edge attributes
network::is.bipartite(nw)
#> [1] TRUE
nw_coords <- cbind(
  network::get.vertex.attribute(nw, "x"),
  network::get.vertex.attribute(nw, "y")
)

plot(ig, vertex.label.cex = 0.65, vertex.size = 15, main = "igraph")

plot(nw, 
     vertex.col = network::get.vertex.attribute(nw, "color"),
     label = network::network.vertex.names(nw),
     coord = nw_coords,
     vertex.cex = 4,
     label.cex = 0.6, label.pos = 5,
     edge.col = "lightgray",
     pad = 0,
     main = "network")


ig2 <- igraph::graph_from_data_frame(
  d = network::as.data.frame.network(nw, unit = "edges"),
  directed = network::is.directed(nw),
  vertices = network::as.data.frame.network(nw, unit = "vertices")
)
ig2
#> IGRAPH 7f421d7 UN-B 32 89 -- 
#> + attr: name (v/c), type (v/l), x (v/n), y (v/n), color (v/c)
#> + edges from 7f421d7 (vertex names):
#>  [1] Evelyn   --e1 Laura    --e1 Brenda   --e1 Evelyn   --e2 Laura    --e2
#>  [6] Theresa  --e2 Evelyn   --e3 Laura    --e3 Theresa  --e3 Brenda   --e3
#> [11] Charlotte--e3 Frances  --e3 Evelyn   --e4 Theresa  --e4 Brenda   --e4
#> [16] Charlotte--e4 Evelyn   --e5 Laura    --e5 Theresa  --e5 Brenda   --e5
#> [21] Charlotte--e5 Frances  --e5 Eleanor  --e5 Ruth     --e5 Evelyn   --e6
#> [26] Laura    --e6 Theresa  --e6 Brenda   --e6 Frances  --e6 Eleanor  --e6
#> [31] Pearl    --e6 Nora     --e6 Laura    --e7 Theresa  --e7 Brenda   --e7
#> [36] Charlotte--e7 Eleanor  --e7 Ruth     --e7 Verne    --e7 Sylvia   --e7
#> + ... omitted several edges
igraph::is_bipartite(ig2)
#> [1] TRUE
plot(ig2, vertex.label.cex = 0.65, vertex.size = 15, main = "round-trip igraph")

The package need some review but I'd love some input/PR on this once I get to that.

I'm happy to help. I can't promise I'll get to a PR too soon, but I'll make sure I can provide any feedback or input I can.

mbojan commented 3 years ago

Thanks @knapply

knapply commented 3 years ago

Suggestion: wait until the next version of {network} is actually released before using as.data.frame.network() here as It seems things could shift dramatically over there.

mbojan commented 3 years ago

Indeed. I keep my finger on the pulse. Thanks.