开发者

Re-layout graph after small modification while preserving features of original layout

开发者 https://www.devze.com 2023-03-17 04:45 出处:网络
Is there a simple way to do the following in Mathe开发者_如何学Gomatica 8? Construct a graph, and display it using some graph layout.

Is there a simple way to do the following in Mathe开发者_如何学Gomatica 8?

  1. Construct a graph, and display it using some graph layout.
  2. Modify the graph slightly (e.g. add or remove an edge or a vertex).
  3. Re-compute the layout starting from the original layout, in such a way that the the "shape" of the object is more or less preserved. E.g. re-run a spring-electric layout algorithm starting with the coordinates of the previous layout.

If the graph hasn't changed between two displays, the layout shouldn't change either (or only minimally). Using the display of the new Graph or GraphPlot are both acceptable.

EDIT: In essence I need similar layouts for similar graphs. I always obtain similar graphs by modifying an existing one, which may have already been laid out, but any generic solution is acceptable.

EDIT 2: Here's an example of where this kind of thing is useful. Go to http://ccl.northwestern.edu/netlogo/models/GiantComponent and click "Run in browser" (requires Java). Click Setup then click Go. You can see the graph evolve. If we do this in Mathematica, then each of the successive graphs will look completely different, and it will be difficult to see that it is the same graph that is evolving. In several applications it's quite useful to be able to visualize small changes to the graph as such. But if many successive changes are done, then re-computing the layout is a must, simply fading or highlighting edges is not sufficient. Again, this is just an example: I'm not trying to use Mathematica to animate a graph, or to visualize the emergence of the giant component.


Here are two basic approaches for altering graphs in MMA 8.0. The first relies on HighlightGraph and in particular on GraphHighlightStyle -> "DehighlightHide". The second approach uses the VertexCoordinates of a graph in future variants of that graph.

We'll discuss deletion separately from addition because they involve slightly different methods.

[P.S. : I made several edits to my answer in to make it clearer.]

First some data:

edges={1\[UndirectedEdge]8,1\[UndirectedEdge]11,1\[UndirectedEdge]18,1\[UndirectedEdge]19,1\[UndirectedEdge]21,1\[UndirectedEdge]25,1\[UndirectedEdge]26,1\[UndirectedEdge]34,1\[UndirectedEdge]37,1\[UndirectedEdge]38,4\[UndirectedEdge]11,4\[UndirectedEdge]12,4\[UndirectedEdge]26,4\[UndirectedEdge]27,4\[UndirectedEdge]47,4\[UndirectedEdge]56,4\[UndirectedEdge]57,4\[UndirectedEdge]96,4\[UndirectedEdge]117,5\[UndirectedEdge]11,5\[UndirectedEdge]18,7\[UndirectedEdge]21,7\[UndirectedEdge]25,7\[UndirectedEdge]34,7\[UndirectedEdge]55,7\[UndirectedEdge]76,8\[UndirectedEdge]11,26\[UndirectedEdge]29,26\[UndirectedEdge]49,26\[UndirectedEdge]52,26\[UndirectedEdge]111,27\[UndirectedEdge]28,27\[UndirectedEdge]51,42\[UndirectedEdge]47,49\[UndirectedEdge]97,51\[UndirectedEdge]96}

Here is the initial graph:

g = Graph[edges, VertexLabels -> "Name", ImagePadding -> 10, 
ImageSize -> 500]

Re-layout graph after small modification while preserving features of original layout

"Deleting" a graph edge without changing the overall appearance of the graph.

Let's begin to remove the edge (4,11) located at the center of the graph. remainingEdgesAndVertices contains all vertices and the initial edges with the exception of edge (4,11).

remainingEdgesAndVertices = 
 Join[VertexList[g],  Complement[EdgeList[g], {4 \[UndirectedEdge] 11}]]

Let's "delete" (i.e. hide) the edge (4,11):

 HighlightGraph[g, remainingEdgesAndVertices, VertexLabels -> "Name", 
   ImagePadding -> 10, GraphHighlightStyle -> "DehighlightHide", 
   ImageSize -> 500]

Re-layout graph after small modification while preserving features of original layout

