ノードとエッジを使って,「つながり」「ながれ」「かかわり」といった関係を描写する 2/3
有向グラフの場合
イントロダクション
Step 0シチュエーションの設定
説明上の必要から,「1.無向グラフの場合」で作成した関係性のグラフをイメージとして引用します(下図)。ここではこの組織に,あるあたらしいワークが発生したとして話をすすめます。
さて,このワークでは,成果物を各部門のリーダーが査収します。したがって,先の無向グラフに方向性を組み入れるとしたなら,エッジは次のような突端を持ちえます。
さらにはこのワークは,従来のようにリーダー間で調整をなすのではなく,専任の管理者をおいてすすめられることになりました。
管理者「Iris」は,成果物の査収に加え各部のリーダーとの間で諸事の調整を図ります。これを示すに,「Iris」へのエッジには,その両端に矢を加えるものとします。
ここでは,以上のような関係性を図示していきます。
基本的な流れは前頁と同様です。したがってRパッケージのインストールなど作業に従となる要素についての言及は省きます。
さて,ここで目的とする表現は,下図円形ノードからなるシンプルなグラフと,
下図ピクチャノードによるグラフの2点です。
illustration: "Girl's Design Materials"
仕様
- 自己ループおよび同じ方向の多重エッジには非対応です
作図にあたり参考にした書籍およびWebページ(「1.無向グラフの場合」と同じ)
- 杉山公造(1993)『グラフ自動描画法とその応用 ―ビジュアルヒューマンインタフェース―』計測自動制御学会編, コロナ社, pp.8-16., 77-81.
- R+igraph ―"Kazuhiro Takemoto"(2017.1閲覧)
- igraphパッケージの使い方 1.グラフオブジェクトの作成と取り扱い, igraphパッケージの使い方 2.グラフオブジェクトのプロット ―"もうカツ丼でいいよな"(2017.1閲覧)
- グラフ・ネットワーク分析で遊ぶ(1):グラフ可視化・描画手法 ―"六本木で働くデータサイエンティストのブログ"(2017.1閲覧)
- 隣接行列 ―"WTOPIA v1.0 documentation"(2017.1閲覧)
工程|前段
Step 1[隣接行列シート]ノードの配置
ワークの“ながれ”を行列によってあらわします。ここでは,このためのシートを「隣接行列」と名付けます。
「隣接行列」シートの縦方向ないしは横方向にすべてのノードを書き出して,それらを残る一方に対し「行列を入れ替える」でコピーします。
scrollable
Step 2[隣接行列シート]“ながれ”の書き出し(1)
各ノードに関し,行方向に“ながれ”の起(始)点を示していきます。
たとえばここでの例の場合,Step 0の設定では,ノード「Iris」は各部門のリーダー3人とエッジを持っていることがわかります。したがって,「Iris」行に関しては,列見出しの該当するノードとの交点にそれぞれ「1」を立てていきます。
scrollable
残るメンバーに関しても同様の作業をおこないます。注意点として,たとえば次行「Sarah」の場合,Step 0の設定では,周囲に5本のエッジが存在しているのがわかります。有向グラフの場合,問題とするのはあくまで起点だけ(=「Sarah」から始まるエッジか)なので,「Sarah」行に立てるtrue(1)は「Iris」列との交点のみとなります。
そんな感じでこれらを処理したものが下図です。
scrollable
Step 3[隣接行列シート]“ながれ”の書き出し(2)
この行列の中の未入力の場所,換言すれば起点のエッジを持たないものを明示します。
最初にこの行列以外の,たとえばシートの外れにでも「0」と入力し,これをコピーします。
次に行列の見出しを除くすべての部分を選択し
「形式を選択して貼り付け」から
演算「加算」の貼り付けを選びます。
これにより,「1」以外の空のセルが「0」で埋まります。この時点で,一時的に利用する目的で入力した先の0を削除しておきます。
scrollable
Step 4[座標シート]見出しの作成(1)
あたらしい別のシートに,グラフ描画に必要なデータを用意していきます。ここではこのシートを,便宜上「座標」シートと呼ぶことにします。
さしずめ,以下の場所にそれぞれ見出しを入力しておきます。
ピクチャノードを利用する場合は,セルB1に,素材を格納してあるフォルダまでのフルパスを入力しておき(したがってすべての素材が同じフォルダの中に準備されている必要があります)
「隣接行列」シートから,縦に並べたすべてのノード(行見出し)をコピーして,この「座標」シートに貼り付けます。
ピクチャノードを利用する場合には,「File」欄に,各ノードに対応するファイルの名前をそれぞれ指定しておきます。
工程|中段
Step 5[R]igraphパッケージの読み込み
Rを起動します。
ここでは別途「igraph」パッケージを必要とします。これがRにインストールされていない場合,インストールを要します。とまれ
- library(igraph)
にてパッケージを呼び出します。
scrollable
Step 6[R]ノード座標の計算
Excelに戻り「隣接行列」シートの行列を見出しも含めて選択し,クリップボードにコピーしておきます。
再びRに移って,コンソールに
- dat <- as.matrix(read.table("clipboard", header=T, row.names=1))
と入力します。これにより,オブジェクトdatにクリップボードの内容を見出し付きの行列として保持させます。
scrollable
続けて
- g <- graph.adjacency(dat, mode="directed")
と入力します。これにより,datを“方向性を持つ”エッジリストに転換し,この結果をオブジェクトgに保持させます。
scrollable
このgの内容がExcelで利用したいもののうちの1つですので,これをテキストファイルに書き出します。
ここではテキストファイルを作業ディレクトリにそのまま書き出します。したがってファイル名(edgelist.txt)のみしか記していませんが,任意の場所に出力したい場合,その場所までをフルパスで明示する必要があります。
- write_graph(g, "edgelist.txt", "ncol")
scrollable
gをもとに,任意のアルゴリズムを使ってノードの座標を求めます。結果はオブジェクトlayに渡します。
さて,そのアルゴリズムですが,ここではKamada-Kawai layout algorithmを使用するものとします。
- lay <- layout_with_kk(g)
※他のアルゴリズムについては,「1.無向グラフの場合」を参照。
scrollable
このlayの内容がExcelで利用したいものの残る1つですので,これをテキストファイルに書き出します。
ここでも同様,テキストファイルを作業ディレクトリに書き出すのでファイル名(node.txt)のみしか記していません。任意の場所に出力したい場合,その場所までフルパスで明示することが必要です。
- write.table(lay, file="node.txt", row.names=F, col.names=F)
scrollable
Step 7[座標シート]計算結果をExcelへ
先のStepで作成したnode.txtを開きます。
このファイルの内容をすべて選択し,クリップボードにコピーします。
「座標」シートのセルB4をアクティブにして,貼り付けテキストファイルウィザードを使用,とたどります。
ダイアログの「元のデータ形式」が「カンマやタブなど」側が選択されていることを確認して,完了を返します。
同様の作業をedgelist.txtにおいても繰り返し,セルF4にデータを貼り付けます。
scrollable
このとき,G列の内容を行位置を保ったままI列に移します。
scrollable
F列とI列のノード名の間を,マイナス記号(-)で埋めておきます。
scrollable
工程|後段
Step 8[座標シート]散布図で“ながれ”を描く
これまでに求めた座標にいくらかの調整を加え,それらを使って目的のグラフを描きます。
これは手間を必要とするので,ここではマクロで処理します。上述のシート構成の場合のみ,下のコードが機能します。
Const ratio As Double = 0.1 ' 重み Sub DrawDirectedGraph() ' "ながれ" 描写のための有向グラフ ver.17.0129 ' *** bdastyle.net/tools/scatterplot/network-visualization-based-on-force-directed-layout-2.html ' *** by hawcas 2017 Dim tgtNode As Range ' Nodeのデータ範囲 Dim tgtEdge As Range ' Edgeのデータ範囲 Dim n As Long ' リストの数-1 Dim capSP() As String ' 始点ノード Dim capEP() As String ' 終点ノード Dim naEG() As Boolean ' 非描画エッジ@双方向 Dim apEG() As Boolean ' 描画エッジ@双方向 Dim cntUD As Long ' 無向エッジのカウンタ Dim myFormula(14) As String ' 計算式 Dim serNo As Integer ' 系列番号 Dim serName As String ' 系列名 Dim myDir As String ' 画像ディレクトリ Dim adLabel As String ' ラベルのアドレス Dim myStr As String Dim i As Long Dim j As Long Dim k As Long Application.ScreenUpdating = False ' シートをコピー myStr = ActiveSheet.Name ActiveSheet.Copy after:=Worksheets(myStr) ' ノードに関するセル範囲を格納 Set tgtNode = Range("a3").CurrentRegion Set tgtNode = tgtNode.Range("a2").Resize(RowSize:=tgtNode.Rows.Count - 1, columnsize:=4) ' エッジに関するセル範囲を格納 Set tgtEdge = Range("f3").CurrentRegion Set tgtEdge = tgtEdge.Range("a2").Resize(RowSize:=tgtEdge.Rows.Count - 1, columnsize:=8) Call Heading ' 見出しを作成 ' エッジの始点/終点のノード名を格納 n = tgtEdge.Rows.Count - 1 ReDim capSP(n) ReDim capEP(n) ReDim naEG(n) ReDim apEG(n) For i = 0 To n capSP(i) = Range("f4").Offset(i, 0).Value capEP(i) = Range("f4").Offset(i, 3).Value Next ' エッジリストを書き換える cntUD = 0 k = 0 For i = 0 To n ' 終点ノード For j = k To n ' 始点ノード If capSP(j) = capEP(i) Then If capSP(i) = capEP(j) Then apEG(i) = True ' 描画 naEG(j) = True ' 非描画 cntUD = cntUD + 1 Exit For End If End If Next k = k + 1 Next j = 0 For i = 0 To n If naEG(i) = False Then With Range("f4") .Offset(j, 0).Value = capSP(i) .Offset(j, 2).Value = ">" .Offset(j, 3).Value = capEP(i) If apEG(i) = True Then ' 双方向の場合 .Offset(j, 1).Value = "<" End If End With j = j + 1 End If Next If Not (cntUD = 0) Then ' 余剰が出た場合それを削除 Range(Range("f4").Offset(n + 1 - cntUD, 0), Range("f4").Offset(n, 3)).Delete End If ' tgtEdge の修正 Set tgtEdge = tgtEdge.Range("a1").Resize(RowSize:=n + 1 - cntUD, columnsize:=19) ' 計算式を作ってシートに配置 n = tgtEdge.Rows.Count - 1 For i = 0 To n myFormula(0) = ex1(tgtEdge, tgtNode, i) myFormula(1) = ex2(tgtEdge, tgtNode, i) myFormula(2) = ey1(tgtEdge, tgtNode, i) myFormula(3) = ey2(tgtEdge, tgtNode, i) myFormula(4) = hypotenuse(tgtEdge, i) myFormula(5) = flgx(tgtEdge, i) myFormula(6) = flgy(tgtEdge, i) myFormula(7) = direction(tgtEdge, i) myFormula(8) = rad(tgtEdge, i) k = 0 For j = 4 To 12 tgtEdge.Range("a1").Offset(i, j).Formula = myFormula(k) k = k + 1 Next Next Range("n1").Formula = "=average(" & Range(tgtEdge.Range("a1").Offset(0, 8), _ tgtEdge.Range("a1").Offset(n, 8)).Address & ")*" & ratio ' marginを設置 For i = 0 To n myFormula(9) = mbase(tgtEdge, i) myFormula(10) = mheight(tgtEdge, i) myFormula(11) = ex1prime(tgtEdge, i) myFormula(12) = ex2prime(tgtEdge, i) myFormula(13) = ey1prime(tgtEdge, i) myFormula(14) = ey2prime(tgtEdge, i) k = 9 For j = 13 To 18 tgtEdge.Range("a1").Offset(i, j).Formula = myFormula(k) k = k + 1 Next Next ' 散布図を挿入 Range(tgtNode.Range("b1"), tgtNode.Range("b1").Offset(tgtNode.Rows.Count - 1, 1)).Select ActiveSheet.Shapes.AddChart(xlXYScatter).Select ' ノード系列の基本的な書式設定 serNo = 1 With ActiveChart .HasLegend = False With .SeriesCollection(serNo) .Name = "Node" .MarkerStyle = xlMarkerStyleCircle .MarkerForegroundColorIndex = xlColorIndexNone ' 枠線なし .MarkerBackgroundColor = RGB(80, 80, 80) ' マーカーの色 End With End With ' エッジ系列の挿入 For i = 0 To tgtEdge.Rows.Count - 1 serName = "" For j = 0 To 3 serName = serName & tgtEdge.Range("a1").Offset(i, j) Next With ActiveChart.SeriesCollection.NewSeries .XValues = Range(tgtEdge.Range("a1").Offset(i, 15), tgtEdge.Range("a1").Offset(i, 16)) .Values = Range(tgtEdge.Range("a1").Offset(i, 17), tgtEdge.Range("a1").Offset(i, 18)) .Name = serName .Border.Color = RGB(168, 0, 0) ' 線色 .Format.Line.Weight = 1.5 'pt, 線幅 End With Next ' エッジを直線に For serNo = 2 To tgtEdge.Rows.Count + 1 With ActiveChart.SeriesCollection(serNo) .ChartType = xlXYScatterLinesNoMarkers ' 散布図→直線 .Format.Line.EndArrowheadStyle = msoArrowheadOpen ' 終点:開いた矢印 If apEG(serNo - 2) = True Then .Format.Line.BeginArrowheadStyle = msoArrowheadOpen ' 始点:開いた矢印 End If End With Next ' ピクチャノードか否か For i = 0 To tgtNode.Rows.Count - 1 Select Case tgtNode.Range("d1").Offset(i, 0).Value Case "" ' File未指定の場合→円形ノード With ActiveSheet.ChartObjects(1).Chart.SeriesCollection(1).Points(i + 1) .MarkerSize = 14 ' pt, マーカーサイズ End With Case Else ' File指定の場合→ピクチャノード myDir = Range("b1").Value & "\" & tgtNode.Range("d1").Offset(i, 0).Value With ActiveSheet.ChartObjects(1).Chart.SeriesCollection(1).Points(i + 1) '.MarkerStyle = xlMarkerStylePicture ' 「塗り色」としてではなくピクチャそのものを指定する場合 .MarkerSize = 36 ' pt, マーカーサイズ ' 「塗り色」としてではなくピクチャそのものを指定する場合,ここはコメントアウト .Fill.UserPicture (myDir) End With End Select Next ' データラベルを挿入 adLabel = "'" & ActiveSheet.Name & "'!" & _ Range(tgtNode.Range("a1"), tgtNode.Range("a1").Offset(tgtNode.Rows.Count - 1, 0)).Address With ActiveSheet.ChartObjects(1).Chart.SeriesCollection(1) .HasDataLabels = True With .DataLabels .Format.TextFrame2.TextRange.InsertChartField msoChartFieldRange, (adLabel), 0 .ShowRange = True .ShowValue = False .Position = xlLabelPositionBelow End With End With Application.ScreenUpdating = True End Sub Private Sub Heading() Dim myList As Variant Dim i As Integer myList = Array( _ "ex1", "ex2", "ey1", "ey2", _ "hypotenuse", _ "flg-x", "flg-y", "direction", _ "rad", "m-base", "m-height", _ "ex1'", "ex2'", "ey1'", "ey2'" _ ) For i = 0 To 14 Range("j3").Offset(0, i).Value = myList(i) Next Range("m1").Value = "margin" End Sub Private Function ex1(myErange As Range, myNrange As Range, myRow As Long) As String ex1 = "=vlookup(" & myErange.Range("a1").Offset(myRow, 0). _ Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ", " & myNrange.Address & ", 2, false)" ' ex1(J) End Function Private Function ex2(myErange As Range, myNrange As Range, myRow As Long) As String ex2 = "=vlookup(" & myErange.Range("a1").Offset(myRow, 3). _ Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ", " & myNrange.Address & ", 2, false)" ' ex2(K) End Function Private Function ey1(myErange As Range, myNrange As Range, myRow As Long) As String ey1 = "=vlookup(" & myErange.Range("a1").Offset(myRow, 0). _ Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ", " & myNrange.Address & ", 3, false)" ' ey1(L) End Function Private Function ey2(myErange As Range, myNrange As Range, myRow As Long) As String ey2 = "=vlookup(" & myErange.Range("a1").Offset(myRow, 3). _ Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ", " & myNrange.Address & ", 3, false)" ' ey2(M) End Function Private Function hypotenuse(myErange As Range, myRow As Long) As String Dim obj As Range Set obj = myErange.Range("a1") hypotenuse = "=sqrt((" & obj.Offset(myRow, 5). _ Address(RowAbsolute:=False, ColumnAbsolute:=False) & "-" & _ obj.Offset(myRow, 4). _ Address(RowAbsolute:=False, ColumnAbsolute:=False) & ")^2+(" & _ obj.Offset(myRow, 7). _ Address(RowAbsolute:=False, ColumnAbsolute:=False) & "-" & _ obj.Offset(myRow, 6). _ Address(RowAbsolute:=False, ColumnAbsolute:=False) & ")^2)" ' hypotenuse(N) End Function Private Function flgx(myErange As Range, myRow As Long) As String Dim obj As Range Set obj = myErange.Range("a1") flgx = "=if((" & obj.Offset(myRow, 5). _ Address(RowAbsolute:=False, ColumnAbsolute:=False) & "-" & _ obj.Offset(myRow, 4). _ Address(RowAbsolute:=False, ColumnAbsolute:=False) & ")>0, 1, if((" & _ obj.Offset(myRow, 5). _ Address(RowAbsolute:=False, ColumnAbsolute:=False) & "-" & _ obj.Offset(myRow, 4). _ Address(RowAbsolute:=False, ColumnAbsolute:=False) & ")<0, -1, 0))" ' flg-x(O) End Function Private Function flgy(myErange As Range, myRow As Long) As String Dim obj As Range Set obj = myErange.Range("a1") flgy = "=if((" & obj.Offset(myRow, 7). _ Address(RowAbsolute:=False, ColumnAbsolute:=False) & "-" & _ obj.Offset(myRow, 6). _ Address(RowAbsolute:=False, ColumnAbsolute:=False) & ")>0, 1, if((" & _ obj.Offset(myRow, 7). _ Address(RowAbsolute:=False, ColumnAbsolute:=False) & "-" & _ obj.Offset(myRow, 6). _ Address(RowAbsolute:=False, ColumnAbsolute:=False) & ")<0, -1, 0))" ' flg-y(P) End Function Private Function direction(myErange As Range, myRow As Long) As String Dim obj As Range Set obj = myErange.Range("a1") direction = "=if(and(" & obj.Offset(myRow, 9).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=1, " & obj.Offset(myRow, 10).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=0), 1, if(and(" & obj.Offset(myRow, 9).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=0, " & obj.Offset(myRow, 10).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=1), 2, if(and(" & obj.Offset(myRow, 9).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=-1, " & obj.Offset(myRow, 10).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=0), 3, if(and(" & obj.Offset(myRow, 9).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=0, " & obj.Offset(myRow, 10).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=-1), 4, if(and(" & obj.Offset(myRow, 9).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=1, " & obj.Offset(myRow, 10).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=1), 5, if(and(" & obj.Offset(myRow, 9).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=-1, " & obj.Offset(myRow, 10).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=1), 6, if(and(" & obj.Offset(myRow, 9).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=-1, " & obj.Offset(myRow, 10).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=-1), 7, if(and(" & obj.Offset(myRow, 9).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=1, " & obj.Offset(myRow, 10).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=-1), 8, 9))))))))" ' direction(Q) End Function Private Function rad(myErange As Range, myRow As Long) As String Dim obj As Range Set obj = myErange.Range("a1") rad = "=if((" & obj.Offset(myRow, 5).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "-" & obj.Offset(myRow, 4).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ")=0, 0, abs(atan((" & obj.Offset(myRow, 7).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "-" & obj.Offset(myRow, 6).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ")/(" & obj.Offset(myRow, 5).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "-" & obj.Offset(myRow, 4).Address(RowAbsolute:=False, ColumnAbsolute:=False) & "))))" ' rad(R) End Function Private Function mbase(myErange As Range, myRow As Long) As String Dim obj As Range Set obj = myErange.Range("a1") mbase = "=if(or(" & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=1, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=3, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=5, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=6, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=7, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=8), " & Range("n1").Address & "*cos(" & obj.Offset(myRow, 12). _ Address(RowAbsolute:=False, ColumnAbsolute:=False) & "), 0)" ' m-base(S) End Function Private Function mheight(myErange As Range, myRow As Long) As String Dim obj As Range Set obj = myErange.Range("a1") mheight = "=if(or(" & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=5, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=6, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=7, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=8), " & Range("n1").Address & "*sin(" & obj.Offset(myRow, 12). _ Address(RowAbsolute:=False, ColumnAbsolute:=False) & "), " & Range("n1").Address & ")" ' m-height(T) End Function Private Function ex1prime(myErange As Range, myRow As Long) As String Dim obj As Range Set obj = myErange.Range("a1") ex1prime = "=if(" & obj.Offset(myRow, 1).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=""<"", if(or(" & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=1, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=5, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=8), " & obj.Offset(myRow, 4).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "+" & obj.Offset(myRow, 13).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ", if(or(" & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=3, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=6, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=7), " & obj.Offset(myRow, 4).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "-" & obj.Offset(myRow, 13).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ", if(or(" & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=2, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=4), " & obj.Offset(myRow, 5).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ", if(" & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=9," & obj.Offset(myRow, 4).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ")))), " & obj.Offset(myRow, 4).Address(RowAbsolute:=False, ColumnAbsolute:=False) & ")" ' ex1'(U) End Function Private Function ex2prime(myErange As Range, myRow As Long) As String Dim obj As Range Set obj = myErange.Range("a1") ex2prime = "=if(or(" & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=1, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=5, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=8), " & obj.Offset(myRow, 5).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "-" & obj.Offset(myRow, 13).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ", if(or(" & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=3, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=6, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=7), " & obj.Offset(myRow, 5).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "+" & obj.Offset(myRow, 13).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ", if(or(" & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=2, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=4), " & obj.Offset(myRow, 5).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ", if(" & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=9," & obj.Offset(myRow, 4).Address(RowAbsolute:=False, ColumnAbsolute:=False) & "))))" ' ex2'(V) End Function Private Function ey1prime(myErange As Range, myRow As Long) As String Dim obj As Range Set obj = myErange.Range("a1") ey1prime = "=if(" & obj.Offset(myRow, 1).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=""<"", if(or(" & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=2, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=5, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=6), " & obj.Offset(myRow, 6).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "+" & obj.Offset(myRow, 14).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ", if(or(" & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=4, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=7, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=8), " & obj.Offset(myRow, 6).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "-" & obj.Offset(myRow, 14).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ", if(or(" & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=1, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=3), " & obj.Offset(myRow, 6).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ", if(" & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=9," & obj.Offset(myRow, 6).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ")))), " & obj.Offset(myRow, 6).Address(RowAbsolute:=False, ColumnAbsolute:=False) & ")" ' ey1'(W) End Function Private Function ey2prime(myErange As Range, myRow As Long) As String Dim obj As Range Set obj = myErange.Range("a1") ey2prime = "=if(or(" & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=2, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=5, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=6), " & obj.Offset(myRow, 7).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "-" & obj.Offset(myRow, 14).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ", if(or(" & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=4, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=7, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=8), " & obj.Offset(myRow, 7).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "+" & obj.Offset(myRow, 14).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ", if(or(" & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=1, " & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=3), " & obj.Offset(myRow, 7).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ ", if(" & obj.Offset(myRow, 11).Address(RowAbsolute:=False, ColumnAbsolute:=False) & _ "=9," & obj.Offset(myRow, 6).Address(RowAbsolute:=False, ColumnAbsolute:=False) & "))))" ' ey2'(X) End Function
Step 9グラフの完成
マクロによって,円形のノードの場合は下図のような,
ピクチャノードの場合は下図のようなアウトプットが導けます。
下では無向グラフのように見えますが,これはピクチャノードの場合,初期の設定値ではエッジの突端がノードによって遮蔽されるゆえ,です。
エッジの長さは,表上部の「margin」にて任意に調整できます。0< margin <1の範囲(双方向が混在する場合,上限はこの半分程度)で,値が大きなほど短くなります。
先のようにエッジの突端が隠れた場合など,これによって調整が可能です。
scrollable
縦横軸の表示範囲を調整する,あるいはデータラベルに書式設定を加えるなどして任意の加工を続け,目的のグラフの完成です。
Next
その他の参照
このサイトの関連How-toです。