Serialize and unserialize in R. How to coerce similar objects?

I tried the following:

library(yaml)
library(igraph)

g1 <- make_star(7)
y1 <- as.yaml(g1)
g2 <- yaml.load(y1)

class(g1)                   # = igraph
class(g2)                   # = list

identical(str(g1), str(g2))	# true

g1 and g2 are structurally identical. But g1 has the class igraph. And g2 the class list. Is there an obvious way to convert g2 to igraph class.

No, there isn’t, because not all data is saved during the as.yaml() step:

> g1 <- make_star(7)
> unclass(g1)[[10]]
<environment: 0x10a0252e0>
> g2 <- yaml.load(as.yaml(g1))
> g2[[10]]
Error in g2[[10]] : subscript out of bounds

The part that is lost is a hidden R environment that stores all sorts of stuff about the graph that igraph itself needs:

> ls(unclass(g1)[[10]], all.names=T)
[1] ".__igraph_version__." "myid"

I guess you’ll need saveRDS() and readRDS() if you want to store an igraph object into a file in a way that preserves all R-specific stuff.

1 Like

Then, is it possible to "un.igraph" an igraph object and retrieve the underlying list structure?
Omitting the platform dependent data, but enough to reconstruct the graph.

As an example for graph g1: g2 <- as.igraph(un.igraph(g1))

If this is possible, then making a nice print of an igrapgh object is equivalent to printing a (nested) list neatly: prettyprint(un.igraph(g1)).

What I’m actually asking is a cross-platform definition of an igraph object.

As we said before, saveRDS / readRDS provide a cross-platform way to losslessly serialize an R/igraph object.

You focus on “serializing igraph objects”, but since graph attributes can be arbitrary R objects, the only way to do this is to use a serialization that works for any R object. That naturally leads to save / load / saveRDS / readRDS.

There are also functions like dput and dump, but as the dput documentation states,

Deparsing an object is difficult, and not always possible. With the default control , dput() attempts to deparse in a way that is readable, but for more complex or unusual objects (see dump ), not likely to be parsed as identical to the original. Use control = "all" for the most complete deparsing;

None of this is igraph specific though. It all applies to any R object.

While dput is nice for getting a fairly accurate picture of what is in an R object, the documentation states that it cannot handle all objects and is not suitable for data transfer:

This is not a good way to transfer objects between R sessions. dump is better, but the functions save and saveRDS are designed to be used for transporting R data, and will work with R objects that dput does not handle correctly as well as being much faster.

So, straight from the horse’s mouth (the R documentation), there’s your answer: use save or saveRDS.

Thanks for the very helpful comments.

What I’m trying to achieve is to distribute an object, but in a concise, human-readable form.
With minimal dependencies on system and environment. So that others can experiment with the result. And in such a way that it can be published in limited space.

Building on the received input I arrived at:

library(igraph)
g1 <- make_star(7)
dput(g1) # transfer output to the code example

# code example
library(igraph)
s1 = r"---(
structure(list(7, TRUE,
 c(1, 2, 3, 4, 5, 6),
 c(0, 0, 0, 0, 0, 0),
 c(0, 1, 2, 3, 4, 5),
 c(0, 1, 2, 3, 4, 5),
 c(0, 0, 1, 2, 3, 4, 5, 6),
 c(0, 6, 6, 6, 6, 6, 6, 6),
 list(c(1, 0, 1),
 list(name = "In-star",  mode = "in", center = 1),
 list(),
 list()) ),
 class = "igraph") 
)---"
g2 <- dget(rawConnection(charToRaw(s1)), keep.source=FALSE) 
g2 <- upgrade_graph(g2)
stopifnot(identical_graphs(g1, g2))

Dget doesn’t like <envionment> so I got rid of that.

Using a raw vector can be avoided, making it simpler.

library(igraph)
g1 <- make_star(7)
dput(g1) # transfer output to the code example

# code example
library(igraph)
s1 = deparse(
structure(list(7, TRUE,
 c(1, 2, 3, 4, 5, 6),
 c(0, 0, 0, 0, 0, 0),
 c(0, 1, 2, 3, 4, 5),
 c(0, 1, 2, 3, 4, 5),
 c(0, 0, 1, 2, 3, 4, 5, 6),
 c(0, 6, 6, 6, 6, 6, 6, 6),
 list(c(1, 0, 1),
 list(name = "In-star",  mode = "in", center = 1),
 list(),
 list()) ),
 class = "igraph") 
)
g2 <- dget(textConnection(s1), keep.source=FALSE) 
g2 <- upgrade_graph(g2)
stopifnot(identical_graphs(g1, g2))

Triggered by Tamas’s example, I think it’s better to avoid the dput of the igraph object altogether by applying an unclass operation first.

library(igraph)
g1 <- graph_from_literal(1 -+2)		# non-empty igraph object
ug <- unclass(g1)					# remove class igraph
ug[[10]] <- NULL					# remove environment
dput(ug, stdout())				    # output to console
us <- list(2, TRUE, 0, 1, 0, 0, c(0, 1, 1), c(0, 0, 1), list(c(1, 0, 1), structure(list(), .Names = character(0)), list(name = c("1", "2")), structure(list(), .Names = character(0))))

g2 <- structure(us, class="igraph") # add class igraph
g3 <- upgrade_graph(g2)				# and upgrade to current environment