If we had actually removed edge (4, 11) the graph would have radically changed its appearance.

 Graph[Complement[edges, {4 \[UndirectedEdge] 11}], 
   VertexLabels -> "Name", ImagePadding -> 10, ImageSize -> 500]

Re-layout graph after small modification while preserving features of original layout

"Adding" a graph edge without changing the overall appearance of the graph.

Adding a graph edge is slightly more challenging. There are two ways that come to mind. The method used here works backwards. You include the new edge first in hidden form and then uncover it later. The initial graph with the hidden, "to-be-added" edge will be in a layout similar to that of the graph with the "new" edge. The reason is this: they are in fact the same graph: however they show different numbers of edges.

g2 = Graph[Append[edges, 42 \[UndirectedEdge] 37], 
 VertexLabels -> "Name", ImagePadding -> 10, ImageSize -> 500]

HighlightGraph[g2, 
   Join[Complement[EdgeList[g2], {42 \[UndirectedEdge] 37}], 
   VertexList[g2]], VertexLabels -> "Name", ImagePadding -> 10, 
   GraphHighlightStyle -> "DehighlightHide"]

Re-layout graph after small modification while preserving features of original layout

Now show the graph with the "new edge" added.

Re-layout graph after small modification while preserving features of original layout

This looks very different from Figure 1. But it seems to be a natural extension of Fig. 4.

Adding new vertices and edges on-the-fly

There is another way to add edges (and vertices) while maintaining the overall appearance. It was inspired by something Sjoerd wrote in his response.

Let's reserve the point {0,0} for a future vertex 99. We simply add that point to the VertexCoordinates from g2:

vc = VertexCoordinates -> 
  Append[AbsoluteOptions[g2, VertexCoordinates][[2]], {0, 0}]

Now let's see what it looks like. g3 is just g2 with the additional vertex (999) and edge (4,99).

g3 = Graph[Append[EdgeList [g2], 4 \[UndirectedEdge] 999], vc, 
  VertexLabels -> "Name", ImagePadding -> 10, 
  GraphHighlightStyle -> "DehighlightHide", ImageSize -> 500]

Re-layout graph after small modification while preserving features of original layout

This procedure allows us to add new edges and vertices as we move forward. But some trial and error will be needed to ensure that the new vertices are located in a suitable position.

Adding only another edge (without a new vertex) is much easier: just add the new edge and use the VertexCoordinates from the prior graph.

You should be able to delete edges from a graph using the same approach (using same VertexCoordinates).


As you know there are several graph formats floating around in MMA. We have the Combinatorica package format, the GraphPlot format and the M8 Graph format.

GraphPlot
You can find the coordinates of GraphPlot nodes as follows.

GraphPlot[{1 -> 2, 2 -> 3, 3 -> 1, 3 -> 4}, DirectedEdges -> True,
           VertexLabeling -> True]

Re-layout graph after small modification while preserving features of original layout

This plot can be manually manipulated. You can still find both the old and the new coordinates in it:

Re-layout graph after small modification while preserving features of original layout

VertexCoordinateRules -> {{0.000196475, 0.}, {0.,0.847539}, 
                          {0.916405, 0.423865}, {2.03143, 0.42382}}

Re-layout graph after small modification while preserving features of original layout

VertexCoordinateRules -> {{0.000196475, 0.}, {0., 0.847539}, 
                          {1.07187,0.708887}, {1.9537, 0.00924285}}

You can draw the plot again using the modified coordinates:

GraphPlot[{1 -> 2, 2 -> 3, 3 -> 1, 3 -> 4}, DirectedEdges -> True,
          VertexLabeling -> True, newRules]

Re-layout graph after small modification while preserving features of original layout

or draw a new graph

GraphPlot[{1 -> 2, 2 -> 3, 3 -> 1, 3 -> 4, 1 -> 5, 5 -> 4}, 
          DirectedEdges -> True, VertexLabeling -> True]

that by default looks like this:

Re-layout graph after small modification while preserving features of original layout

using the old coordinates:

updatedRules = VertexCoordinateRules -> 
                 Append[VertexCoordinateRules /. newRules, {1, 0}];
GraphPlot[{1 -> 2, 2 -> 3, 3 -> 1, 3 -> 4, 1 -> 5, 5 -> 4}, 
           DirectedEdges -> True, VertexLabeling -> True, updatedRules]

