FCMPにおけるDictionary/Hash Object.SQLとハッシュを繋ぐもの

803 Views

October 21, 24

スライド概要

[第10回大阪sas勉強会]

profile-image

SAS言語を中心として,解析業務担当者・プログラマなのコミュニティを活性化したいです

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

関連スライド

各ページのテキスト
1.

FCMPにおけるDictionary/Hash Object. SQLとハッシュを繋ぐもの イーピーエス株式会社 森岡 裕

2.

本発表は中級者向け以上です. 以下の前提知識ついては本発表では説明しません 初級者は一度に理解しようと聴くのではなく,イメージをつかむ感じで 【前提知識】 FCMPプロシジャ(ユーザー定義関数を作成するプロシジャ) ハッシュオブジェクト SQL

3.

%macro get(master=,key=,var=); %let name = &sysindex; %let qkey = %sysfunc( tranwrd( %str("&key") , %str( ) , %str(",") ) ); keyが一意になっていることを前提として、他の データセットから値を取得するマクロ %get if 0 then set &master(keep= &key &var); if _N_=1 then do; declare hash h&name.(dataset:"&master(keep= &key &var)", duplicate:'E'); keyが一意になってなくても動き、keyが存在す h&name..definekey(&qkey); るかをYNで返すマクロ %chk h&name..definedata(all:'Y'); h&name..definedone(); この2つのマクロがあれば、例えばADSLなどは end; 結合と存在確認の2機能 ほぼ1ステップで作成可能。 if h&name..find() ne 0 then do; call missing(of &var); end; %mend get; %macro chk(master=,key=,flg=); %let name = &sysindex; %let qkey = %sysfunc( tranwrd( %str(“&key”) , %str( ) , %str(“,”) ) ); %Getマクロ if 0 then set &master(keep= &key); if _N_=1 then do; declare hash h&name.(dataset:”&master(keep= &key)”, multidata:’Y’); h&name..definekey(&qkey); h&name..definedone(); %Chkマクロ end; &flg = ifc(h&name..check()=0,”Y”,”N”); %mend chk;

4.

data adsl(label=""); set sdtm.dm; findメソッドと Checkメソッドをマクロ化してしまえばSDTM/ADaM のデータハンドリングは,(乱暴に言って)ほぼそ れで勝負がつく VISIT=“Day 1”; %get(master=EX,key=USUBJID VISIT,var=EXDAT) TRTSDT=input(EXDAT,?? Yymmdd10.); CMCAT="CONCOMITANT MEDICATION"; %chk(master=cm,key=usubjid cmcat,fl=CMFL); ・・・・・・・・・・・ run;

5.

ハッシュオブジェクトを綺麗にマクロ化すると ハンドリング周りのだいたいの問題はクリアされるが全てがそれで解決するわけではない. たとえば… ・ 通常のハッシュオブジェクトはデータステップ内の記述のため SQLプロシジャの中で利用できないことの不便さ ・ whereステートメントメ内で作動させれない. 例えば1億オブザベーションのデータセットAについて, 数百obsのデータセットBにIDがある症例だけをwhereで絞りたいといった状況 ・ マクロを減らしたい.あるいは複雑なネスト構造のマクロ内での使用時に, 見通しをよくするためにマクロに依らずにハッシュオブジェクトを 使いたいといった状況

6.

FCMPプロシジャでユーザー定義関数を作成できるが その中で, ハッシュオブジェクトとディクショナリオブジェクトが利用できる 今日はその話をしてみたいです

7.

data dm; USUBJID="A-001";SUBJID="001";RFSTDTC="2024-09-01";RFENDTC="2024-10-01";output; USUBJID="A-002";SUBJID="002";RFSTDTC="2024-09-02";RFENDTC="2024-10-02";output; USUBJID="A-003";SUBJID="003";RFSTDTC="2024-09-03";RFENDTC="2024-10-03";output; run; data raw_ae; SUBJID="001";AETERM="Vomit";output; SUBJID="003";AETERM="Vomit";output; SUBJID="005";AETERM="Vomit";output; run; DMドメインからSUBJIDをキーにして USUBJIDを取得するような処理を考 えてみる

8.

