医薬の話,Analysis Results StandardってCDISCの標準の一つにYAMLっていうファイル形式がめっちゃ出てくる.
しかしSASにはProc JSONのようにYAMLファイルを簡単に作ってくれる仕組みがない.構造としてYAMLはJSONの上位互換のようなので,JSONで出してYAMLに変換してもいいけど,まあ普通にSASからダイレクトに作れた方がハッピーでしょうということで,簡単に作ってみた.
要はproc JSONは proc streamにwriteステートメントやexportステートメントといった部品が組み込まれたようなものなので,proc streamに,複数のデータセットをソースとすることができ,その内容を指定構造 として流し込めるパーツマクロがあれば,似たようなものができると考えた
だいたいこういうのやろうって時は,類似のアイデアが過去のPharmaSUGかPhuseかglobal forumにあがってるものだけど,今回は珍しく全くなかった気がする
まだ開発途中で,今はとりあえず,ネスト構造どうやって実装してやろうかなって思ったりしてる
ただ単純なものなら 多分できたっぽいので公開.たぶん現在進行形で似たようなことやらされて困ってる人が業界にいるんじゃないかなぁとか思って一助になれば幸い
とりあえず,任意データセットから,マッピング・シーケンス・マッピングのシーケンスを任意の階層(インデント)で,だせるようにしました.データセットからの絞りもいれたので1obsでマッピングすればスカラーにもなります(スカラーなら手でうてばいいけど,動的にだしたければ可能よってことで)
一応,サンプルでつくったYAMLが正しく認識されて,JSONならこうなるよっていうのは変換サイト(https://codebeautify.org/yaml-to-json-xml-csv)で試して見ました
以下 マクロと 実行サンプル.まだARSの各種公開されてるサンプルほどの構造を全部楽につくれるほどではないけど,ネストの問題さえどうにかすれば,まーSASから 比較的直観的に設計できるんじゃないかなぁとか
/*======================
macroname: ymlexport
author: sasyama
version:0.1 2024-05-27
説明: proc JSONにおけるexportステートメントのノリで使ってください
varlist=に半スペで列記した変数にたいして、index=で指定した階層(1につき半スペ2に変換))さげて出力
cat=で指定できる構造の種類は以下の3つ
[1] mapping
Name: アルフレッド
Age: 14
[2] sequence
- アルフレッド
- 14
[3] mappingsequence
- Name: アルフレッド
Age: 14
さらに構造の下に、マッピングやシーケンスを持った、深い構造の実装については現在検討中
wh=には%nrbquote()を配置
マクロ変数のスコープは未整備。グローバルで、MVAR_i_j MVARNAME_i_j (iはobs分 jは変数分できる) yobs nw__
----------------------------------
Copyright (c) [2024] [sasyama]
This software is released under the MIT License.
http://opensource.org/licenses/mit-license.php
========================*/
%let nw=__ NEWLINE;
%macro ymlexport(ds=,wh= ,cat=mapping,varlist=,indent=1);
%local yobs;
%let varlist=%sysfunc(compbl(&varlist));
%let varnum = %sysfunc( count( &varlist, %str ( ) ))+1;
%do i = 1 %to &varnum+1;
%let var&i = %sysfunc(scan( &varlist,&i, %str ( ) ));
%put &&var&i;
%end;
%macro dloop;
%do i =1 %to &varnum;
call symput(cats("mvarname&i._",_N_),ksubstr(cat(repeat(" ",&indent * 2),vname(&&var&i)),2));
call symputx(cats("mvar&i._",_N_),&&var&i);
call symput(cats("mvarseq&i._",_N_),ksubstr(cat(repeat(" ",&indent * 2),"- ",&&var&i),2));
%if &i=1 %then %do;
call symput(cats("mvarname_mapseq&i._",_N_),ksubstr(cat(repeat(" ",&indent * 2),"- ",vname(&&var&i)),2));
%end;
%else %do;
call symput(cats("mvarname_mapseq&i._",_N_),ksubstr(cat(repeat(" ",&indent * 2 + 2),vname(&&var&i)),2));
%end;
%end;
%mend;
%let _rc = %sysfunc(dosubl(%str(
data tmp;
set &ds;
where &wh;
run;
data _NULL_;
if 0 then set tmp nobs=NOBS;
call symputx("yobs", NOBS);
stop;
run;
data _null_;
set tmp;
%dloop;
run;
)));
%do h=1 %to &yobs.;
%do i = 1 %to &varnum;
%if %index(%lowcase(&cat),mapping) and %index(%lowcase(&cat),sequence) %then %do;
&&mvarname_mapseq&i._&h: &&mvar&i._&h &nw.;
%end;
%else %if %index(%lowcase(&cat),mapping) %then %do;
&&mvarname&i._&h: &&mvar&i._&h &nw.;
%end;
%else %if %index(%lowcase(&cat),sequence) %then %do;
&&mvarseq&i._&h &nw.;
%end;
%end;
%end;
%mend;
/*マクロ終わり*/
/*実行例*/
proc stream outfile=sample resetdelim="__";
begin
#マッピングの例 &nw.;
sashelp.class: &nw.;
%ymlexport(ds=sashelp.class
,wh=%nrbquote(name="アルフレッド")
,cat=mapping
,varlist=name age sex
,indent=1)
--- &nw.;
#シーケンスの例 &nw.;
sashelp.class: &nw.;
%ymlexport(ds=sashelp.class
,wh=%nrbquote(name="アルフレッド")
,cat=sequence
,varlist=name age sex
,indent=1)
--- &nw.;
#マッピングをシーケンスにする例 &nw.;
sashelp.class: &nw.;
%ymlexport(ds=sashelp.class
,wh=%nrbquote(name in ("アルフレッド","アリス"))
,cat=mappingsequence
,varlist=name age sex
,indent=1)
;;;;
run;