nysol-view 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+