素直に平文で書くなら data samp1; set raw_ae; if 0 then set dm(keep=SUBJID USUBJID); if _N_ =1 then do; declare hash h1(dataset:"dm"); h1.definekey("SUBJID"); h1.definedata("USUBJID"); h1.definedone(); end; if h1.find() ne 0 then call missing(of USUBJID); run; 先に紹介したマクロを使えば1行 data samp2; set raw_ae; %get(master=dm, key=SUBJID, var=USUBJID); run;

9.

これを関数で考える

10.

proc fcmp outlib=work.functions.common; /*SUBJIDをいれるとDMドメインからUSUBJIDを返す関数*/ function usubjid(SUBJID $) $; declare hash h1(dataset: "DM"); rc=h1.definekey("SUBJID"); rc=h1.definedata("USUBJID"); rc=h1.definedone(); if h1.find() eq 0 then return(USUBJID); else return(""); endsub; quit; え… 簡単すぎ... /*使ってみる */ data wk1; length SUBJID AETERM USUBJID $200.; set raw_ae; USUBJID=usubjid(SUBJID); run;

11.

え,えすきゅーえるの中で ハッシュオブジェクトがうごいとるーーーーー proc sql noprint; create table wk2 as select SUBJID, usubjid(SUBJID) as USUBJID ,AETERM from raw_ae; quit;

12.

data wk3; set raw_ae; where usubjid(SUBJID) ="A-001"; run; データセットに存在しない,USUBJIDで絞り込みを行うという whereステートメントにおいて,びっくりなことが起きている

13.

AEドメインの中にSUBJIDがあるかどうかでYNのフラグを返す処理を考えてみる 素直に平文で書くなら 先に紹介したマクロを使うなら data samp3; set dm; data samp4; if _N_=1 then do; set dm; declare hash %chk(master=raw_ae,key=SUBJID,flg=AEFL) h1(dataset:"raw_ae",multidata:"Y"); run; h1.definekey("SUBJID"); h1.definedone(); end; AEFL = ifc( h1.check() = 0 , "Y", "N"); run;

14.

proc fcmp outlib=work.functions.common; /*SUBJIDをいれるとAEドメインにあるかどうかでYNが返る関数*/ function ae_chk(SUBJID $) $; declare hash h1(dataset: "raw_ae"); rc=h1.definekey("SUBJID"); rc=h1.definedone(); if h1.check() eq 0 then return("Y"); else return("N"); endsub; quit; これも当然,SQLでもwhere句でも動く data wk5; set dm; AEFL= ae_chk(SUBJID) ; run;

15.

data wk6; set dm; where ae_chk(SUBJID) ="Y"; run; 通常,データステップのwhereステートメントでは SQLにおける in サブクエリや Exists句をつかったような処理が書けないというのが 文法だったが proc sql; create table wk8 as select * from dm where exists (select 1 from raw_ae where dm.SUBJID=raw_ae.SUBJID); quit; proc sql; create table wk7 as select * from dm FCMPのハッシュオブジェクトを使うことで可能となること where SUBJID in が (select SUBJID from raw_ae); わかった quit;

16.

どんな技術にも必ず弱点がある ハッシュオブジェクトの関数化も

17.

関数定義時にハッシュオブジェクトに格納のデータセットが固定 引数にできない proc fcmp outlib=work.functions.common; /*SUBJIDをいれるとDMドメインからUSUBJIDを返す関数*/ function usubjid(SUBJID $) $; declare hash h1(dataset: "DM"); rc=h1.definekey("SUBJID"); rc=h1.definedata("USUBJID"); rc=h1.definedone(); if h1.find() eq 0 then return(USUBJID); else return(""); endsub; quit; 関数の場合は一つの戻り値しかとれない. コールルーチンにするとSQLの中で動かない.

18.

サブルーチンもできる

19.

proc fcmp outlib=work.functions.common; /*USUBJIDをキーにRFSTDTCとRFENDTCを返すサブルーチン*/ subroutine rfdtc(USUBJID $ , RFSTDTC $, RFENDTC $) ; outargs RFSTDTC, RFENDTC; declare hash h1(dataset: "DM"); rc=h1.definekey("USUBJID"); rc=h1.definedata("RFSTDTC"); rc=h1.definedata("RFENDTC"); rc=h1.definedone(); if h1.find() ne 0 then call missing(of RFSTDTC RFENDTC); endsub ; quit; data wk4; length SUBJID USUBJID RFSTDTC RFENDTC $200.; set raw_ae; USUBJID=usubjid(SUBJID); call missing(of RFSTDTC RFENDTC); call rfdtc(USUBJID, RFSTDTC, RFENDTC); run;