Re-layout graph after small modification while preserving features of original layout


Graph

I don't think you can manipulate a Graph as you can a GraphPlot, but you can access its vertex coordinates.

GraphData["AGraph"]

Re-layout graph after small modification while preserving features of original layout

oldCoords = AbsoluteOptions[GraphData["AGraph"], VertexCoordinates]

(* ==>  VertexCoordinates -> {{1., 2.}, {2., 3.}, {2., 1.}, {1.,1.}, 
                       {1., 3.}, {2., 2.}} *)

It is good to have these old coordinates because if we re-create this graph using its adjacency matrix its layout is slightly different. This can be restored using the old coordinates.

Re-layout graph after small modification while preserving features of original layout


You might want to check if the GraphLayout option helps with your graph in problem.

I checked all the combinations of possible values of ComponentLayout and PackingLayout with an example graph (graph0 and graph1 which is graph0 with one edge removed, in the following code). Some combinations definitely look more useful for your purpose (changes the graph layout less when an edge is removed. I find

"ComponentLayout" -> "CircularEmbedding"
"ComponentLayout" -> "LayeredDrawing"
"ComponentLayout" -> "SpiralEmbedding"

preserve the layout the best.

The code to show all combinations is

In[5]:= Quit
In[12]:= $COMPONENTLAYOUTS={(*Automatic,None,*)"CircularEmbedding","HighDimensionalEmbedding","LayeredDrawing","LinearEmbedding","RadialEmbedding","RandomEmbedding","SpiralEmbedding","SpringElectricalEmbedding","SpringEmbedding"};
$PACKINGLAYOUTS={"ClosestPacking","ClosestPackingCenter","Layered","LayeredLeft","LayeredTop","NestedGrid"};
layoutopt[c_,p_]:=GraphLayout-> {"ComponentLayout"->$COMPONENTLAYOUTS[[ c]],"PackingLayout"-> $PACKINGLAYOUTS[[p]]};
In[4]:= words=DictionaryLookup["*zz"];
In[5]:= graph0=Flatten[Map[(Thread[#\[DirectedEdge]DeleteCases[Nearest[words,#,3],#]])&,words]];
i=RandomInteger[{1,Length[graph0]}];
graph0[[i]]
graph1=Drop[graph0,{i}];
Out[7]= tizz\[DirectedEdge]fizz
In[18]:= g0[i_,j_]:=Graph[graph0,VertexLabels->"Name",ImagePadding->20,ImageSize->200,layoutopt[i,j]];
g1[i_,j_]:=Graph[graph1,VertexLabels->"Name",ImagePadding->20,ImageSize->200,layoutopt[i,j]]

Column[Grid/@Table[
{
$COMPONENTLAYOUTS[[c]],
$PACKINGLAYOUTS[[p]],
g0[c,p],
g1[c,p]
},
{c,1,Length[$COMPONENTLAYOUTS]},
{p,1,Length[$PACKINGLAYOUTS]}
]]


This is at best a partial answer. Also, I am working with Mma 7.

If I modify a graph such that it now contains an 'orphan' vertex (no connecting edges) but I still want to show the vertex on a new graph, this may be done by converting to an adjacency matrix (as originally pointed out by Carl Woll)

For example:

gr1 = {1 -> 2, 2 -> 3, 3 -> 4, 4 -> 5, 5 -> 6, 6 -> 1};
gplot1 = GraphPlot[gr1, Method -> "CircularEmbedding", 
  VertexLabeling -> True]

Defining a new graph, gr2, as follows:

gr2 = {2 -> 3, 3 -> 4, 4 -> 5, 5 -> 6}

A new plot showing vertex 1 may be generated as follows, for example:

Needs["GraphUtilities`"];

 gplot2 = 
 GraphPlot[SparseArray@Map[# -> 1 &, EdgeList[gr2]], 
  VertexLabeling -> True, 
  VertexCoordinateRules -> 
   Thread[VertexList[gr1] -> 
     First@Cases[gp1, GraphicsComplex[points_, __] :> points, 
       Infinity]]]

giving

Re-layout graph after small modification while preserving features of original layout

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号