SASテクニック 臨床試験の解析1 ADaMデータから有害事象の集計をする

# CDISC準拠のADaMデータの解析プログラムの紹介
臨床試験のデータ解析をSASで行う例
有害事象の集計や臨検値の集計は、ほとんど同じ
rtfやexcel出力まではやらないが、出力直前のデータまでをなんとかSQLでやってみる
そうすればRやpythonなど他の言語でも再現しやすい

臨検値の要約統計量算出について
https://qiita.com/saspy/items/8fa79567807682b8b55c

# CDISC知らない人への補足
CDISC準拠のADaM.ADSLとADaM.ADAEから有害事象の集計(SOC/PTごと)をする

ADSLは1人1行で、なんの薬飲んだかとかその人自身の情報が入ってる
今回は、各グループに何人いるのか、分母の計算に使用
ADAEは、発生した有害事象のデータ
※発生のY,Nのフラグがあるわけではなく、むしろYだけ抽出した感じ
詳しくは、ADaM IGとかCDISCとかでググってください

SOC/PT:有害事象をグループ分けしたときの、大きい分類、小さい分類
MedDRAでググるといいかも

# AE集計をSQLだけでやってみた!
ADSLとADAEから有害事象(AE)の集計を行う
SAFFLとTRTEMFLを抽出条件、それ以外を使う場合はマクロ変数Whereに指定

sortは、IAO順(マクロ変数に変数名をいれる)、SOCコード順、PTコード順
AE集計は基本同じなので、マクロ化してみた

# libname

```sas
libname adam "ADaMのデータセットのフォルダパス" access = readonly;
```


# AE集計マクロ

```sas
%macro AE_Macro(IAO = , Where = %str());
%* TRT01ANのmin,max *;
proc Sql noprint;
  select min( TRT01AN ) into : Min_N trimmed from adam.ADSL where SAFFL = "Y";
  select max( TRT01AN ) into : Max_N trimmed from adam.ADSL where SAFFL = "Y";
quit;
%let TRT_N = %sysevalf( &Max_N. - &Min_N. + 1 );
%put 群の数:&TRT_N.;

proc Sql noprint;
%* 全体の集計 *;
  create table work.AE_ALL as
    select
      TRTAN , TRTA , BUNBO ,
      count( distinct USUBJID ) as REISUU ,
      count( USUBJID ) as KENSUU ,
      cats( calculated REISUU ) || " (" || put( round( calculated REISUU / BUNBO * 100 , 1e-1 ) , 5.1 ) || ") [" || cats( calculated KENSUU ) || "]" as OUT length = 50
    from
      ( select A.* , B.BUNBO
        from adam.ADAE( where = ( SAFFL = "Y" and TRTEMFL = "Y" %if %length( &Where. ) > 0 %then and &Where.; ) ) as A
        left join
          ( select TRT01AN , TRT01A , count( USUBJID ) as BUNBO
            from adam.ADSL( where = ( SAFFL = "Y" ) )
            group by TRT01AN, TRT01A
          ) as B
        on A.TRTAN = B.TRT01AN and A.TRTA = B.TRT01A
      )
    group by TRTAN , TRTA , BUNBO;

%* SOCごとの集計 *;
  create table work.AE_SOC as
    select
      &IAO. , AESOCCD , AESOC , TRTAN , TRTA , BUNBO ,
      count( distinct USUBJID ) as REISUU,
      count( USUBJID ) as KENSUU,
      cats( calculated REISUU ) || " (" || put( round( calculated REISUU / BUNBO * 100 , 1e-1 ) , 5.1 ) || ") [" || cats( calculated KENSUU ) || "]" as OUT length = 50
    from
      ( select A.* , B.BUNBO
        from adam.ADAE( where = ( SAFFL = "Y" and TRTEMFL = "Y" %if %length( &Where. ) > 0 %then and &Where.; ) ) as A
        left join
          ( select TRT01AN , TRT01A , count( USUBJID ) as BUNBO
            from adam.ADSL( where = ( SAFFL = "Y" ) )
            group by TRT01AN , TRT01A
          ) as B
        on A.TRTAN = B.TRT01AN and A.TRTA = B.TRT01A
      )
    group by &IAO. , AESOCCD , AESOC , TRTAN , TRTA , BUNBO;

%* PTごとの集計 *;
  create table work.AE_PT as
    select
      &IAO. , AESOCCD , AESOC , AEPTCD , AEDECOD , TRTAN , TRTA , BUNBO ,
      count( distinct USUBJID ) as REISUU ,
      count( USUBJID ) as KENSUU ,
      cats( calculated REISUU ) || " (" || put( round( calculated REISUU / BUNBO * 100 , 1e-1 ) , 5.1 ) || ") [" || cats( calculated KENSUU ) || "]" as OUT length = 50
    from
      ( select A.*, B.BUNBO
        from adam.ADAE( where = ( SAFFL = "Y" and TRTEMFL = "Y" %if %length( &Where. ) > 0 %then and &Where.; ) ) as A
        left join
          ( select TRT01AN , TRT01A , count( USUBJID ) as BUNBO
            from adam.ADSL( where = ( SAFFL = "Y" ) )
            group by TRT01AN , TRT01A
          ) as B
      on A.TRTAN = B.TRT01AN and A.TRTA = B.TRT01A
      )
    group by &IAO. , AESOCCD , AESOC , AEPTCD , AEDECOD , TRTAN , TRTA , BUNBO;

%* 集計結果まとめ *;
  create table work.STAT_RESULT as
    select &IAO. , AESOCCD , AESOC , AEPTCD , AEDECOD
           %do i = &Min_N. %to &Max_N. ;
             , max( case when TRTAN = &i. then OUT else "0 ( 0.0) [0]" end ) as OUT&i. length = 50
           %end ;
    from
      ( select * from work.AE_ALL
        outer union corr
        select * from work.AE_SOC
        outer union corr
        select * from work.AE_PT
      )
    group by &IAO. , AESOCCD , AESOC , AEPTCD , AEDECOD
    order by &IAO. , AESOCCD , AESOC , AEPTCD , AEDECOD;
quit;

%mend;
```

%AE_Macro(IAO = IAOの変数名, Where = %str());で実行(where = %str(AESER = "Y")など)
OUT1,OUT2などに例数と割合と件数の値が入る
クロスオーバー試験などには未対応

# memo
全体、SOC、PTごとに計算
count( distinct A )で重複削除してAをカウントする
↑を利用して、例数と件数を算出
SQLの練習として、転置もtransposeプロシジャを使わずにやってみた
※ TRTANの値をベタ打ちするのは汎用性が低いため、マクロでループを回した
IAOの変数名はADaM IGで決まってなかった気がするので、マクロパラメータで指定する