20.

FCMPの中のハッシュオブジェクトで使えるメソッドは以下の通り DO_OVERメソッドやSUMメソッドなどは,使えない.通常のハッシュオブジェクトに比べて 制限がかかることをに注意

21.

Dictionaryオブジェクト

23.

proc fcmp; declare dictionary dc; length data2 data3 data5 $200; dc[1] = 100; dc[2] = 200; dc["A"]="AAA"; dc["B"]="BBB"; dc[1,1] = 1000; dc["A","B",1] = "CCC"; key1 = 1; key4_1 = 1; data1 = dc[key1]; key4_2 = 1; put key1= data1= ; data4 = dc[key4_1,key4_2]; key2 = "A"; put key4_1= key4_2= data4=; data2 = dc[key2]; key5_1 = "A"; put key2= data2=; key5_2 = "B"; dc[1] = "ZZZ"; key5_3 = 1; data3 = dc[key1]; data5 = dc[key5_1,key5_2,key5_3]; put key1= data3=; put key5_1= key5_2= key5_3= data5=; run; quit;

24.

FCMPの中のDcitonaryオブジェクトで使えるメソッドは以下の通り ハッシュオブジェクトに似ている.ただ,ハッシュオブジェクトと違って 単純な出し入れだけの場合は メソッドを必要とせず 辞書名[key] =値で操作ができ,それが一番多く使う あと,上級者向けになりすぎるので,言及しませんが,実は辞書には,ハッシュオブジェクトや配列など 通常の単一値以外のものを格納することができる… なんと..

25.

テストデータ data a; SEX="M";COUNTRY="JPN";output; SEX="F";COUNTRY="CHN";output; SEX="M";COUNTRY="JPN";output; run; 値から,それぞれ 英語用・日本語用の表示が欲しい場合など,SASフォーマットではなく 今回の技を使う

26.

proc fcmp outlib=work.functions.common; function f1(key1 $,key2 $,key3 $) $ ; length key1 key2 rvalue dummy $200 ; _key1=strip(key1); _key2=strip(key2); _key3=strip(key3); ①英語or日本語 declare dictionary d1 ; ②カテゴリ名 d1['E','SEX','M']='Male'; d1['J','SEX','M']='男性'; ③カテゴリ値 d1['E','SEX','F']='Female'; の 3つのキーで管理される辞書オブジェクトをつくる d1['J','SEX','F']='女性'; d1['E','COUNTRY','JPN']='Japan'; d1['J','COUNTRY','JPN']='日本'; d1['E','COUNTRY','CHN']='China'; d1['J','COUNTRY','CHN']='中国'; rvalue=d1[_key1,_key2,_key3]; return(rvalue); endsub; run; quit;

27.

options cmplib=work.functions ; data output1; length out1 out2 $200.; set a; out1=f1("E","SEX",SEX); out2=f1("E","COUNTRY",COUNTRY); run; proc sql; select f1("E","SEX",SEX) as out1, f1("E","COUNTRY",COUNTRY) as out2 from output1; quit; proc sql; select f1("J","SEX",SEX) as out1, f1("J","COUNTRY",COUNTRY) as out2 from output1; quit;

28.

data b; SEX="M";output; SEX="F";output; SEX="U";output; run; data c; set b; where f1("E","SEX",SEX) ne ""; run; これもまたwhereステートメントで作動するため,辞書にあるもの(ないもの)のみを抽出するといったことが 可能になる

29.

余談

30.

マニアックすぎて,過去の私の総会論文発表の中で,一番ウケが悪かったものとして SQLの中で,LAG関数が使えない制限を, FCMPの ハッシュオブジェクトやDictionaryで無理やり突破するというの があるので,気になる人はぜひ…

35.

まー そんなこんなで,自分はハッシュオブジェクトを通常のマクロ ベースで使ってますが,毎度かき捨てで関数化したり ADSLやADTTEのような構造固定のADaMの処理を関数にしたり, 色々有用かも.あと自分,SASフォーマット嫌いなので それならむしろ 全部Dictionaryオブジェクトにしたい ご清聴ありがとうございましたー