igraph / rigraph

igraph R package
https://r.igraph.org
532 stars 200 forks source link

Make an edge-colored multigraph from several graphs #1351

Open ngmaclaren opened 2 months ago

ngmaclaren commented 2 months ago

What is the feature or improvement you would like to see? I would like to make a multilayer network, implemented as an edge-colored multigraph, with a single function call in igraph. Specifically, I would like to make a multigraph M from from several graphs or digraphs {g1, g2, ...} which includes all vertices from {V1, V2, ...} and all edges from {E1, E2, ...}. M should contain an edge (u, v) if any {E1, E2, ...} contains (u, v). Additionally, if several (u, v) exist, I would like to retain all such edges in M. I would like to retain vertex and edge information from {g1, g2, ...} and also add a label to each edge in M specifying its originating graph.

I think my request is related to #1345 but I would like to (a) retain node and edge attributes, and (b) keep all component edges rather than merge multiple edges between the same vertices.

My attempt is below. The function works by adding a "layer" attribute to each edge, which is the name of the graph if present, then using rbind() on the vertex and edge data frames. The resulting multigraph is directed if any of the component networks is.

make_edge_colored_graph <- function(..., list = character()) {
    if(length(list) > 0) {
        gl <- list
    } else {
        gl <- list(...)
    }

    ## Need all original vertex and edge attributes to be the same in order to use the rbind solution, below
    vattr <- lapply(gl, vertex_attr_names)
    eattr <- lapply(gl, edge_attr_names)
    ## https://stackoverflow.com/questions/18813526/check-whether-all-elements-of-a-list-are-in-equal-in-r
    identicalValue <- function(x,y) if (identical(x,y)) x else FALSE
    if(isFALSE(Reduce(identicalValue, vattr))) stop("Vertices in all graphs must have the same attributes.")
    if(isFALSE(Reduce(identicalValue, eattr))) stop("Edges in all graphs must have the same attributes.")

    ## The new graph should have an edge attribute called "layer" that labels each edge by its originating graph
    use.graph.names <- all(sapply(gl, function(g) !is.null(g$name)))
    if(use.graph.names) {
        gl <- lapply(gl, function(g) {
            E(g)$layer <- g$name
            g
        })
    } else {
        gl <- mapply(
            function(g, int) {
                E(g)$layer <- int
                g
            }, gl, seq_along(gl)
        )
    }

    ## From here I can use data frames, but is there a better way to do this?
    edfs <- lapply(gl, as_data_frame, "edges")
    edf <- do.call(rbind, edfs)

    vdfs <- lapply(gl, as_data_frame, "vertices")
    vdfs <- lapply(vdfs, function(df) {
        old <- colnames(df)
        if(!("name" %in% colnames(df))) { # Make a "name" attribute if not present to support !duplicated(), below
            df$name <- rownames(df)
            df <- df[, c("name", old)]
        }
        df
    })
    vdf <- do.call(rbind, vdfs)
    vdf <- vdf[!duplicated(vdf), ]

    g <- graph_from_data_frame(edf, vertices = vdf, directed = any(sapply(gl, is_directed)))

    return(g)
}

And here is a demonstration:

library(igraph)

g1 <- make_ring(4, directed = FALSE)
g2 <- make_star(5, mode = "undirected")
g3 <- make_tree(5, mode = "undirected")

E(g1)$color <- 1
E(g2)$color <- 2
E(g3)$color <- 3

V(g1)$color <- 0
V(g2)$color <- 0
V(g3)$color <- 0

M <- make_edge_colored_graph(g1, g2, g3)

dev.new(height = 10, width = 10)
plot.new()
lyt <- matrix(c(1, 2, 3, 4, 4, 4), nrow = 2, byrow = TRUE)
layout(lyt)
for(g in list(g1, g2, g3, M)) plot(g, edge.width = 2, layout = layout_in_circle)
legend("bottomleft", bty = "n", legend = c(g1$name, g2$name, g3$name),
       col = palette.colors()[2:4], lwd = 3, cex = 2)

Use cases for the feature As I understand it, igraph doesn't support multilayer networks per se and probably won't at least in the near future. However, it seems that edge-colored multigraphs are possible. For the time being, I just wanted to make an edge-colored multigraph to visualize several layers of a social network. I don't know if my implementation, above, is good enough to be useful for algorithms, but maybe it could be improved?

It looks like other people are interested in doing something similar:

References Kivelä, Mikko, et al. "Multilayer networks." Journal of Complex Networks 2.3 (2014): 203-271.