在小修改后重新布局图形,同时保留原始布局的特征

时间:2011-07-01 15:41:57

标签: wolfram-mathematica mathematica-8 graph-visualization

在Mathematica 8中有一种简单的方法可以执行以下操作吗?

  1. 构建图表,并使用一些图表布局显示它。
  2. 稍微修改图形(例如,添加或删除边或顶点)。
  3. 从原始布局开始重新计算布局,以便或多或少地保留对象的“形状”。例如。重新运行弹簧电动布局算法,从先前布局的坐标开始。
  4. 如果图形在两个显示之间没有变化,则布局不应该改变(或者只是最小化)。使用新GraphGraphPlot的显示都是可以接受的。

    编辑:本质上我需要类似图表的类似布局。我总是通过修改现有的图表来获得类似的图表,这些图表可能已经布局,但任何通用的解决方案都是可以接受的。

    编辑2:以下是此类事情有用的示例。转到http://ccl.northwestern.edu/netlogo/models/GiantComponent并单击“在浏览器中运行”(需要Java)。单击设置,然后单击执行。您可以看到图表的演变。如果我们在Mathematica中这样做,那么每个连续的图形看起来都会完全不同,并且很难看出它是同一个正在发展的图形。在几个应用程序中,能够可视化图形的小变化是非常有用的。但是,如果进行了许多连续的更改,那么重新计算布局是必须的,简单地淡化或突出显示边缘是不够的。同样,这只是一个例子:我不是试图使用Mathematica来动画图形,或者想象巨型组件的出现。

4 个答案:

答案 0 :(得分:9)

以下是在MMA 8.0中更改图表的两种基本方法。第一个依赖于HighlightGraph,特别是GraphHighlightStyle -> "DehighlightHide"。第二种方法在该图的未来变体中使用图的VertexCoordinates。

我们将与添加分开讨论删除,因为它们涉及的方法略有不同。

[P.S。 :我对我的答案进行了多次编辑,以使其更清晰。]

首先是一些数据:

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}

这是初始图表:

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

Fig1

“删除”图表边缘而不更改图表的整体外观。

让我们开始移除位于图形中心的边缘(4,11)。 remainingEdgesAndVertices包含所有顶点和初始边,但edge(4,11)除外。

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

让我们“删除”(即隐藏)边缘(4,11):

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

Fig2

如果我们实际上已经删除了边缘(4,11),那么图形将从根本上改变它的外观。

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

Fig3

“添加”图表边缘而不更改图表的整体外观。

添加图形边缘更具挑战性。有两种方式可以想到。这里使用的方法向后工作。您首先以隐藏形式包含新边缘,然后稍后将其揭开。具有隐藏的“待添加”边缘的初始图形将采用类似于具有“新”边缘的图形的布局。原因是:它们实际上是相同的图形:但它们显示不同数量的边缘。

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"]

Fig4

现在显示添加了“新边缘”的图表。 Fig

这看起来与图1非常不同。但它似乎是图4的自然延伸。

即时添加新的顶点和边

还有另一种方法可以在保持整体外观的同时添加边(和顶点)。它的灵感来自Sjoerd在回应中所写的内容。

让我们为未来的顶点99保留点{0,0}。我们只需将该点添加到g2中的VertexCoordinates

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

现在让我们看看它的样子。 g3只是g2,带有附加顶点(999)和边缘(4,99)。

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

Fig6

此过程允许我们在向前移动时添加新的边和顶点。但是需要一些试验和错误来确保新顶点位于合适的位置。

只添加另一条边(没有新的顶点)要容易得多:只需添加新边并使用前一图中的VertexCoordinates即可。

您应该能够使用相同的方法从图表中删除边缘(使用相同的VertexCoordinates)。

答案 1 :(得分:6)

如您所知,MMA中有几种图表格式。我们有Combinatorica包格式,GraphPlot格式和M8 Graph格式。

<强> GraphPlot
您可以按如下方式找到GraphPlot个节点的坐标。

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

enter image description here

此图可以手动操作。您仍然可以在其中找到旧坐标和新坐标:

enter image description here

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

enter image description here

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

您可以使用修改后的坐标再次绘制绘图:

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

enter image description here

或绘制新图

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

默认情况下如下:

enter image description here

使用旧坐标:

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]

enter image description here


图表

我认为你不能像Graph那样操纵GraphPlot,但你可以访问它的顶点坐标。

GraphData["AGraph"]

enter image description here

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

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

拥有这些旧坐标是件好事,因为如果我们使用其邻接矩阵重新创建此图形,其布局会略有不同。这可以使用旧坐标恢复。

enter image description here

答案 2 :(得分:0)

您可能想要检查GraphLayout选项是否有助于解决问题中的图表。

我使用示例图表(ComponentLayoutPackingLayout检查了graph0graph1的所有可能值的组合,其中graph0删除了一个边缘,在以下代码中)。某些组合看起来确实对您的目的更有用(当删除边缘时更改图形布局。我找到

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

保持最佳布局。

显示所有组合的代码是

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]}
]]

答案 3 :(得分:0)

这至多是部分答案。此外,我正在与Mma 7合作。

如果我修改图形使其现在包含'孤儿'顶点(没有连接边)但我仍想在新图上显示顶点,这可以通过转换为adjacency matrix来完成(正如Carl Woll最初指出的那样)

例如:

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

定义新图形gr2,如下所示:

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

可以如下生成显示顶点1的新图,例如:

Needs["GraphUtilities`"];

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

enter image description here