nysol-view 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/mgv.rb ADDED
@@ -0,0 +1,809 @@
1
+ #!/usr/bin/env ruby
2
+ #-*- coding: utf-8 -*-
3
+
4
+ require "rubygems"
5
+ require "nysol/mcmd"
6
+ require "set"
7
+
8
+ # ver="1.0" # 初期リリース 2016/11/07
9
+ # ver="1.1" # k=除外対応 2016/11/08
10
+ # ver="1.2" # nested graph対応,色対応 2016/11/09
11
+ $cmd=$0.sub(/.*\//,"")
12
+
13
+ $version=1.2
14
+ $revision="###VERSION###"
15
+
16
+ def help
17
+
18
+ STDERR.puts <<EOF
19
+ ------------------------
20
+ #{$cmd} version #{$version}
21
+ ------------------------
22
+ 概要) CSVによるグラフ構造データをDOTフォーマットで出力する
23
+
24
+ 書式1) #{$cmd} [type=flat|nest] [k=] [ni=] [nf=] [nv=] [nc=] [nr=] [col=] [nl=] [nw=]
25
+ [-clusterLabel] [-noiso] ei= ef= [ev=] [er=] [el=] [-d] [o=]
26
+
27
+ type= : グラフのタイプ
28
+ flat: key項目をクラスタとした木構造グラフ
29
+ nest: 木構造であることを前提にした入れ子構造グラフ(データが木構造でない場合の描画は不定)
30
+ k= : 入れ子グラフのクラスタ項目名(ni=を指定した場合は同じ項目名でなければならない)
31
+ 複数項目指定不可
32
+ k=を省略すればtype=に関わらずクラスタを伴わない普通のグラフ描画となる。
33
+
34
+ ni= : 節点集合ファイル名
35
+ nf= : 節点ID項目名
36
+ nv= : 節点の大きさ項目名(この値に応じて節点の楕円の大きさが変化する,1項目のみ指定可)。
37
+ nc= : 節点カラー項目名(この値に応じて枠線カラーが変化する,1項目のみ指定可)。
38
+ カラーは、RGBを16進数2桁づつ6桁で表現する。ex) FF00FF:紫
39
+ さらに最後に2桁追加すればそれは透過率となる。
40
+ nr= : ノードの拡大率(デフォルト=3.0,最大10.0まで指定可能)
41
+ 節点の楕円のサイズを1.0〜nr=の値に基準化する。
42
+ すなわちnv=が最小の節点の大きさが1.0、最大の節点の大きさがnr=で指定した値となる。
43
+ nl= : ノードラベルの項目(複数指定したら"_"で区切って結合される)
44
+ nw= : 節点の枠線の幅を指定する(デフォルトは1)
45
+ -clusterLabel : k=を指定して入れ子グラフを作成する場合、クラスタのラベルも表示する
46
+ -noiso : 孤立節点(隣接節点のない節点)は出力しない
47
+
48
+ ei= : 枝集合ファイル名
49
+ ef= : 開始節点ID項目名,終了節点ID項目名
50
+ ev= : 枝の幅項目名(この値に応じて枝の幅(太さ)が変化する)
51
+ ec= : 枝の色を表す項目
52
+ ed= : 枝の矢印を表す項目(-dの指定、未指定に関わらず優先される)
53
+ 値としては、F,B,W,N,nullの5つの値のいずれかでなければならない。
54
+ ef=e1,e2とした場合、それぞれで描画される矢印は以下の通り。
55
+ F: e1->e2, B: e1<-e2, W: e1<->e2, N:e1-e2(矢印なし),null:デフォルト
56
+ デフォルトは、-dが指定されていればF、-dの指定がなければNとなる。
57
+ er= : エッジの拡大率(デフォルト=10.0,最大20.0まで指定可能)
58
+ 枝の太さを1.0〜er=の値に基準化する。
59
+ すなわちev=が最小の枝の太さが1.0、最大の枝の太さer=で指定した値となる。
60
+ el= : エッジラベルの項目(複数指定したら"_"で区切って結合される)
61
+
62
+ -d : 有向グラフと見なす。"edge [dir=none]"を記述する。
63
+ o= : 出力ファイル名
64
+
65
+ -h,--help : ヘルプの表示
66
+
67
+ 基本例)
68
+ $ cat node.csv
69
+ cluster%0,node%1,support
70
+ #1_1,a,0.4666666667
71
+ #1_1,b,0.4
72
+ #1_2,d,0.2666666667
73
+ #1_2,e,0.3333333333
74
+ #2_1,#1_1,0.6666666667
75
+ #2_1,c,0.3333333333
76
+ #3_1,#1_2,0.4
77
+ #3_1,#2_1,0.8
78
+ #3_1,f,0.2
79
+
80
+ $ cat edge.csv
81
+ cluster,node1,node2,support
82
+ #1_1,a,b,0.1
83
+ #1_2,d,e,0.1
84
+ #2_1,#1_1,c,0.2
85
+ #3_1,#1_2,#2_1,0.3
86
+ #3_1,#2_1,f,0.4
87
+
88
+ $ #{$cmd} k=cluster ni=node.csv nf=node nv=support ei=edge.csv ef=node1,node2 ev=support -el -nl nr=6.0 er=20 o=result.dot
89
+ # fdpはgraphVizのコマンド, nested graphはdotコマンドでは描画できない
90
+ $ fdp -Tpdf result.dot >result.pdf
91
+ $ open result.png
92
+ $ cat result.dot
93
+ digraph G {
94
+ edge [dir=none color="#00000050"]
95
+ n_15 [label="j_0.09090909091" height=0.5 width=0.75 style="setlinewidth(1)" ]
96
+ n_14 [label="i_0.09090909091" height=0.5 width=0.75 style="setlinewidth(1)" ]
97
+ subgraph cluster_4 {
98
+ n_12 [label="g_0.1818181818" height=0.857142857 width=1.2857142855 style="setlinewidth(1)" ]↩
99
+ n_13 [label="h_0.1818181818" height=0.857142857 width=1.2857142855 style="setlinewidth(1)" ]↩
100
+ n_12 -> n_13 [label="0.1818181818" style="setlinewidth(1)" ]↩
101
+ }
102
+ subgraph cluster_5 {
103
+ cluster_3 []
104
+ cluster_2 []
105
+ cluster_2 -> cluster_3 [label="0.2727272727" style="setlinewidth(20)" ]
106
+ subgraph cluster_2 {
107
+ n_6 [label="a_0.3636363636" height=1.5714285715 width=2.35714285725 style="setlinewidth(1)" ]
108
+ n_7 [label="b_0.3636363636" height=1.5714285715 width=2.35714285725 style="setlinewidth(1)" ]
109
+ n_9 [label="d_0.3636363636" height=1.5714285715 width=2.35714285725 style="setlinewidth(1)" ]
110
+ n_10 [label="e_0.4545454545" height=1.9285714285 width=2.89285714275 style="setlinewidth(1)" ]
111
+ n_6 -> n_7 [label="0.1818181818" style="setlinewidth(1)" ]
112
+ n_6 -> n_9 [label="0.1818181818" style="setlinewidth(1)" ]
113
+ n_7 -> n_9 [label="0.1818181818" style="setlinewidth(1)" ]
114
+ n_6 -> n_10 [label="0.2727272727" style="setlinewidth(20)" ]
115
+ n_7 -> n_10 [label="0.1818181818" style="setlinewidth(1)" ]
116
+ n_9 -> n_10 [label="0.2727272727" style="setlinewidth(20)" ]
117
+ }
118
+ subgraph cluster_3 {
119
+ n_8 [label="c_0.2727272727" height=1.2142857145 width=1.8214285717499998 style="setlinewidth(1)" ]
120
+ n_11 [label="f_0.1818181818" height=0.857142857 width=1.2857142855 style="setlinewidth(1)" ]
121
+ n_8 -> n_11 [label="0.1818181818" style="setlinewidth(1)" ]
122
+ }
123
+ }
124
+ }
125
+ EOF
126
+ exit
127
+ end
128
+ def ver()
129
+ $revision ="0" if $revision =~ /VERSION/
130
+ STDERR.puts "version #{$version} revision #{$revision}"
131
+ exit
132
+ end
133
+
134
+ help() if ARGV.size <= 0 or ARGV[0]=="--help"
135
+ ver() if ARGV[0]=="--version"
136
+
137
+ # ===================================================================
138
+ # パラメータ処理
139
+ args=MCMD::Margs.new(ARGV,"type=,k=,ni=,nf=,nv=,nc=,ei=,ef=,ev=,o=,nl=,el=,nr=,er=,ed=,-d,nw=,-clusterLabel,-noiso,-debug","ei=,ef=")
140
+
141
+ # mcmdのメッセージは警告とエラーのみ
142
+ ENV["KG_VerboseLevel"]="2" unless args.bool("-mcmdenv")
143
+
144
+ type = args. str("type=","flat") # グラフタイプ
145
+ if type!="nest" and type!="flat"
146
+ raise "key= takes `nest' or `flat'"
147
+ end
148
+
149
+ ni = args. file("ni=","r") # nodeファイル名
150
+ nf = args.field("nf=", ni) # nodeID項目名
151
+ nv = args.field("nv=", ni) # node value項目名
152
+ nc = args.field("nc=", ni) # node color項目名
153
+ nr = args.float("nr=" , 3.0 ,1.0 ,10.0 ) # node 拡大率
154
+ nl = args.field("nl=", ni) # nodeラベル項目名
155
+ nw =args.int("nw=",1,1) # nodeの枠線の太さ
156
+ unless args.keyValue["ni="]
157
+ if args.keyValue["nf="] or args.keyValue["nv="] or args.keyValue["nc="] or args.keyValue["nr="] or args.keyValue["-nl"] or args.keyValue["-cl"] or args.keyValue["nw="]
158
+ raise "nf=,nv=,nc=,nr=,-nl,-cl cannot be specified without ni="
159
+ end
160
+ end
161
+
162
+ if args.keyValue["ni="]
163
+ #unless args.keyValue["nf="] or args.keyValue["nv="]
164
+ unless args.keyValue["nf="]
165
+ raise "nf= must be specified when ni= is given"
166
+ end
167
+ end
168
+
169
+ ei = args. file("ei=","r") # edgeファイル名
170
+ key= args.field("k=", ei,nil,1,1) # nested graph クラスタ項目
171
+ key=key["names"][0] if key
172
+ ef = args.field("ef=", ei) # edge始点node項目名,終了節点項目名
173
+ ev = args.field("ev=", ei) # edge value項目名
174
+ ec = args.field("ec=", ei) # edge color項目名
175
+ ed = args.field("ed=", ei) # edge direction項目名
176
+ er = args.float("er=" , 10.0 ,1.0 ,20.0 ) # edge 拡大率
177
+ el = args.field("el=", ei) # edgeラベル項目
178
+ ef1=ef["names"][0]
179
+ ef2=ef["names"][1]
180
+ if ef1==nil or ef2==nil then
181
+ raise "ef= takes two field names"
182
+ end
183
+
184
+ if ni
185
+ nf=nf["names"][0]
186
+ nv=nv["names"][0] if nv
187
+ nc=nc["names"][0] if nc
188
+ end
189
+
190
+ ev=ev["names"][0] if ev
191
+
192
+ directed = args. bool("-d") #有向グラフ
193
+ directedStr="edge []"
194
+ directedStr="edge [dir=none]" unless directed
195
+ oFile = args.file("o=","w") # 出力ファイル名
196
+
197
+ clusterLabel=args.bool("-clusterLabel")
198
+ noiso=args.bool("-noiso")
199
+
200
+ # edgeデータからnested graphのtree構造を作る
201
+ # clusterのみの構造を作る
202
+ def mkTree(iFile,oFile)
203
+ temp=MCMD::Mtemp.new
204
+ xxroot =temp.file
205
+ xxbase=[]
206
+ xxbase << temp.file
207
+
208
+ # #{iFile}
209
+ # key,nam%0,keyNum,num,nv,nc
210
+ # #2_1,#1_1,4,1,6,1
211
+ # #2_1,#1_2,4,2,0.9999999996,1
212
+ xxiFile1=temp.file
213
+ xxiFile2=temp.file
214
+ xxkey =temp.file
215
+ xxnum =temp.file
216
+ xxleaf =temp.file
217
+ xxcheck =temp.file
218
+
219
+ # keyNumとnum項目のuniqリストを作り、お互いの包含関係でrootノードとleafノードを識別する。
220
+ system "mcut f=keyNum,num i=#{iFile} | msortf f=keyNum o=#{xxiFile1}"
221
+ system "mcut f=keyNum i=#{xxiFile1} | muniq k=keyNum o=#{xxkey}"
222
+ system "mcut f=num i=#{xxiFile1} | muniq k=num o=#{xxnum}"
223
+
224
+ # leaf nodesの選択
225
+ system "mcommon k=num K=keyNum m=#{xxkey} -r i=#{xxnum} | mcut f=num o=#{xxleaf}"
226
+
227
+ # root nodesの選択
228
+ system "mcommon k=keyNum K=num m=#{xxnum} -r i=#{xxkey} | mcut f=keyNum:node0 o=#{xxbase[0]}"
229
+
230
+ # leaf nodeの構造を知る必要はないので入力ファイルのnodeからleafを除外
231
+ system "mcommon k=num m=#{xxleaf} -r i=#{xxiFile1} o=#{xxiFile2}"
232
+
233
+ # root nodesファイルから親子関係noodeを次々にjoinしていく
234
+ # xxbase0 : root nodes
235
+ # node0%0
236
+ # 3
237
+ # 4
238
+ # xxbase1
239
+ # node0%0,node1
240
+ # 3,
241
+ # 4,1
242
+ # 4,2
243
+ # xxbase2
244
+ # node0,node1%0,node2
245
+ # 3,,
246
+ # 4,1,
247
+ # 4,2,
248
+ # join項目(node2)の非null項目が0件で終了
249
+
250
+ # system "cat #{xxbase[0]}"
251
+ # node0%0
252
+ # #1_3
253
+ # #2_1
254
+ i=0
255
+ depth=nil
256
+ while true
257
+ # puts "xxbase[#{i}]"
258
+ # system "cat #{xxbase[i]}"
259
+ xxbase << temp.file
260
+ system "mnjoin k=node#{i} K=keyNum m=#{xxiFile2} f=num:node#{i+1} -n i=#{xxbase[i]} o=#{xxbase[i+1]}"
261
+ system "mdelnull f=node#{i+1} i=#{xxbase[i+1]} o=#{xxcheck}"
262
+ size=MCMD::mrecount("i=#{xxcheck}")
263
+ if size==0
264
+ system "msortf f=* i=#{xxbase[i]} o=#{oFile}"
265
+ depth=i+1
266
+ break
267
+ end
268
+ i+=1
269
+ end
270
+ # system "cat #{oFile}"
271
+ # node0%0,node1
272
+ # 3,
273
+ # 4,1
274
+ # 4,2
275
+ #puts "depth=#{depth}"
276
+ # depth=2
277
+ return depth
278
+ end
279
+
280
+ # edgeデータからflat graph構造を作る
281
+ # clusterのみの構造を作る
282
+ def mkFlat(iFile,oFile)
283
+ f=""
284
+ f << "mcut f=keyNum:node0 i=#{iFile} |"
285
+ f << "muniq k=node0 o=#{oFile}"
286
+ system(f)
287
+ return 1
288
+ end
289
+
290
+
291
+ def keyBreakDepth(newFlds,oldFlds)
292
+ return 0 unless oldFlds # 先頭行
293
+ (0...newFlds.size).each{|i|
294
+ return i if newFlds[i]!=oldFlds[i]
295
+ }
296
+ end
297
+
298
+ ##########################
299
+ # creating tree structure
300
+ #
301
+ # digraph G {edge [dir=none]
302
+ # subgraph n_3 {
303
+ # ##3
304
+ # }
305
+ # subgraph n_4 {
306
+ # ##4
307
+ # subgraph n_1 {
308
+ # ##1
309
+ # }
310
+ # subgraph n_2 {
311
+ # ##2
312
+ # }
313
+ # }
314
+ # }
315
+ def dotTree(iFile,depth,header,footer,oFile)
316
+ File.open(oFile,"w"){|fpw|
317
+ fpw.puts header
318
+ fpw.puts "##0" # 孤立node(keyがnullのnode)
319
+ iCSV=MCMD::Mcsvin.new("i=#{iFile} -array")
320
+ oldFlds=nil
321
+ stack=[] # "subgraph {"に対応する終了括弧"}"のスタック
322
+ lastDepth=0 # 前行で出力されたsubgraphの深さ
323
+ iCSV.each{|newFlds|
324
+ next if newFlds[0]=="0" # 孤立nodeはスキップ
325
+ kbd=keyBreakDepth(newFlds,oldFlds) # 前行に比べてどの位置でkeybreakがあったか
326
+ (0...lastDepth-kbd).each{|i| # 前行より深さが戻った分終了括弧"}"を出力
327
+ fpw.puts stack.pop
328
+ }
329
+ # keybreakした位置から最深の位置までsubgraphを出力
330
+ (kbd...depth).each{|i|
331
+ break unless newFlds[i] # nullはその深さにsubgraphなしということ
332
+ indent=' '*(i+1) # インデント
333
+ fpw.puts "#{indent}subgraph cluster_#{newFlds[i]} {"
334
+ fpw.puts "###{newFlds[i]}"
335
+ stack.push("#{indent}}") # 対応する終了括弧をスタックしておく
336
+ lastDepth=i+1 # 出力した最深位置の更新
337
+ }
338
+ oldFlds=newFlds
339
+ }
340
+ (0...lastDepth).each{|i| # 深さが戻った分終了括弧"}"を出力
341
+ fpw.puts stack.pop
342
+ }
343
+ fpw.puts footer
344
+ }
345
+ end
346
+
347
+ # 全てをsubgraphを付けずにnodeとして出力する
348
+ def dotPlain(iFile,depth,header,footer,oFile)
349
+ File.open(oFile,"w"){|fpw|
350
+ fpw.puts header
351
+ fpw.puts "##0" # 孤立node(keyがnullのnode)
352
+ iCSV=MCMD::Mcsvin.new("i=#{iFile} -array")
353
+ iCSV.each{|flds|
354
+ flds.each{|fld|
355
+ break if fld==nil or fld==""
356
+ fpw.puts "###{fld}"
357
+ }
358
+ }
359
+ fpw.puts footer
360
+ }
361
+ end
362
+
363
+ def replace(treeFile,nodePath,edgePath,clusterLabel,noiso,oFile)
364
+ File.open(oFile,"w"){|dot|
365
+ File.open(treeFile,"r"){|tree|
366
+ while line=tree.gets
367
+ if line[0]=="#"
368
+ num=line.strip.sub("##","")
369
+ # 孤立nodeのclusterラベル(null)は出力しない
370
+ if clusterLabel and File.exist?("#{nodePath}/L_#{num}") and num!="0"
371
+ File.open("#{nodePath}/L_#{num}","r"){|label|
372
+ dot.puts label.read
373
+ }
374
+ end
375
+ if File.exist?("#{nodePath}/c_#{num}") # このifにマッチしないケースはないけど念のため
376
+ unless noiso and num=="0" # -noisoが指定されたら孤立ノードは出力しない
377
+ File.open("#{nodePath}/c_#{num}","r"){|node|
378
+ dot.puts node.read
379
+ }
380
+ end
381
+ end
382
+ if File.exist?("#{edgePath}/c_#{num}") # 孤立nodeはedgeなしなのでマッチする
383
+ File.open("#{edgePath}/c_#{num}","r"){|edge|
384
+ dot.puts edge.read
385
+ }
386
+ end
387
+ else
388
+ dot.puts line
389
+ end
390
+ end
391
+ }
392
+ }
393
+ end
394
+
395
+ ####################
396
+ # mapファイルの作成
397
+ # 1) key,node名の値に一対一対応するnodeIDを作成(niがなければeiから作成)
398
+ def mkMap(key,nf,ni,ef1,ef2,ei,oFile)
399
+ temp=MCMD::Mtemp.new
400
+ xxa=temp.file
401
+ xxb=temp.file
402
+ xxc=temp.file
403
+ xxL1=temp.file
404
+ xxL2=temp.file
405
+ xxleaf=temp.file
406
+
407
+ f=""
408
+ if ni then
409
+ f=""
410
+ f << "mcommon k=#{nf} K=#{key} m=#{ni} -r i=#{ni} |"
411
+ f << "mcut f=#{nf}:nam |"
412
+ f << "msetstr v=1 a=leaf o=#{xxleaf}"
413
+ system(f)
414
+ system "mcut f=#{nf}:nam i=#{ni} o=#{xxa}"
415
+ system "mcut f=#{key}:nam i=#{ni} o=#{xxb}"
416
+ f=""
417
+ f << "mcat i=#{xxa},#{xxb} |"
418
+ f << "muniq k=nam |"
419
+ f << "mjoin k=nam m=#{xxleaf} f=leaf -n |"
420
+ # nullは最初に来るはずなので、mcalでなくmnumberでもnullを0に採番できるはずだが念のために
421
+ f << "mcal c='if(isnull($s{nam}),0,line()+1)' a=num |"
422
+ f << "mnullto f=nam v=##NULL## o=#{oFile}"
423
+ system(f)
424
+ else
425
+ f=""
426
+ f << "mcommon k=#{ef1} K=#{key} m=#{ei} -r i=#{ei} |"
427
+ f << "mcut f=#{ef1}:nam o=#{xxL1}"
428
+ system(f)
429
+ f=""
430
+ f << "mcommon k=#{ef2} K=#{key} m=#{ei} -r i=#{ei} |"
431
+ f << "mcut f=#{ef2}:nam o=#{xxL2}"
432
+ system(f)
433
+ f=""
434
+ f << "mcat i=#{xxL1},#{xxL2} |"
435
+ f << "muniq k=nam |"
436
+ f << "msetstr v=1 a=leaf o=#{xxleaf}"
437
+ system(f)
438
+ system "mcut f=#{ef1}:nam i=#{ei} o=#{xxa}"
439
+ system "mcut f=#{ef2}:nam i=#{ei} o=#{xxb}"
440
+ system "mcut f=#{key}:nam i=#{ei} o=#{xxc}"
441
+ f=""
442
+ f << "mcat i=#{xxa},#{xxb},#{xxc} |"
443
+ f << "muniq k=nam |"
444
+ f << "mjoin k=nam m=#{xxleaf} f=leaf -n |"
445
+ f << "mcal c='if(isnull($s{nam}),0,line()+1)' a=num |"
446
+ f << "mnullto f=nam v=##NULL## o=#{oFile}"
447
+ system(f)
448
+ end
449
+ end
450
+
451
+ ####################
452
+ # nodeファイルの作成
453
+ # 1) key,node名すべての値に一対一対応するnodeIDを作成=>xxmap
454
+ # niがなければeiから作成
455
+ # 1) key,node名に対応するnodeIDをjoinする
456
+ # 2) nv項目を基準化
457
+ # 3) nvがなければ全データ1をセット
458
+ # 4) 基準化された値をnr倍する
459
+ # 3) ncがなければ全データnullをセット
460
+ #
461
+ # オリジナルのkey,node名に一意のnodeID(num)をつけて、nodeマスターを作成する
462
+ def mkNode(key,nf,nl,nv,nr,nc,ni,ef1,ef2,ei,mapFile,oFile)
463
+ temp=MCMD::Mtemp.new
464
+ xxa=temp.file
465
+ xxb=temp.file
466
+
467
+ # nodeファイルから作成
468
+ if ni
469
+ # mcal cat用のlabel項目の作成
470
+ label=[]
471
+ if nl
472
+ nl["names"].each{|name|
473
+ label << "$s{#{name}}"
474
+ }
475
+ else
476
+ label << "$s{#{nf}}"
477
+ end
478
+
479
+ nvcStr=""
480
+ nvcStr << ",#{nv}:nv" if nv
481
+ nvcStr << ",#{nc}:nc" if nc
482
+
483
+ # map
484
+ # nam,leaf,num
485
+ # ##NULL##,,0
486
+ # #1_1,,2
487
+ # #1_2,,3
488
+ # #1_3,,4
489
+ # #2_1,,5
490
+ # a,1,6
491
+ # b,1,7
492
+ # c,1,8
493
+ f=""
494
+ f << "mcal c='cat(\"_\",#{label.join(',')})' a=##label i=#{ni} |"
495
+ f << "mcut f=#{key}:key,#{nf}:nam,##label:nl#{nvcStr} |"
496
+ f << "mnullto f=key v=##NULL## |"
497
+ f << "msetstr v= a=nv |" unless nv
498
+ f << "msetstr v= a=nc |" unless nc
499
+ f << "mjoin k=key K=nam m=#{mapFile} f=num:keyNum |"
500
+ f << "mjoin k=nam K=nam m=#{mapFile} f=num,leaf |"
501
+ f << "mnormalize f=nv:nv2 c=range |"
502
+ f << "mcal c='${nv2}*(#{nr}-1)+1' a=nvv |"
503
+ f << "mcut f=key,nam,keyNum,num,nl,nv,nvv,nc,leaf o=#{xxa}"
504
+ system(f)
505
+ # key(cluster)のnvとncを結合しておく
506
+ f=""
507
+ f << "mjoin k=keyNum K=num m=#{xxa} f=nl:nlk,nv:nvKey,nc:ncKey -n i=#{xxa} |"
508
+ # rootのclusterはnlkがnullになるので、keyをlabelとしておく
509
+ f << "mcal c='if(isnull($s{nlk}),$s{key},$s{nlk})' a=nlKey |"
510
+ f << "mcut f=nlk -r o=#{oFile}"
511
+ system(f)
512
+
513
+ # edgeファイルから作成
514
+ else
515
+ system "mcut f=#{key}:key,#{ef1}:nam,#{ef1}:nl i=#{ei} o=#{xxa}"
516
+ system "mcut f=#{key}:key,#{ef2}:nam,#{ef2}:nl i=#{ei} o=#{xxb}"
517
+ f=""
518
+ f << "mcat i=#{xxa},#{xxb} |"
519
+ f << "mnullto f=key v=##NULL## |"
520
+ f << "muniq k=key,nam |"
521
+ f << "mjoin k=key K=nam m=#{mapFile} f=num:keyNum |"
522
+ f << "mjoin k=nam K=nam m=#{mapFile} f=num,leaf |"
523
+ f << "msetstr v=,,,,, a=nv,nvv,nc,nlKey,nvKey,ncKey |"
524
+ f << "mcut f=key,nam,keyNum,num,nl,nv,nvv,nc,leaf,nvKey,ncKey o=#{oFile}"
525
+ system(f)
526
+ end
527
+ # system "head #{oFile}"
528
+ # key,nam,keyNum%0,num,nv,nvv,nc,leaf,nvKey,ncKey
529
+ # ##NULL##,j,0,15,0.09090909091,1,FF0000,1,,
530
+ # ##NULL##,i,0,14,0.09090909091,1,FF0000,1,,
531
+ # #1_1,a,2,6,0.3636363636,1.857142857,FF0000,1,0.7272727273,
532
+ # #1_1,b,2,7,0.3636363636,1.857142857,00FF00,1,0.7272727273,
533
+ # #1_1,d,2,9,0.3636363636,1.857142857,00FF00,1,0.7272727273,
534
+ # #1_1,e,2,10,0.4545454545,2.142857143,0000FF,1,0.7272727273,
535
+ # #1_2,c,3,8,0.2727272727,1.571428571,FF0000,1,0.2727272727,
536
+ # #1_2,f,3,11,0.1818181818,1.285714286,FF0000,1,0.2727272727,
537
+ # #1_3,g,4,12,0.1818181818,1.285714286,00FF00,1,,
538
+ end
539
+
540
+ #####################
541
+ # edgeフィアルの作成
542
+ # 1) key,node名に対応するnodeIDをjoinする
543
+ # 2) ev項目を基準化
544
+ # 3) evがなければ全データ1をセット
545
+ # 4) 基準化された値をer倍する
546
+ def mkEdge(key,ef1,ef2,el,ec,ed,ev,er,ei,mapFile,oFile)
547
+ # mcal cat用のlabel項目の作成
548
+ label=[]
549
+ if el
550
+ el["names"].each{|name|
551
+ label << "$s{#{name}}"
552
+ }
553
+ end
554
+
555
+ evcdStr=""
556
+ evcdStr << ",#{ev}:ev" if ev
557
+ evcdStr << ",#{ec}:ec" if ec
558
+ evcdStr << ",#{ed}:ed" if ed
559
+ f=""
560
+ if el
561
+ f << "mcal c='cat(\"_\",#{label.join(',')})' a=##label i=#{ei} |"
562
+ else
563
+ f << "msetstr v= a=##label i=#{ei} |"
564
+ end
565
+ f << "mcut f=#{key}:key,#{ef1}:nam1,#{ef2}:nam2,##label:el#{evcdStr} |"
566
+ f << "msetstr v=1 a=ev |" unless ev
567
+ f << "mnullto f=key v=##NULL## |"
568
+ f << "mjoin k=key K=nam m=#{mapFile} f=num:keyNum |"
569
+ f << "mjoin k=nam1 K=nam m=#{mapFile} f=num:num1,leaf:leaf1 |"
570
+ f << "mjoin k=nam2 K=nam m=#{mapFile} f=num:num2,leaf:leaf2 |"
571
+ f << "mnormalize f=ev:ev2 c=range |"
572
+ f << "mcal c='${ev2}*(#{er}-1)+1' a=evv |"
573
+ f << "mcut f=key,nam1,nam2,keyNum,num1,num2,el,ev,evv,leaf1,leaf2 o=#{oFile}"
574
+ system(f)
575
+ end
576
+
577
+ #########################################
578
+ # dot用のnodeデータをcluster別に作成する
579
+ def dotNode(iFile,nw,type,clusterLabel,oPath)
580
+ # system "cat #{iFile}"
581
+ # key,nam,keyNum%0,num,nl,nv,nvv,nc,leaf,nvKey,ncKey
582
+ # ##NULL##,j,0,15,j_A,0.09090909091,1,FF0000,1,,
583
+ # ##NULL##,i,0,14,i_A,0.09090909091,1,FF0000,1,,
584
+ # #1_1,a,2,6,a_A,0.3636363636,1.857142857,FF0000,1,0.7272727273,
585
+ # #1_1,b,2,7,b_B,0.3636363636,1.857142857,00FF00,1,0.7272727273,
586
+ # #1_1,d,2,9,d_B,0.3636363636,1.857142857,00FF00,1,0.7272727273,
587
+ # #1_1,e,2,10,e_C,0.4545454545,2.142857143,0000FF,1,0.7272727273,
588
+ # #1_2,c,3,8,c_A,0.2727272727,1.571428571,FF0000,1,0.2727272727,
589
+ # #1_2,f,3,11,f_A,0.1818181818,1.285714286,FF0000,1,0.2727272727,
590
+ # #1_3,g,4,12,g_B,0.1818181818,1.285714286,00FF00,1,,
591
+ # #1_3,h,4,13,h_C,0.1818181818,1.285714286,0000FF,1,,
592
+ # #2_1,#1_2,5,3,#1_2_,0.2727272727,1.571428571,,,,
593
+ # #2_1,#1_1,5,2,#1_1_,0.7272727273,3,,,,
594
+ iCSV=MCMD::Mcsvin.new("k=keyNum i=#{iFile}")
595
+ block=""
596
+ iCSV.each{|flds,top,bot|
597
+ nam=flds["nam"]
598
+ nl =flds["nl"]
599
+ nv =flds["nv"]
600
+ nvv=flds["nvv"]
601
+ nc =flds["nc"]
602
+ leaf=flds["leaf"]
603
+
604
+ prefix="n"
605
+ prefix="cluster" unless leaf
606
+ nStr ="#{prefix}_#{flds["num"]}"
607
+ attrStr=""
608
+
609
+ unless prefix=="cluster"
610
+ # node label
611
+ # labelが#で始まる場合は、clusterLabelが指定されていない限りlabelを表示しない
612
+ if nl[0]!="#" or clusterLabel
613
+ attrStr << "label=\"#{nl}\" "
614
+ else
615
+ attrStr << "label=\"\" "
616
+ end
617
+
618
+ # node shape
619
+ if nvv
620
+ nRatioNorm=nvv.to_f
621
+ attrStr << "height=#{0.5*nRatioNorm} width=#{0.75*nRatioNorm} "
622
+ end
623
+
624
+ # node color
625
+ if nc
626
+ attrStr << "color=\"##{nc}\" "
627
+ end
628
+
629
+ # node linewidth
630
+ if nw
631
+ attrStr << "style=\"setlinewidth(#{nw})\" "
632
+ end
633
+ end
634
+ block << "#{nStr} [#{attrStr}]\n"
635
+
636
+ if bot
637
+ keyNum=flds["keyNum"]
638
+ key=flds["key"]
639
+ nlKey=flds["nlKey"]
640
+ nvKey=flds["nvKey"]
641
+ ncKey=flds["ncKey"]
642
+ File.open("#{oPath}/c_#{keyNum}","w"){|fpw|
643
+ fpw.write(block)
644
+ }
645
+ # クラスタのラベルや色も出力しておく
646
+ attrStr=""
647
+ attrStr << "label=\"#{nlKey}\"\n"
648
+
649
+ # node color
650
+ if ncKey
651
+ attrStr << "color=\"##{ncKey}\"\n"
652
+ end
653
+ # node linewidth
654
+ if nw and ncKey
655
+ if ncKey
656
+ attrStr << "style=\"setlinewidth(#{nw})\"\n"
657
+ end
658
+ end
659
+ File.open("#{oPath}/L_#{keyNum}","w"){|fpw|
660
+ fpw.write(attrStr)
661
+ }
662
+ block=""
663
+ end
664
+ }
665
+ end
666
+
667
+ #########################################
668
+ # dot用のedgeデータをcluster別に作成する
669
+ def dotEdge(iFile,oPath)
670
+ # key,nam1,nam2%0,keyNum,num1,num2,ev,evv
671
+ # #2_1,#1_1,#1_2,4,1,2,0.2727272727,20
672
+ # #1_1,a,b,1,5,6,0.1818181818,0
673
+ iCSV=MCMD::Mcsvin.new("k=keyNum i=#{iFile}")
674
+ block=""
675
+ iCSV.each{|flds,top,bot|
676
+ num1=flds["num1"]
677
+ num2=flds["num2"]
678
+ el=flds["el"]
679
+ ev=flds["ev"]
680
+ ec=flds["ec"]
681
+ ed=flds["ed"]
682
+ evv=flds["evv"]
683
+ leaf1=flds["leaf1"]
684
+ leaf2=flds["leaf2"]
685
+
686
+ prefix1="n"
687
+ prefix2="n"
688
+ prefix1="cluster" unless leaf1
689
+ prefix2="cluster" unless leaf2
690
+ e1Str ="#{prefix1}_#{num1}"
691
+ e2Str ="#{prefix2}_#{num2}"
692
+
693
+ attrStr=""
694
+ attrStr << "label=\"#{el}\" " if el
695
+ attrStr << "style=\"setlinewidth(#{evv})\" " if evv
696
+ attrStr << "color=\"##{ec}\" " if ec
697
+ if ed
698
+ if ed=="F"
699
+ attrStr << "dir=forward "
700
+ elsif ed=="B"
701
+ attrStr << "dir=back "
702
+ elsif ed=="W"
703
+ attrStr << "dir=both "
704
+ elsif ed=="N"
705
+ attrStr << "dir=none "
706
+ end
707
+ end
708
+
709
+ block << "#{e1Str} -> #{e2Str} [#{attrStr}]\n"
710
+
711
+ if bot
712
+ keyNum=flds["keyNum"]
713
+ File.open("#{oPath}/c_#{keyNum}","w"){|fpw|
714
+ fpw.write(block)
715
+ }
716
+ block=""
717
+ end
718
+ }
719
+ end
720
+
721
+ #############
722
+ # entry point
723
+
724
+ temp=MCMD::Mtemp.new
725
+ xxni =temp.file
726
+ xxei =temp.file
727
+ xxmap =temp.file
728
+ xxnode=temp.file
729
+ xxedge=temp.file
730
+ xxnode2=temp.file
731
+ xxedge2=temp.file
732
+ xxtree=temp.file
733
+ xxdotNode=temp.file
734
+ xxdotEdge=temp.file
735
+ MCMD::mkDir(xxdotNode)
736
+ MCMD::mkDir(xxdotEdge)
737
+
738
+ # 処理前のデータ修正
739
+ unless key
740
+ if ni
741
+ system "msetstr v= a=#key i=#{ni} o=#{xxni}"
742
+ ni=xxni
743
+ end
744
+ system "msetstr v= a=#key i=#{ei} o=#{xxei}"
745
+ ei=xxei
746
+ key="#key"
747
+ end
748
+
749
+ # ノードファイルの作成
750
+ mkMap(key,nf,ni,ef1,ef2,ei,xxmap)
751
+ mkNode(key,nf,nl,nv,nr,nc,ni,ef1,ef2,ei,xxmap,xxnode)
752
+ mkEdge(key,ef1,ef2,el,ec,ed,ev,er,ei,xxmap,xxedge)
753
+ #system "head #{xxmap}"
754
+ # nam%0,num
755
+ # ,0
756
+ # #1_1,1
757
+ # #1_2,2
758
+ #
759
+ #system "cat #{xxnode}"
760
+ #exit
761
+ # key,nam,keyNum%0,num,nv,nvv,nc,leaf,nvKey,ncKey
762
+ # #1_1,a,1,5,0.4666666667,3.222222223,,1,0.6666666667,
763
+ # #1_1,b,1,6,0.4,2.666666666,,1,0.6666666667,
764
+ #system "cat #{xxedge}"
765
+ # key,nam1,nam2%0,keyNum,num1,num2,ev,evv,leaf1,leaf2
766
+ # #3_1,#1_2,#2_1,4,2,3,0.3,13.66666667,,
767
+ # #1_1,a,b,1,5,6,0.1,1,1,1
768
+ #exit
769
+ # dot用のnodeとedgeデータをcluster別ファイルとして生成
770
+ dotNode(xxnode,nw,type,clusterLabel,xxdotNode)
771
+ dotEdge(xxedge ,xxdotEdge)
772
+ #system "rm -rf ./xxdotNode"
773
+ #system "cp -R #{xxdotNode} ./xxdotNode"
774
+ #system "rm -rf ./xxdotEdge"
775
+ #system "cp -R #{xxdotEdge} ./xxdotEdge"
776
+
777
+ depth=nil
778
+ if type=="flat"
779
+ depth=mkFlat(xxnode,xxtree)
780
+ elsif type=="nest"
781
+ # tree構造の処理
782
+ # クラスタのみtree構造に格納する
783
+ depth=mkTree(xxnode,xxtree)
784
+ # puts "xxtree"
785
+ # system "cat #{xxtree}"
786
+ # node0%0,node1%1
787
+ # 3,
788
+ # 4,1
789
+ # 4,2
790
+ end
791
+
792
+ # tree構造をdotとして書き出す。その時、node,edgeを置換するためのキーワードを埋め込む
793
+ xxdotTree=temp.file
794
+ header=""
795
+ header << "digraph G {\n"
796
+ header << " #{directedStr}\n"
797
+ footer=""
798
+ footer << "}\n"
799
+ #system "cat #{xxtree}"
800
+ dotTree(xxtree,depth,header,footer,xxdotTree)
801
+ #puts "--------"
802
+ #system "cat #{xxdotTree}"
803
+ #exit
804
+ # xxdotTreeのnode,edge keywordをxxdotNode,xxdotEdgeで置換してdotの完成
805
+ replace(xxdotTree,xxdotNode,xxdotEdge,clusterLabel,noiso,oFile)
806
+
807
+ # 終了メッセージ
808
+ MCMD::endLog(args.cmdline)
809
+