Reputation: 5098
I have generated a graph:
library(DiagrammeR)
grViz("
digraph boxes_and_circles {
# a 'graph' statement
graph [layout = neato, overlap = true, fontsize = 10, outputorder = edgesfirst]
# several 'node' statements
node [shape = circle,
fontname = Helvetica]
A [pos = '1,1!'];
B [pos = '0,2!'];
C [pos = '1.5,3!'];
D [pos = '2.5,1!'];
E [pos = '4,1!'];
F [pos = '4,2!'];
G [pos = '5,1!'];
H [pos = '6,2!'];
I [pos = '1.5,-0.1!'];
# several 'edge' statements
A->B B->C
D->E D->F E->F E->G F->G G->H F->H
}
")
Which produces:
Now I would like to draw a box with dotted lines around the nodes A, B, and C.
How can I accomplish this in R? A key requirement of the solution is that it is reproducible, i.e. that I can run the script multiple times and get the same result.
Upvotes: 11
Views: 747
Reputation: 19857
An easy solution with DiagrammR
would be to use dot rather than neato. You mostly lose the ability to manually position the nodes (attribute pos doesn't work anymore), but you gain the ability to use cluster and subgraph to draw lines around sets of nodes.
library(DiagrammeR)
grViz("
digraph boxes_and_circles {
# a 'graph' statement
graph [ fontsize = 10,rankdir=LR]
# several 'node' statements
node [shape = circle,
fontname = Helvetica]
# several 'edge' statements
subgraph cluster_1 {
style=dotted
A->B->C
}
D->E D->F E->F E->G F->G G->H F->H
I
}
")
Upvotes: 2
Reputation: 3201
Here's another approach based on igraph. It is inspired by this igraph code sample.
I'm assuming that using igraph instead of DiagrammeR is an option - maybe that is not the case...
We leave positioning of the vertices to a standard layout algorithm and query it for the resulting vertex positions. These positions are then used to draw a dotted rectangle around an arbitrary set of "selected" vertices. No user interaction is needed.
We start with the graph topology.
library(igraph)
set.seed(42)
df <- data.frame(from = c('A', 'B', 'I', 'D', 'D', 'E', 'E', 'F', 'F', 'G'),
to = c('B', 'C', 'I', 'E', 'F', 'G', 'F', 'H', 'G', 'H'))
g <- graph.data.frame(df, directed = TRUE)
The size of the vertices and arrows in the graph can be set freely, according to taste.
vertexsize <- 50
arrowsize <- 0.2
We ask the Fruchterman-Reingold layout engine to calculate the coordinates of the vertices.
coords <- layout_with_fr(g)
Then plot the graph.
plot(g,
layout = coords,
vertex.size = vertexsize,
edge.arrow.size = arrowsize,
rescale = FALSE,
xlim = range(coords[,1]),
ylim = range(coords[,2]))
If we like to see what's going on, we can add coordinate axes and print the vertex coordinates:
axis(1)
axis(2)
V(g) # ordered vertex list
coords # coordinates of the vertices (in the same coordinate system as our dotted rectangle)
We now figure out the bounding box of the vertices that we want a rectangle around.
selectedVertices = c("A", "B", "C")
vertexIndices <- sapply(selectedVertices, FUN = function(x) { return(as.numeric(V(g)[x])) } )
llx <- min(coords[vertexIndices, 1])
lly <- min(coords[vertexIndices, 2])
urx <- max(coords[vertexIndices, 1])
ury <- max(coords[vertexIndices, 2])
Almost there. We already have the coordinates of the vertex centers in coords[], but we also need the size of the vertices in the coordinate system of plot(). From the plot.igraph source code we can see that the vertex.size option for plot() gets divided by 200 and then used as radius for drawing the vertex. We use a 50% bigger value as the margin around the bounding box of the vertex coordinates when drawing the dotted rectangle.
margin <- (vertexsize / 200) * 1.5
rect(llx - margin, lly - margin, urx + margin, ury + margin, lty = 'dotted')
This is the result we get:
Upvotes: 6
Reputation: 20811
You could use @StevenBeaupre's solution for the widget, but there are a few packages for graphing networks using R's graphics. One is igraph
if you are open to using other solutions.
This will make the graph
library('igraph')
set.seed(11)
g <- data.frame(from = c('A', 'B', 'I', 'D', 'D', 'E', 'E', 'F', 'F', 'G'),
to = c('B', 'C', 'I', 'E', 'F', 'G', 'F', 'H', 'G', 'H'))
(gg <- graph.data.frame(g, directed = TRUE))
plot(gg, vertex.color = 'white')
And there are many ways to add a box to r graphics; here is one where you can click the plot to add the box without having to calculate anything
rekt <- function(...) {
coords <- c(unlist(locator(1)), unlist(locator(1)))
rect(coords[1], coords[2], coords[3], coords[4], ..., xpd = NA)
}
rekt(border = 'red', lty = 'dotted', lwd = 2)
I get this
Upvotes: 5