Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make an edge-colored multigraph from several graphs #1351

Open
ngmaclaren opened this issue Apr 30, 2024 · 0 comments
Open

Make an edge-colored multigraph from several graphs #1351

ngmaclaren opened this issue Apr 30, 2024 · 0 comments
Labels
feature a feature request or enhancement

Comments

@ngmaclaren
Copy link
Contributor

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.

@maelle maelle added the feature a feature request or enhancement label May 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature a feature request or enhancement
Projects
None yet
Development

No branches or pull requests

2 participants