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/m2gv.rb ADDED
@@ -0,0 +1,812 @@
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/12/13
9
+ $cmd=$0.sub(/.*\//,"")
10
+
11
+ $version=1.0
12
+ $revision="###VERSION###"
13
+
14
+ def help
15
+
16
+ STDERR.puts <<EOF
17
+ ------------------------
18
+ #{$cmd} version #{$version}
19
+ ------------------------
20
+ 概要) CSVによるグラフ構造データをDOTフォーマットで出力する
21
+
22
+ 書式1) #{$cmd} [type=flat|nest] [k=] [ni=] [nf=] [nv=] [nc=] [col=] [nl=] [nw=]
23
+ [-clusterLabel] [-noiso] ei= ef= [ev=] [er=] [el=] [-d] [o=]
24
+
25
+ type= : グラフのタイプ(省略時はflat)
26
+ flat: key項目をクラスタとした木構造グラフ
27
+ nest: 木構造であることを前提にした入れ子構造グラフ(データが木構造でない場合の描画は不定)
28
+ k= : 入れ子グラフのクラスタ項目名(ni=を指定した場合は同じ項目名でなければならない)
29
+ 複数項目指定不可
30
+ k=を省略すればtype=に関わらずクラスタを伴わない普通のグラフ描画となる。
31
+
32
+ ni= : 節点集合ファイル名
33
+ nf= : 節点ID項目名
34
+ nv= : 節点の大きさ項目名(この値に応じて節点の楕円の大きさが変化する,1項目のみ指定可)。
35
+ nc= : 節点カラー項目名(この値に応じて枠線カラーが変化する,1項目のみ指定可)。
36
+ カラーは、RGBを16進数2桁づつ6桁で表現する。ex) FF00FF:紫
37
+ さらに最後に2桁追加すればそれは透過率となる。
38
+ nl= : ノードラベルの項目(複数指定したら"_"で区切って結合される)
39
+ 省略すればnf=でしてした項目をラベルとする。
40
+ nw= : 節点の枠線の幅を指定する(デフォルトは1)
41
+ -clusterLabel : k=を指定して入れ子グラフを作成する場合、クラスタのラベルも表示する
42
+ -noiso : 孤立節点(隣接節点のない節点で、本コマンドではni=に出てきてei=に出てこない節点のこと)は出力しない
43
+
44
+ ei= : 枝集合ファイル名
45
+ ef= : 開始節点ID項目名,終了節点ID項目名
46
+ ev= : 枝の幅項目名
47
+ ec= : 枝の色を表す項目(色の値はnc=と同じ)
48
+ ed= : 枝の矢印を表す項目(-dの指定、未指定に関わらず優先される)
49
+ 値としては、F,B,W,N,nullの5つの値のいずれかでなければならない。
50
+ ef=e1,e2とした場合、それぞれで描画される矢印は以下の通り。
51
+ F: e1->e2, B: e1<-e2, W: e1<->e2, N:e1-e2(矢印なし),null:デフォルト
52
+ デフォルトは、-dが指定されていればF、-dの指定がなければNとなる。
53
+ el= : エッジラベルの項目(複数指定したら"_"で区切って結合される)
54
+ 省略すれば、エッジラベルは表示されない。
55
+
56
+ -d : 有向グラフと見なす。"edge [dir=none]"を記述する。
57
+ o= : 出力ファイル名
58
+
59
+ -h,--help : ヘルプの表示
60
+
61
+ 基本例)
62
+ $ cat edge.csv
63
+ e1,e2,v
64
+ a,b,11
65
+ a,c,20
66
+ b,d,11
67
+ d,e,8
68
+ c,e,9
69
+
70
+ $ #{$cmd} ei=edge.csv ef=e1,e2 ev=v el=v er=20 o=result.dot
71
+ $ cat result.dot
72
+ digraph G {
73
+ edge [dir=none]
74
+ n_2 [label="a" style="setlinewidth(1)" ]
75
+ n_3 [label="b" style="setlinewidth(1)" ]
76
+ n_4 [label="c" style="setlinewidth(1)" ]
77
+ n_5 [label="d" style="setlinewidth(1)" ]
78
+ n_6 [label="e" style="setlinewidth(1)" ]
79
+ n_2 -> n_3 [label="11" style="setlinewidth(5.75)" ]
80
+ n_2 -> n_4 [label="20" style="setlinewidth(20)" ]
81
+ n_3 -> n_5 [label="11" style="setlinewidth(5.75)" ]
82
+ n_4 -> n_6 [label="9" style="setlinewidth(2.583333333)" ]
83
+ n_5 -> n_6 [label="8" style="setlinewidth(1)" ]
84
+ }
85
+
86
+ ネストグラフの例)
87
+ $ cat edge4.csv
88
+ cluster,node1,node2,support
89
+ #1_1,a,b,0.1
90
+ #1_2,d,e,0.1
91
+ #2_1,#1_1,c,0.2
92
+ #3_1,#1_2,#2_1,0.3
93
+ #3_1,#2_1,f,0.4
94
+
95
+ $ #{$cmd} mgv.rb type=flat k=cluster ei=edge4.csv ef=node1,node2 o=result.dot
96
+ # fdpはgraphVizのコマンド, nested graphはdotコマンドでは描画できない
97
+ $ fdp -Tpdf result.dot >result.pdf
98
+ $ open result.pdf
99
+ $ cat result.dot
100
+ digraph G {
101
+ edge [dir=none]
102
+ subgraph cluster_1 {
103
+ n_5 [label="a" style="setlinewidth(1)" ]
104
+ n_6 [label="b" style="setlinewidth(1)" ]
105
+ n_5 -> n_6 []
106
+ }
107
+ subgraph cluster_2 {
108
+ n_8 [label="d" style="setlinewidth(1)" ]
109
+ n_9 [label="e" style="setlinewidth(1)" ]
110
+ n_8 -> n_9 []
111
+ }
112
+ subgraph cluster_3 {
113
+ cluster_1 []
114
+ n_7 [label="c" style="setlinewidth(1)" ]
115
+ cluster_1 -> n_7 []
116
+ }
117
+ subgraph cluster_4 {
118
+ cluster_2 []
119
+ cluster_3 []
120
+ n_10 [label="f" style="setlinewidth(1)" ]
121
+ cluster_2 -> cluster_3 []
122
+ cluster_3 -> n_10 []
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=,ec=,o=,nl=,el=,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 "type= 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
+ nl = args.field("nl=", ni) # nodeラベル項目名
154
+ nw =args.int("nw=",1,1) # nodeの枠線の太さ
155
+ unless args.keyValue["ni="]
156
+ if args.keyValue["nf="] or args.keyValue["nv="] or args.keyValue["nc="] or args.keyValue["-nl"] or args.keyValue["-cl"] or args.keyValue["nw="]
157
+ raise "nf=,nv=,nc=,-nl,-cl cannot be specified without ni="
158
+ end
159
+ end
160
+
161
+ if args.keyValue["ni="]
162
+ #unless args.keyValue["nf="] or args.keyValue["nv="]
163
+ unless args.keyValue["nf="]
164
+ raise "nf= must be specified when ni= is given"
165
+ end
166
+ end
167
+
168
+ ei = args. file("ei=","r") # edgeファイル名
169
+ key= args.field("k=", ei,nil,1,1) # nested graph クラスタ項目
170
+ key=key["names"][0] if key
171
+ ef = args.field("ef=", ei) # edge始点node項目名,終了節点項目名
172
+ ev = args.field("ev=", ei) # edge value項目名
173
+ ec = args.field("ec=", ei) # edge color項目名
174
+ ed = args.field("ed=", ei) # edge direction項目名
175
+ el = args.field("el=", ei) # edgeラベル項目
176
+ ef1=ef["names"][0]
177
+ ef2=ef["names"][1]
178
+ if ef1==nil or ef2==nil then
179
+ raise "ef= takes two field names"
180
+ end
181
+
182
+ if ni
183
+ nf=nf["names"][0]
184
+ nv=nv["names"][0] if nv
185
+ nc=nc["names"][0] if nc
186
+ end
187
+
188
+ ev=ev["names"][0] if ev
189
+ ed=ed["names"][0] if ed
190
+ ec=ec["names"][0] if ec
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,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
+ File.open("#{nodePath}/c_#{num}","r"){|node|
377
+ dot.puts node.read
378
+ }
379
+ end
380
+ if File.exist?("#{edgePath}/c_#{num}") # 孤立nodeはedgeなしなのでマッチする
381
+ File.open("#{edgePath}/c_#{num}","r"){|edge|
382
+ dot.puts edge.read
383
+ }
384
+ end
385
+ else
386
+ dot.puts line
387
+ end
388
+ end
389
+ }
390
+ }
391
+ end
392
+
393
+ ####################
394
+ # mapファイルの作成
395
+ # 1) key,node名の値に一対一対応するnodeIDを作成(niがなければeiから作成)
396
+ def mkMap(key,nf,ni,ef1,ef2,ei,oFile)
397
+ temp=MCMD::Mtemp.new
398
+ xxa=temp.file
399
+ xxb=temp.file
400
+ xxc=temp.file
401
+ xxL1=temp.file
402
+ xxL2=temp.file
403
+ xxleaf=temp.file
404
+
405
+ # leaf nodeの構築
406
+ system "mcommon k=#{ef1} K=#{key} m=#{ei} -r i=#{ei} | mcut f=#{ef1}:nam o=#{xxa}"
407
+ system "mcommon k=#{ef2} K=#{key} m=#{ei} -r i=#{ei} | mcut f=#{ef2}:nam o=#{xxb}"
408
+ system "mcommon k=#{nf} K=#{key} m=#{ei} -r i=#{ni} | mcut f=#{nf}:nam o=#{xxc}" if ni
409
+ f=""
410
+ if ni
411
+ f << "mcat i=#{xxa},#{xxb},#{xxc} |"
412
+ else
413
+ f << "mcat i=#{xxa},#{xxb} |"
414
+ end
415
+ f << "muniq k=nam |"
416
+ f << "msetstr v=1 a=leaf o=#{xxleaf}"
417
+ system(f)
418
+
419
+ f=""
420
+ if ni then
421
+ system "mcut f=#{nf}:nam i=#{ni} o=#{xxa}"
422
+ system "mcut f=#{key}:nam i=#{ni} o=#{xxb}"
423
+ f=""
424
+ f << "mcat i=#{xxa},#{xxb} |"
425
+ f << "muniq k=nam |"
426
+ f << "mjoin k=nam m=#{xxleaf} f=leaf -n |"
427
+ # nullは最初に来るはずなので、mcalでなくmnumberでもnullを0に採番できるはずだが念のために
428
+ f << "mcal c='if(isnull($s{nam}),0,line()+1)' a=num |"
429
+ f << "mnullto f=nam v=##NULL## o=#{oFile}"
430
+ system(f)
431
+ else
432
+ system "mcut f=#{ef1}:nam i=#{ei} o=#{xxa}"
433
+ system "mcut f=#{ef2}:nam i=#{ei} o=#{xxb}"
434
+ system "mcut f=#{key}:nam i=#{ei} o=#{xxc}"
435
+ f=""
436
+ f << "mcat i=#{xxa},#{xxb},#{xxc} |"
437
+ f << "muniq k=nam |"
438
+ f << "mjoin k=nam m=#{xxleaf} f=leaf -n |"
439
+ f << "mcal c='if(isnull($s{nam}),0,line()+1)' a=num |"
440
+ f << "mnullto f=nam v=##NULL## o=#{oFile}"
441
+ system(f)
442
+ end
443
+ end
444
+
445
+ ####################
446
+ # nodeファイルの作成
447
+ # 1) key,node名すべての値に一対一対応するnodeIDを作成=>xxmap
448
+ # niがなければeiから作成
449
+ # 1) key,node名に対応するnodeIDをjoinする
450
+ # 2) nvがなければ全データ1をセット
451
+ # 3) ncがなければ全データnullをセット
452
+ #
453
+ # オリジナルのkey,node名に一意のnodeID(num)をつけて、nodeマスターを作成する
454
+ def mkNode(key,nf,nl,nv,nc,ni,ef1,ef2,ei,noiso,mapFile,oFile)
455
+ temp=MCMD::Mtemp.new
456
+ xxbyEdge=temp.file
457
+ xxbyNode=temp.file
458
+ xxa=temp.file
459
+ xxb=temp.file
460
+
461
+ # edgeファイルからnode情報を生成
462
+ # noiso(孤立node排除)の場合は、edgeにあってnodeにないidを省く必要があるので計算する。
463
+ if ni==nil or (ni!=nil and noiso)
464
+ system "mcut f=#{key}:key,#{ef1}:nam,#{ef1}:nl i=#{ei} o=#{xxa}"
465
+ system "mcut f=#{key}:key,#{ef2}:nam,#{ef2}:nl i=#{ei} o=#{xxb}"
466
+ f=""
467
+ f << "mcat i=#{xxa},#{xxb} |"
468
+ f << "mnullto f=key v=##NULL## |"
469
+ f << "muniq k=key,nam |"
470
+ f << "mjoin k=key K=nam m=#{mapFile} f=num:keyNum |"
471
+ f << "mjoin k=nam K=nam m=#{mapFile} f=num,leaf |"
472
+ f << "msetstr v=,,,, a=nv,nc,nlKey,nvKey,ncKey |"
473
+ f << "mcut f=key,nam,keyNum,num,nl,nv,nc,leaf,nvKey,ncKey o=#{xxbyEdge}"
474
+ system(f)
475
+ end
476
+
477
+ # nodeファイルから作成
478
+ if ni
479
+ # mcal cat用のlabel項目の作成
480
+ label=[]
481
+ if nl
482
+ nl["names"].each{|name|
483
+ label << "$s{#{name}}"
484
+ }
485
+ else
486
+ label << "$s{#{nf}}"
487
+ end
488
+
489
+ nvcStr=""
490
+ nvcStr << ",#{nv}:nv" if nv
491
+ nvcStr << ",#{nc}:nc" if nc
492
+
493
+ # map
494
+ # nam,leaf,num
495
+ # ##NULL##,,0
496
+ # #1_1,,2
497
+ # #1_2,,3
498
+ # #1_3,,4
499
+ # #2_1,,5
500
+ # a,1,6
501
+ # b,1,7
502
+ # c,1,8
503
+ f=""
504
+ f << "mcal c='cat(\"_\",#{label.join(',')})' a=##label i=#{ni} |"
505
+ f << "mcut f=#{key}:key,#{nf}:nam,##label:nl#{nvcStr} |"
506
+ f << "mnullto f=key v=##NULL## |"
507
+ f << "msetstr v= a=nv |" unless nv
508
+ f << "msetstr v= a=nc |" unless nc
509
+ f << "mjoin k=key K=nam m=#{mapFile} f=num:keyNum |"
510
+ f << "mjoin k=nam K=nam m=#{mapFile} f=num,leaf |"
511
+ f << "mcut f=key,nam,keyNum,num,nl,nv,nc,leaf o=#{xxa}"
512
+ system(f)
513
+ # key(cluster)のnvとncを結合しておく
514
+ f=""
515
+ f << "mjoin k=keyNum K=num m=#{xxa} f=nl:nlk,nv:nvKey,nc:ncKey -n i=#{xxa} |"
516
+ # rootのclusterはnlkがnullになるので、keyをlabelとしておく
517
+ f << "mcal c='if(isnull($s{nlk}),$s{key},$s{nlk})' a=nlKey |"
518
+ f << "mcut f=nlk -r o=#{xxbyNode}"
519
+ system(f)
520
+ end
521
+
522
+ if ni!=nil and noiso
523
+ system "mcommon k=key,nam m=#{xxbyEdge} i=#{xxbyNode} o=#{oFile}"
524
+ elsif ni!=nil
525
+ system "mv #{xxbyNode} #{oFile}"
526
+ else
527
+ system "mv #{xxbyEdge} #{oFile}"
528
+ end
529
+ # system "head #{oFile}"
530
+ # key,nam,keyNum%0,num,nv,nvv,nc,leaf,nvKey,ncKey
531
+ # ##NULL##,j,0,15,0.09090909091,1,FF0000,1,,
532
+ # ##NULL##,i,0,14,0.09090909091,1,FF0000,1,,
533
+ # #1_1,a,2,6,0.3636363636,1.857142857,FF0000,1,0.7272727273,
534
+ # #1_1,b,2,7,0.3636363636,1.857142857,00FF00,1,0.7272727273,
535
+ # #1_1,d,2,9,0.3636363636,1.857142857,00FF00,1,0.7272727273,
536
+ # #1_1,e,2,10,0.4545454545,2.142857143,0000FF,1,0.7272727273,
537
+ # #1_2,c,3,8,0.2727272727,1.571428571,FF0000,1,0.2727272727,
538
+ # #1_2,f,3,11,0.1818181818,1.285714286,FF0000,1,0.2727272727,
539
+ # #1_3,g,4,12,0.1818181818,1.285714286,00FF00,1,,
540
+ end
541
+
542
+ #####################
543
+ # edgeフィアルの作成
544
+ # 1) key,node名に対応するnodeIDをjoinする
545
+ # 2) ev項目を基準化
546
+ # 3) evがなければ全データ1をセット
547
+ def mkEdge(key,ef1,ef2,el,ec,ed,ev,ei,mapFile,oFile)
548
+ # mcal cat用のlabel項目の作成
549
+ label=[]
550
+ if el
551
+ el["names"].each{|name|
552
+ label << "$s{#{name}}"
553
+ }
554
+ end
555
+
556
+ evcdStr=""
557
+ evcdStr << ",#{ev}:ev" if ev
558
+ evcdStr << ",#{ec}:ec" if ec
559
+ evcdStr << ",#{ed}:ed" if ed
560
+ f=""
561
+ if el
562
+ f << "mcal c='cat(\"_\",#{label.join(',')})' a=##label i=#{ei} |"
563
+ else
564
+ f << "msetstr v= a=##label i=#{ei} |"
565
+ end
566
+ f << "mcut f=#{key}:key,#{ef1}:nam1,#{ef2}:nam2,##label:el#{evcdStr} |"
567
+ f << "msetstr v= a=ev |" unless ev
568
+ f << "msetstr v= a=ed |" unless ed
569
+ f << "msetstr v= a=ec |" unless ec
570
+ f << "mnullto f=key v=##NULL## |"
571
+ f << "mjoin k=key K=nam m=#{mapFile} f=num:keyNum |"
572
+ f << "mjoin k=nam1 K=nam m=#{mapFile} f=num:num1,leaf:leaf1 |"
573
+ f << "mjoin k=nam2 K=nam m=#{mapFile} f=num:num2,leaf:leaf2 |"
574
+ f << "mcut f=key,nam1,nam2,keyNum,num1,num2,el,ev,ed,ec,leaf1,leaf2 o=#{oFile}"
575
+ system(f)
576
+ system "cp #{oFile} xxo"
577
+ end
578
+
579
+ #########################################
580
+ # dot用のnodeデータをcluster別に作成する
581
+ def dotNode(iFile,nw,type,clusterLabel,oPath)
582
+ #system "cat #{iFile}"
583
+ # key,nam,keyNum%0,num,nl,nv,nvv,nc,leaf,nvKey,ncKey
584
+ # ##NULL##,j,0,15,j_A,0.09090909091,1,FF0000,1,,
585
+ # ##NULL##,i,0,14,i_A,0.09090909091,1,FF0000,1,,
586
+ # #1_1,a,2,6,a_A,0.3636363636,1.857142857,FF0000,1,0.7272727273,
587
+ # #1_1,b,2,7,b_B,0.3636363636,1.857142857,00FF00,1,0.7272727273,
588
+ # #1_1,d,2,9,d_B,0.3636363636,1.857142857,00FF00,1,0.7272727273,
589
+ # #1_1,e,2,10,e_C,0.4545454545,2.142857143,0000FF,1,0.7272727273,
590
+ # #1_2,c,3,8,c_A,0.2727272727,1.571428571,FF0000,1,0.2727272727,
591
+ # #1_2,f,3,11,f_A,0.1818181818,1.285714286,FF0000,1,0.2727272727,
592
+ # #1_3,g,4,12,g_B,0.1818181818,1.285714286,00FF00,1,,
593
+ # #1_3,h,4,13,h_C,0.1818181818,1.285714286,0000FF,1,,
594
+ # #2_1,#1_2,5,3,#1_2_,0.2727272727,1.571428571,,,,
595
+ # #2_1,#1_1,5,2,#1_1_,0.7272727273,3,,,,
596
+ iCSV=MCMD::Mcsvin.new("k=keyNum i=#{iFile}")
597
+ block=""
598
+ iCSV.each{|flds,top,bot|
599
+ nam=flds["nam"]
600
+ nl =flds["nl"]
601
+ nv =flds["nv"]
602
+ nc =flds["nc"]
603
+ leaf=flds["leaf"]
604
+
605
+ prefix="n"
606
+ prefix="cluster" unless leaf
607
+ nStr ="#{prefix}_#{flds["num"]}"
608
+ attrStr=""
609
+
610
+ unless prefix=="cluster"
611
+ # node label
612
+ # labelがleafでなければ、-clusterLabelが指定されていない限りlabelを表示しない
613
+ if leaf or clusterLabel
614
+ attrStr << "label=\"#{nl}\" "
615
+ else
616
+ attrStr << "label=\"\" "
617
+ end
618
+
619
+ # node shape
620
+ if nv
621
+ nRatioNorm=nv.to_f
622
+ attrStr << "height=#{0.5*nRatioNorm} width=#{0.75*nRatioNorm} "
623
+ end
624
+
625
+ # node color
626
+ if nc
627
+ attrStr << "color=\"##{nc}\" "
628
+ end
629
+
630
+ # node linewidth
631
+ if nw
632
+ attrStr << "style=\"setlinewidth(#{nw})\" "
633
+ end
634
+ end
635
+ block << "#{nStr} [#{attrStr}]\n"
636
+
637
+ if bot
638
+ keyNum=flds["keyNum"]
639
+ key=flds["key"]
640
+ nlKey=flds["nlKey"]
641
+ nvKey=flds["nvKey"]
642
+ ncKey=flds["ncKey"]
643
+ File.open("#{oPath}/c_#{keyNum}","w"){|fpw|
644
+ fpw.write(block)
645
+ }
646
+ # クラスタのラベルや色も出力しておく
647
+ attrStr=""
648
+ attrStr << "label=\"#{nlKey}\"\n"
649
+
650
+ # node color
651
+ if ncKey
652
+ attrStr << "color=\"##{ncKey}\"\n"
653
+ end
654
+ # node linewidth
655
+ if nw and ncKey
656
+ if ncKey
657
+ attrStr << "style=\"setlinewidth(#{nw})\"\n"
658
+ end
659
+ end
660
+ File.open("#{oPath}/L_#{keyNum}","w"){|fpw|
661
+ fpw.write(attrStr)
662
+ }
663
+ block=""
664
+ end
665
+ }
666
+ end
667
+
668
+ #########################################
669
+ # dot用のedgeデータをcluster別に作成する
670
+ def dotEdge(iFile,oPath)
671
+ # key,nam1,nam2%0,keyNum,num1,num2,ev,evv
672
+ # #2_1,#1_1,#1_2,4,1,2,0.2727272727,20
673
+ # #1_1,a,b,1,5,6,0.1818181818,0
674
+ iCSV=MCMD::Mcsvin.new("k=keyNum i=#{iFile}")
675
+ block=""
676
+ iCSV.each{|flds,top,bot|
677
+ num1=flds["num1"]
678
+ num2=flds["num2"]
679
+ el=flds["el"]
680
+ ev=flds["ev"]
681
+ ec=flds["ec"]
682
+ ed=flds["ed"]
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(#{ev})\" " if ev
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,nc,ni,ef1,ef2,ei,noiso,xxmap,xxnode)
752
+ mkEdge(key,ef1,ef2,el,ec,ed,ev,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,oFile)
806
+ #system "cp #{xxdotTree} ./xxdotTree"
807
+ #system "cp -R #{xxdotNode} ./xxdotNode"
808
+ #system "cp -R #{xxdotEdge} ./xxdotEdge"
809
+
810
+ # 終了メッセージ
811
+ MCMD::endLog(args.cmdline)
812
+