SASプログラムの可視化
- SASプログラムステップフローチャート
生成プログラムの紹介
-
○福田
裕章
1(
1MSD株式会社)
Visualization of SAS programs
Hiroaki Fukuda
MSD K.K.
要旨:
データステップ及び
SGPLOTプロシジャにおけるPOLYGON/TEXTス
テートメントを利用した
SASプログラムステップフローチャートを生成する
SASプログラムを紹介する。
キーワード:
SGPLOT, フローチャート, 可視化
2背景
データ加工
Proc Step
Proc Step
データ加工
...
一般的な
SASプログラム
SASプログラムをSASプログラムで読み込み処理することで、
プログラムのステップフローチャートを作成する
フローの可視化が望ましい
様々なデータ
ハンドリング
技術
Object
Hash
Proc
DS2
Proc
Lua
主流は基本的な
Data StepやSQLプロシジャ
⇒ハンドリングのために数多のステップが発生
プログラムの把握に一苦労
フローチャート化
Data
Step
Proc
SQL
3SASプログラムの判定
• 例:データステップの開始
「
data」で始まっている
「
data」の直前が「*/」や「;」で終了している
「
data」に続けて空白+データセット名可能文字列が記載
などなど
4SASシステムによる
認識判定
SASプログラムによる
プログラムステップ判定
• data, proc, set, mergeなどのステートメントに対する処理
• コメントアウトやクオーテーションの処理
• 他にも様々な条件や記載方法(改行等)の考慮
≒
プログラムの概要
• SASプログラムの読み込み
• コメント・クオーテーションの処理
• 各種ステートメントの処理
Data Statement
Proc Statement
Set Statement
Merge Statement
SQLの処理
• フローの分岐処理
Grouping処理 = X座標の決定
出力対象とする
Textの選択
Y座標の決定
• アウトプット(Box/Arrow)用のDataset作成
• フローチャートの描画
5コード処理
分岐処理
アウトプット
処理
Perl Regular
Expressionを利用
Hash Objectを利用
SGPLOT
プロシジャを利用
(座標処理)
テキスト操作の柔軟性
– Perl Regular Expression
• Perl正規表現の一部をSASでは関数として使用することが可能
⇒パターンマッチにより、複雑な文字処理が可能
6コメントアウトの処理(一部)
* rec2: プログラムコード格納変数;<prxchange関数>
(perl-regular-expression, times, source)
* 引数の1番目がPerl正規表現で特定の文字列
パターンを置換
*** 先頭行や前行の最後が「;」「*/」で終了している場合;
rec2 = prxchange('s/(%¥*.*?;|¥/¥*.*?¥*¥/|(^|(?<=[^¥w_¥))) *?¥*.*?;)//', -1, rec2);
*** 改行により複数行にまたがっている場合(/* */);
if index(rec2, "/*") then do;
asts1fn = 1; cont1fn = 1; rec2 = prxchange('s/¥/¥*.*?//', 1, rec2);
end;
if index(rec2, "*/") then aste1fn = 1;
if cont1fn = 1 then do;
if aste1fn ^= 1 then rec2 = "";
else if aste1fn = 1 then do;
rec2 = prxchange('s/(^.*?¥*¥/)//', 1, rec2); cont1fn = 0; end;
テキスト操作の柔軟性
– Perl Regular Expression
7
データステップの処理(一部:条件分岐の提示のみ)
*** 以下のパターンに場合分けしてフラグを設定 ;
** 1.data statementが一行内で完結している場合;
prxmatch('m/;( *)data( +)([¥w_])+(.*);/i', rec2) ** 2.前行の最後が「;」「*/」で終了している場合;
prxmatch('m/(;|¥*¥/)$/', trim(pre_line2)) * 2-1.「data 」で終了している場合(継続);
prxmatch('m/^data( *)$/i', trim(rec2)) * 2-2.「data 」が存在する場合(継続);
prxmatch('m/^data( *)/i', rec2) * 2-3.一行内で完結している場合;
prxmatch('m/^data( +)([¥w_])+(.*);/i', rec2) ** 3.先頭行の場合;
* 3-1.「data 」で終了している場合(継続);
prxmatch('m/^data( *)$/i', trim(rec2))
* 3-2.「data 」+データセット名等で終了している場合(継続); prxmatch('m/^data( +)([¥w_])+(.*)/i', rec2) * 3-3.一行内で完結している場合;
prxmatch('m/^data( +)([¥w_])+(.*);/i', rec2) ** 4.その他の場合; ...
<prxmatch関数>
(perl-regular-expression, source)
* 引数の1番目がPerl正規表現で、特定の
文字列パターンに該当するかを判定
各パターンを考慮することで、データステッ
プの開始かどうかを判定
データハンドリングの柔軟性
– Hash Object
A
B
C
D
E
Q
O
P
X
Y
①
②
③
④
X座標の決定:①~④
• 最終ステップを含むグループを支点と
して決定
• フローの上流から分岐を決定
• Input/Outputの関係から、どの分岐先
に属するか判定(下プログラム)
Y座標の決定:(1)~(11)
• 下流(最終ステップ)から上流へ決定
• 分岐がある場合に複数のInputが同座
標になるように決定
X座標の決定方向
Y
座標の決定方向
(1)
(2)
(3)
(4)
(5)
(7)
(9)
(10)
(8)
(11)
8 *** X座標(group)の決定; dcl hash itxtlst();itxtlst.definekey('itext'); itxtlst.definedata('group'); itxtlst.definedone(); rc1 = itxtlst.check(key: text); ** Object itxtlistに出力テキスト(text)が存在するか確認;
if rc1 = 0 then rc2 = itxtlst.find(key: text); ** itxtlistに存在すれば、groupを取得; if group ^= . and (istepseq <= 2) then igroup = group;
if igroup > &i. then rc3 = itxtlst.add(key: itext, data: igroup); ** Inputの情報をHashに格納;
フローチャートの作成
• SAS9.4からSGPLOTプロシジャにPOLYGON/TEXTステートメントが追
加された
ods graphics / imagemap=on; ** TIPオプション使用に必須;
proc sgplot data=output noborder noautolegend;
** POLYGONステートメントによるボックスの描画; polygon id=boxid x=box_x y=box_y;
** TEXTステートメントによるテキストの描画;
text x=text_x y=text_y text=text /
textattrs=(size=5) splitchar='.' splitpolicy=splitalways tip = (code); ** SERIESステートメントによる矢印の描画;
series x=arrow_x y=arrow_y /
group=arrowid lineattrs=(color=black) arrowheadpos=end arrowheadscale=0.2;
** 軸の設定;
xaxis display=none min=&xmin. max=&xmax. offsetmin=0 offsetmax=0; yaxis display=none min=&ymin. max=&ymax. offsetmin=0 offsetmax=0; run;
Textを囲う枠線を定義するデータセットBox由
来の変数:頂点の座標を定義
矢印を定義するデータセット
Arrow由来の
変数:始点・終点の座標を定義
9結果
1 – 単純なプログラムの場合
10☆
TIPオプションにより指定した変数の値を
表示できる
⇒各ステップのコードを表示するように設定
** Create Data A;data A; set sashelp.class; run;
proc sort data = A; by name age; run; ** Create Data B by SQL procedure;
proc sql;
create table B as
select X.*, Y.pop from X
inner join
(select * from sashelp.demographics) as Y on X.COUNTRY = Y.ISONAME;
quit;
proc sort data = B out = B; by name age; run; ** Create Data C from A and B;
data C;
merge A (in = in1) B (in = in2);
by name age; if in1 = in2;
run;
proc print data = C noobs; run;