8

We can pass literals to make a graph as below:

# R version 4.0.2 (2020-06-22)
library(igraph) # igraph_1.2.6

graph_from_literal(A-B)
# IGRAPH 7e5604a UN-- 2 1 -- 
# + attr: name (v/c)
# + edge from 7e5604a (vertex names):
#   [1] A--B

Thought passing a string would work, but it doesn't. (This is a small example "A-B", imagine a long complex string):

graph_from_literal("A-B")
# IGRAPH 8b32703 UN-- 1 0 -- 
# + attr: name (v/c)
# + edges from 8b32703 (vertex names):

# objects are not the same
identical_graphs(graph_from_literal(A-B),
                 graph_from_literal("A-B"))
# [1] FALSE

Either I am using a wrong function or I need another function to drop the quotes. cat, noquote didn't work. Ideas?


Edit 1: I would want to avoid string manipulation: split "A-B" into "from" and "to", then using graph_from_dataframe. For example, splitting into 2 columns would not work for a simple input of "A-B-C".

Edit 2: Motivation came from another question where I thought I could use igraph package as a solution. I substituted dots with dashes, wanted to convert that string into a graph object, but realised literal doesn't like string input.

So the longer question: How would I convert below into a graph object?

# input
c('0.1', '0.1.1', '0.1.2', '0.11', '0.12', '0.11.1', '0.12.1', '0.12.2')

# expected output:
graph_from_literal(0-1, 0-1-1, 0-1-2, 0-11, 0-12, 0-11-1, 0-12-1, 0-12-2)
# IGRAPH 0792a00 UN-- 5 7 -- 
# + attr: name (v/c)
# + edges from 0792a00 (vertex names):
#   [1] 0--1  0--11 0--12 1--2  1--11 1--12 2--12

Edit 3: There is now a related open GitHub issue 475 to address this functionality.

zx8754
  • 52,746
  • 12
  • 114
  • 209
  • Can you please motivate this request and explain why you want to write this as a string instead of an expression? The purpose of `graph_from_literal` is to be able to create graphs quickly for experimentation. The expectation is that you will type in the expression manually. Then, as far as I can see, it does not matter if you type it with or without quotes. – Szabolcs Sep 20 '21 at 19:12
  • @Szabolcs see "Edit 2". – zx8754 Sep 20 '21 at 19:31
  • 1
    Thanks for the explanation. Discussion about adding adding string support to R/igraph is [here](https://github.com/igraph/rigraph/issues/475). Feel free to comment. Personally, I would consider the proposed application to be a hack (a very convenient hack, but still a hack, as it is not consistent with the intended use of `graph_from_literal`). – Szabolcs Sep 20 '21 at 19:50
  • @Szabolcs great, I was thinking of posting it at GitHub, too. Now with the edit, and having that active issue link to this post, hope the "motivation" is clear. – zx8754 Sep 20 '21 at 20:00

3 Answers3

5

Split the input by comma, parse into an expression and call the internal graph_from_literal_i. No packages other than igraph are used.

graph_from_string <- function(x) {
  e <- str2expression(strsplit(x, ",")[[1]])
  do.call(igraph:::graph_from_literal_i, list(e))
}

# test 1
graph_from_string("A-B")
## IGRAPH 063d605 UN-- 2 1 -- 
## + attr: name (v/c)
## + edge from 063d605 (vertex names):
## [1] A--B

# test 2 - a more complex example
graph_from_string("A-B-C, D-E")
## IGRAPH b155b39 UN-- 5 3 -- 
## + attr: name (v/c)
## + edges from b155b39 (vertex names):
## [1] A--B B--C D--E

If there are no commas in the input then this one-liner would also work:

do.call("graph_from_literal", list(parse(text = "A-B")[[1]]))
## IGRAPH dad0219 UN-- 2 1 -- 
## + attr: name (v/c)
## + edge from dad0219 (vertex names):
## [1] A--B
G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
  • 1
    The one-liner is slightly simpler as `do.call(graph_from_literal, as.list(parse(text = "A-B")))`. – Roland Sep 20 '21 at 10:20
  • "graph_from_literal_i" nice one, thanks. I wonder why "graph_from_string" doesn't already exist. – zx8754 Sep 20 '21 at 10:25
1

Perhaps we can do something like this

> x <- "A-B-C, D-E"

> eval(str2lang(sprintf("graph_from_literal(%s)", x)))
IGRAPH ab43602 UN-- 5 3 -- 
+ attr: name (v/c)
+ edges from ab43602 (vertex names):
[1] A--B B--C D--E
ThomasIsCoding
  • 96,636
  • 9
  • 24
  • 81
0

You can make a data frame with one edge per row instead:

library(tidyverse)
library(igraph)
#> 
#> Attaching package: 'igraph'
#> The following objects are masked from 'package:dplyr':
#> 
#>     as_data_frame, groups, union
#> The following objects are masked from 'package:purrr':
#> 
#>     compose, simplify
#> The following object is masked from 'package:tidyr':
#> 
#>     crossing
#> The following object is masked from 'package:tibble':
#> 
#>     as_data_frame
#> The following objects are masked from 'package:stats':
#> 
#>     decompose, spectrum
#> The following object is masked from 'package:base':
#> 
#>     union

list("A-B", "A-C") %>%
  tibble(edge = .) %>%
  unnest(edge) %>%
  separate(edge, into = c("from", "to"), sep = "-") %>%
  graph_from_data_frame(directed = FALSE)
#> IGRAPH 011a563 UN-- 3 2 -- 
#> + attr: name (v/c)
#> + edges from 011a563 (vertex names):
#> [1] A--B A--C

Created on 2021-09-20 by the reprex package (v2.0.1)

danlooo
  • 10,067
  • 2
  • 8
  • 22