SAS MACRO プログラミング#

このレッスンでは、SASマクロ言語で最も一般的に使用される機能を紹介します。プログラムを繰り返し実行する必要がある場合は、コードにマクロを使用することを真剰に検討する価値があります。なぜなら、

  • マクロを使用すると、プログラムの1か所を変更するだけで、SASがその変更をプログラム全体に反映させることができます

  • マクロを使用すると、同じプログラムまたは異なるプログラムで、コードの一部を一度書いて何度も使用できます

  • マクロを使用すると、プログラムをデータ駆動型にすることができ、SASが実際のデータ値に基づいて動作を決定できます

導入とさらなる例については、以下を参照してください。

  • SAS Macro Programming for Beginners

  • Little SAS Book: A Primer, 5th ed.の第7章「Writing Flexible Code with the SAS Macro Facility」

  • Learning SAS by Example: A Programmer’s Guideの第25章「Introducing the SAS Macro Language」

ここの例では、心疾患のリスクに関する大規模な縦断的疫学研究であるデータセットFraminghamを使用します。fghm113.sas7bdatというデータセットファイルをダウンロードして、任意のフォルダーに置いてください。次に、以下のコードでLIBNAMEパスをこのフォルダーを指すように変更してください。

libname myData "/folders/myfolders/SAS_Notes/data";

*Create a temporary data set, so that we don't save changes to the original data set.;
data fghmtemp;
  set myData.fghm113;
run;

/*Now let's code some variables with some more descriptive values.
  SEX (Gender): 1=Men 
        2=Women
  Period (Examination cycle): 1=Period1 
                2=Period2
                3=Period3
  BPMEDS (Use of anti-hypertensive meds): 0=Not currently
                      1=Currently use
  CURSMOKE (Currently smoke?): 0=No
                 1=Yes
  DIABETES: 0=Not diabetic
      1=Diabetic
  PREVAP (Have angina pectoric?): 0=No
                  1=Yes
  PREVCHD (Coronary heart disease?): 0=No
                   1=Yes
  PREVMI (Myocardial infarction?): 0=No
                   1=Yes
  PREVSTRK (Had a stroke?): 0=No
                 1=Yes
  PREVHYP (Hypertensive? sys bp >=140 or dyas bp >= 90): 0=no
                             1=yes
*/

proc format;
  value YNfmt 0="No"
        1="Yes";
  value perfmt 1="Period 1"
        2="Period 2"
        3="Period 3";
  value gndrfmt 1="Men"
        2="Women";
run;

data fghmtemp;
  set fghmtemp;
  format prevap ynfmt.
       diabetes ynfmt.
       cursmoke ynfmt.
       bpmeds ynfmt.
       prevchd ynfmt.
       prevmi ynfmt.
       prevstrk ynfmt.
       prevhyp ynfmt.
       sex gndrfmt.;
run;

*Check to see if the formatting was done correctly;
proc contents data = fghmtemp;
run;

proc print data = fghmtemp (obs=5);
run;
SAS 出力

SAS システム

CONTENTS プロシジャ

データセット名 WORK.FGHMTEMP オブザベーション数 500
メンバータイプ DATA 変数の数 21
エンジン V9 インデックス数 0
作成日時 2024/06/06 08:36:05 オブザベーションのバッファ長 168
更新日時 2024/06/06 08:36:05 削除済みオブザベーション数 0
保護   圧縮済み NO
データセットタイプ   ソート済み NO
ラベル      
データ表現 SOLARIS_X86_64, LINUX_X86_64, ALPHA_TRU64, LINUX_IA64    
エンコード utf-8 Unicode (UTF-8)    
エンジン/ホスト関連情報
データセットのページサイズ 131072
データセットのページ数 1
データページの先頭 1
ページごとの最大OBS数 779
先頭ページのOBS数 500
データセットの修復数 0
ファイル名 /saswork/SAS_workED820000E33E_odaws01-apse1.oda.sas.com/SAS_work826B0000E33E_odaws01-apse1.oda.sas.com/fghmtemp.sas7bdat
作成したリリース 9.0401M7
作成したホスト Linux
I ノード番号 536886844
アクセス権限 rw-r--r--
所有者名 user-name
ファイルサイズ 256KB
ファイルサイズ (バイト) 262144
変数と属性リスト (アルファベット順)
# 変数 タイプ 長さ 出力形式 入力形式 ラベル
4 AGE 数値 8 BEST12. F12. Age (years) at examination
9 BMI 数値 8 BEST12. F12. Body Mass Index (kr/(M*M)
11 BPMEDS 数値 8 YNFMT. F12. Anti-hypertensive meds Y/N
8 CIGPDAY 数値 8 BEST12. F12. Cigarettes per day
7 CURSMOKE 数値 8 YNFMT. F12. Current Cig Smoker Y/N
10 DIABETES 数値 8 YNFMT. F12. Diabetic Y/N
6 DIABP 数値 8 BEST12. F12. Diastolic BP mmHg
13 GLUCOSE 数値 8 BEST12. F12. Casual Glucose mg/dL
20 HDLC 数値 8 BEST12. F12. HDL Cholesterol mg/dL
12 HEARTRTE 数値 8 BEST12. F12. Ventricular Rate (beats/min)
21 LDLC 数値 8 BEST12. F12. LDL Cholesterol mg/dL
19 PERIOD 数値 8 BEST12. F12. Examination cycle
15 PREVAP 数値 8 YNFMT. F12. Prevalent Angina
14 PREVCHD 数値 8 YNFMT. F12. Prevalent CHD (MI,AP,CI)
18 PREVHYP 数値 8 YNFMT. F12. Prevalent Hypertension
16 PREVMI 数値 8 YNFMT. F12. Prevalent MI (Hosp,Silent)
17 PREVSTRK 数値 8 YNFMT. F12. Prevalent Stroke (Infarct,Hem)
2 RANDID 数値 8 BEST12. F12. Random ID
1 SEX 数値 5 GNDRFMT. F12. SEX
5 SYSBP 数値 8 BEST12. F12. Systolic BP mmHg
3 TOTCHOL 数値 8 BEST12. F12. Serum Cholesterol mg/dL

SAS システム

OBS SEX RANDID TOTCHOL AGE SYSBP DIABP CURSMOKE CIGPDAY BMI DIABETES BPMEDS HEARTRTE GLUCOSE PREVCHD PREVAP PREVMI PREVSTRK PREVHYP PERIOD HDLC LDLC
1 Women 11263 220 55 180 106 No 0 31.17 Yes Yes 86 81 No No No No Yes 3 46 135
2 Men 16365 211 55 173 123 No 0 29.11 No Yes 75 85 No No No No Yes 3 48 163
3 Women 43770 283 64 159 69 No 0 32.93 Yes No 70 230 No No No No Yes 3 45 238
4 Men 47561 290 56 132 70 Yes 40 22.73 No No 100 90 No No No No Yes 3 54 236
5 Women 55965 298 72 109 60 No 0 26.2 No No 80 100 No No No No No 3 38 260

マクロ変数と %LET#

%LET macro-variable-name = value;

ここで、macro-variable-nameは、標準のSAS名の規則(32文字以内、文字またはアンダースコアで始まり、文字、数字、アンダースコアのみを含む)に従って作成する名前です。valueは、マクロ変数名に置き換えられるテキストで、長さは最大64,000文字です。以下のステートメントはそれぞれマクロ変数を作成します。

%LET iterations = 5;

%LET winner = Lance Armstrong;

値には引用符は付けません。先頭と末尾のスペースは切り捨てられ、等号とセミコロンの間のすべてがマクロ変数の値になります。

マクロ変数を使用するには、前にアンパサンド(&)と後ろにピリオド(.)を追加し、マクロ変数名を値を置き換えたい場所に挿入するだけです。
ピリオドは省略可能ですが、後ろに文字を続ける場合には必須になるので、つけるのを習慣にしておくことをおすすめします。
例えば、

DO i = 1 TO &iterations.

TITLE "First: &winner.";

このコードの行があるプログラムを実行すると、最初にマクロプロセッサがマクロ変数の値を置き換えてSASコードを生成します。

DO i = 1 TO 5;

TITLE "First: Lance Armstrong";

そして、生成されたコードを通常どおり実行します。

#

次のプログラムでは、3つのマクロ変数を使用してデータセット名と、そのデータセットから使用する2つの変数名を指定し、これらを使用して分割表とカイ2乗検定の出力を作成します。ここでは、糖尿病と過去の心血管疾患の関係をカイ2乗検定と分割表で示します。

%let response = prevchd;
%let predictor = diabetes;
%let dataset = fghmtemp;

title "&predictor vs &response.";
proc freq data = &dataset.;   
  table &predictor. * &response. / chisq;
run;

title;
SAS 出力

diabetes vs prevchd

FREQ プロシジャ

度数
パーセント
行のパーセント
列のパーセント
表 : DIABETES * PREVCHD
DIABETES(Diabetic Y/N) PREVCHD(Prevalent CHD (MI,AP,CI))
No Yes 合計
No
408
81.60
89.87
92.10
46
9.20
10.13
80.70
454
90.80
 
 
Yes
35
7.00
76.09
7.90
11
2.20
23.91
19.30
46
9.20
 
 
合計
443
88.60
57
11.40
500
100.00

DIABETES * PREVCHD の統計量

統計量 自由度 p 値
カイ 2 乗値 1 7.8534 0.0051
尤度比カイ 2 乗値 1 6.3873 0.0115
連続性補正カイ 2 乗値 1 6.5483 0.0105
Mantel-Haenszel のカイ 2 乗値 1 7.8377 0.0051
ファイ係数   0.1253  
一致係数   0.1244  
Cramer の V 統計量   0.1253  
Fisher の正確検定
セル (1,1) 度数 (F) 408
左側 Pr <= F 0.9972
右側 Pr >= F 0.0090
   
表の確率 (P) 0.0062
両側 Pr <= P 0.0119

標本サイズ = 500

マクロ変数「response」と「predictor」は、TABLEステートメントとTITLEステートメントの両方を更新するために使用されていることに注目してください。マクロプロセッサを通過した後、次のSASコードが生成され実行されます。

title "diabetes vs prevchd";
proc freq data = fghmtemp;   
  table diabetes * prevchd / chisq;
run;

これは短いプログラムで、マクロ変数の出現回数は数回しかありませんでした。しかし、マクロ変数の出現回数が数十回あるような長いプログラムを想像してみてください。プログラムの冒頭でマクロ変数を一度変更するだけで、多くの時間と手間を節約できるでしょう。

マクロの機能#

同じプログラムステートメントを繰り返し書いていることに気づいたら、マクロを作成することを検討するべきかもしれません。マクロは、名前が付けられたSASステートメントのグループにすぎません。そのステートメントのグループを実行したい場合は、ステートメントを再入力する代わりにそのマクロを使用します。

マクロの一般的な形式は次のとおりです。

%MACRO macro-name(parameter-1=, parameter-2=,..., parameter-n= );
   macro-text
%MEND macro-name;

例えば、「month」と「region」というパラメータを持つマクロ%MONTHLYREPORTの場合、次のように始まるでしょう。

%MACRO monthlyreport(month=, region= );

次に、マクロを呼び出す際は、等号の後にマクロ変数の値を指定します。

%monthlyreport(month=May, region=West);

#

次のプログラムでは、前の%LETマクロ変数を使用したコードをマクロ関数に変更し、指定したデータセットの指定した2つの変数間でカイ2乗検定と分割表を作成します。

%macro twobytwo(dataset, predictor, response);
title "&predictor. vs &response.";
proc freq data = &dataset.;
  table &predictor.*&response. / chisq;
run;
title;
%mend twobytwo;

%twobytwo(fghmtemp, diabetes, prevchd);
SAS 出力

diabetes vs prevchd

FREQ プロシジャ

度数
パーセント
行のパーセント
列のパーセント
表 : DIABETES * PREVCHD
DIABETES(Diabetic Y/N) PREVCHD(Prevalent CHD (MI,AP,CI))
No Yes 合計
No
408
81.60
89.87
92.10
46
9.20
10.13
80.70
454
90.80
 
 
Yes
35
7.00
76.09
7.90
11
2.20
23.91
19.30
46
9.20
 
 
合計
443
88.60
57
11.40
500
100.00

DIABETES * PREVCHD の統計量

統計量 自由度 p 値
カイ 2 乗値 1 7.8534 0.0051
尤度比カイ 2 乗値 1 6.3873 0.0115
連続性補正カイ 2 乗値 1 6.5483 0.0105
Mantel-Haenszel のカイ 2 乗値 1 7.8377 0.0051
ファイ係数   0.1253  
一致係数   0.1244  
Cramer の V 統計量   0.1253  
Fisher の正確検定
セル (1,1) 度数 (F) 408
左側 Pr <= F 0.9972
右側 Pr >= F 0.0090
   
表の確率 (P) 0.0062
両側 Pr <= P 0.0119

標本サイズ = 500

前と同じ出力を取得できましたが、今回はマクロを使用しています。またマクロでパラメータ名を指定していません。マクロの実行にパラメータ名を使用しない場合は、パラメータの値を正しい順序で指定する必要があります。この例では、マクロ twobytwoはデータセットを最初に、次に予測変数(行変数)、最後に応答変数(列変数)を想定しています。

MPRINT システムオプション#

マクロプロセッサが解決したプログラムをSASが扱うことを示しましたが、通常はこれらのステートメントは表示されません。ただし、プログラムでMPRINTシステムオプションを指定すると、SASはマクロから生成された解決済みのステートメントをログに出力します。これはデバッグ目的で役立ちます。MPRINTオプションを有効にするには、次のようにOPTIONSステートメントを実行します。

OPTIONS MPRINT;

ログは次のようになります。(この例ではPROC FREQのNOPRINTオプションを追加して出力を抑制しています。)

%macro twobytwo(dataset, predictor, response);
title "&predictor. vs &response.";
proc freq data = &dataset. noprint;    
  table &predictor.*&response. / chisq;
run;
title;
%mend twobytwo;

options mprint;

%twobytwo(fghmtemp, diabetes, prevchd);

options nomprint;
15                                                        SAS システム               2024年 6月 6日 木曜日 08時35分00秒

171        ods listing close;ods html5 (id=saspy_internal) file=_tomods1 options(bitmap_mode='inline') device=svg style=HTMLBlue;
171      ! ods graphics on / outputfmt=png;
172        
173        options notes ;
174        %macro twobytwo(dataset, predictor, response);
175        title "&predictor. vs &response.";
176        proc freq data = &dataset. noprint;
177          table &predictor.*&response. / chisq;
178        run;
179        title;
180        %mend twobytwo;
181        
182        options mprint;
183        
184        %twobytwo(fghmtemp, diabetes, prevchd);
MPRINT(TWOBYTWO):   title "diabetes vs prevchd";
MPRINT(TWOBYTWO):   proc freq data = fghmtemp noprint;
MPRINT(TWOBYTWO):   table diabetes*prevchd / chisq;
MPRINT(TWOBYTWO):   run;

NOTE: 表示出力または出力データセットへの有効な要求がないため、処理が終了されます。
NOTE: PROCEDURE FREQ処理(合計処理時間):
      処理時間           0.00 秒
      ユーザーCPU時間    0.00 秒
      システムCPU時間    0.00 秒
      メモリ             332.96k
      OSメモリ           18848.00k
      タイムスタンプ     2024/06/06 午前08:37:15
      ステップ数                        11  スイッチ数  0
      ページフォルト回数                0
      ページリクレーム回数              54
      ページスワップ回数                0
      自発的コンテキストスイッチ回数    0
      非自発的コンテキストスイッチ回数  0
      ブロック入力操作回数              0
      ブロック出力操作回数              0
      

MPRINT(TWOBYTWO):   title;
185        
186        options nomprint;
187        
188        
189        ods html5 (id=saspy_internal) close;ods listing;
190        

16                                                        SAS システム               2024年 6月 6日 木曜日 08時35分00秒

191        

MPRINTオプションを使用すると、SASがMPRINTの後にマクロの名前(この場合はtwobytwo)を表示して、そのマクロによって生成されたステートメントをラベル付けしていることが分かります。MPRINTシステムオプションを使用すると、マクロが生成する標準的なSASステートメントを簡単に確認できます。

%DO ループ#

DOループは繰り返しのコードを簡素化するのに役立ちますが、通常のDOループはデータステップ内でしか使用できません。変数を少し変えながら同じプロシージャを繰り返し実行したい場合はどうすればよいでしょうか。このような場合、%DOループが役立ちます。このステートメントの一般的な形式は次のとおりです。

%DO index-variable = start TO end;
   SAS code;
%END;

index-variableはループのカウンター変数としてのマクロ変数で、startからendまで動作します。このようなループはPROCステップやデータステップの外で使用でき、複数のこれらのステップをループ処理できます。

#

以下のように大量の変数の組み合わせが異なる2x2表を作成したいとします。

title "prevap vs prevchd";
proc freq data = fghmtemp;   
  table prevap * prevchd / chisq;
run;

title "diabetes vs prevchd";
proc freq data = fghmtemp;   
  table diabetes * prevchd / chisq;
run;

title "prevmi vs prevchd";
proc freq data = fghmtemp;   
  table prevmi * prevchd / chisq;
run;

title "prevstrk vs prevchd";
proc freq data = fghmtemp;
  table prevstrk * prevchd / chisq;
run;

title "prevhyp vs prevchd";
proc freq data = fghmtemp;   
  table prevhyp * prevchd / chisq;
run;
SAS 出力

prevap vs prevchd

FREQ プロシジャ

度数
パーセント
行のパーセント
列のパーセント
表 : PREVAP * PREVCHD
PREVAP(Prevalent Angina) PREVCHD(Prevalent CHD (MI,AP,CI))
No Yes 合計
No
443
88.60
96.30
100.00
17
3.40
3.70
29.82
460
92.00
 
 
Yes
0
0.00
0.00
0.00
40
8.00
100.00
70.18
40
8.00
 
 
合計
443
88.60
57
11.40
500
100.00

PREVAP * PREVCHD の統計量

統計量 自由度 p 値
WARNING: セルの25%において、期待度数が5より小さくなっています。
カイ2乗検定は妥当な検定でないと思われます。
カイ 2 乗値 1 337.9100 <.0001
尤度比カイ 2 乗値 1 209.3011 <.0001
連続性補正カイ 2 乗値 1 328.4425 <.0001
Mantel-Haenszel のカイ 2 乗値 1 337.2342 <.0001
ファイ係数   0.8221  
一致係数   0.6350  
Cramer の V 統計量   0.8221  
Fisher の正確検定
セル (1,1) 度数 (F) 443
左側 Pr <= F 1.0000
右側 Pr >= F <.0001
   
表の確率 (P) <.0001
両側 Pr <= P <.0001

標本サイズ = 500


diabetes vs prevchd

FREQ プロシジャ

度数
パーセント
行のパーセント
列のパーセント
表 : DIABETES * PREVCHD
DIABETES(Diabetic Y/N) PREVCHD(Prevalent CHD (MI,AP,CI))
No Yes 合計
No
408
81.60
89.87
92.10
46
9.20
10.13
80.70
454
90.80
 
 
Yes
35
7.00
76.09
7.90
11
2.20
23.91
19.30
46
9.20
 
 
合計
443
88.60
57
11.40
500
100.00

DIABETES * PREVCHD の統計量

統計量 自由度 p 値
カイ 2 乗値 1 7.8534 0.0051
尤度比カイ 2 乗値 1 6.3873 0.0115
連続性補正カイ 2 乗値 1 6.5483 0.0105
Mantel-Haenszel のカイ 2 乗値 1 7.8377 0.0051
ファイ係数   0.1253  
一致係数   0.1244  
Cramer の V 統計量   0.1253  
Fisher の正確検定
セル (1,1) 度数 (F) 408
左側 Pr <= F 0.9972
右側 Pr >= F 0.0090
   
表の確率 (P) 0.0062
両側 Pr <= P 0.0119

標本サイズ = 500


prevmi vs prevchd

FREQ プロシジャ

度数
パーセント
行のパーセント
列のパーセント
表 : PREVMI * PREVCHD
PREVMI(Prevalent MI (Hosp,Silent)) PREVCHD(Prevalent CHD (MI,AP,CI))
No Yes 合計
No
443
88.60
94.46
100.00
26
5.20
5.54
45.61
469
93.80
 
 
Yes
0
0.00
0.00
0.00
31
6.20
100.00
54.39
31
6.20
 
 
合計
443
88.60
57
11.40
500
100.00

PREVMI * PREVCHD の統計量

統計量 自由度 p 値
WARNING: セルの25%において、期待度数が5より小さくなっています。
カイ2乗検定は妥当な検定でないと思われます。
カイ 2 乗値 1 256.8548 <.0001
尤度比カイ 2 乗値 1 153.8559 <.0001
連続性補正カイ 2 乗値 1 247.5882 <.0001
Mantel-Haenszel のカイ 2 乗値 1 256.3411 <.0001
ファイ係数   0.7167  
一致係数   0.5826  
Cramer の V 統計量   0.7167  
Fisher の正確検定
セル (1,1) 度数 (F) 443
左側 Pr <= F 1.0000
右側 Pr >= F <.0001
   
表の確率 (P) <.0001
両側 Pr <= P <.0001

標本サイズ = 500


prevstrk vs prevchd

FREQ プロシジャ

度数
パーセント
行のパーセント
列のパーセント
表 : PREVSTRK * PREVCHD
PREVSTRK(Prevalent Stroke (Infarct,Hem)) PREVCHD(Prevalent CHD (MI,AP,CI))
No Yes 合計
No
438
87.60
89.02
98.87
54
10.80
10.98
94.74
492
98.40
 
 
Yes
5
1.00
62.50
1.13
3
0.60
37.50
5.26
8
1.60
 
 
合計
443
88.60
57
11.40
500
100.00

PREVSTRK * PREVCHD の統計量

統計量 自由度 p 値
WARNING: セルの25%において、期待度数が5より小さくなっています。
カイ2乗検定は妥当な検定でないと思われます。
カイ 2 乗値 1 5.4832 0.0192
尤度比カイ 2 乗値 1 3.7434 0.0530
連続性補正カイ 2 乗値 1 3.1716 0.0749
Mantel-Haenszel のカイ 2 乗値 1 5.4723 0.0193
ファイ係数   0.1047  
一致係数   0.1042  
Cramer の V 統計量   0.1047  
Fisher の正確検定
セル (1,1) 度数 (F) 438
左側 Pr <= F 0.9925
右側 Pr >= F 0.0519
   
表の確率 (P) 0.0444
両側 Pr <= P 0.0519

標本サイズ = 500


prevhyp vs prevchd

FREQ プロシジャ

度数
パーセント
行のパーセント
列のパーセント
表 : PREVHYP * PREVCHD
PREVHYP(Prevalent Hypertension) PREVCHD(Prevalent CHD (MI,AP,CI))
No Yes 合計
No
191
38.20
93.63
43.12
13
2.60
6.37
22.81
204
40.80
 
 
Yes
252
50.40
85.14
56.88
44
8.80
14.86
77.19
296
59.20
 
 
合計
443
88.60
57
11.40
500
100.00

PREVHYP * PREVCHD の統計量

統計量 自由度 p 値
カイ 2 乗値 1 8.6231 0.0033
尤度比カイ 2 乗値 1 9.2098 0.0024
連続性補正カイ 2 乗値 1 7.8028 0.0052
Mantel-Haenszel のカイ 2 乗値 1 8.6059 0.0034
ファイ係数   0.1313  
一致係数   0.1302  
Cramer の V 統計量   0.1313  
Fisher の正確検定
セル (1,1) 度数 (F) 191
左側 Pr <= F 0.9993
右側 Pr >= F 0.0021
   
表の確率 (P) 0.0013
両側 Pr <= P 0.0039

標本サイズ = 500

コードはすべて同じですが、行の変数のみがPROC FREQの呼び出しごとに変更されています。このコードを何度も書く代わりに、%DOループを使用できます。出力を短くするため、最初の2つのPROC FREQの呼び出しだけを使用します。

%macro twobytwov2(dataset, predictor, response);
  %do i=1 %to %sysfunc(countw(&predictor., ' '));
    %let dep = %scan(&predictor., &i.);
    title "&dep. vs &response.";
    proc freq data = &dataset.;
      table &dep. * &response. / chisq;
    run;
    title;
  %end;
%mend;

*%twobytwov2(fghmtemp, prevap diabetes prevmi prevstrk prevhyp, prevchd);
%twobytwov2(fghmtemp, prevap diabetes, prevchd);
SAS 出力

prevap vs prevchd

FREQ プロシジャ

度数
パーセント
行のパーセント
列のパーセント
表 : PREVAP * PREVCHD
PREVAP(Prevalent Angina) PREVCHD(Prevalent CHD (MI,AP,CI))
No Yes 合計
No
443
88.60
96.30
100.00
17
3.40
3.70
29.82
460
92.00
 
 
Yes
0
0.00
0.00
0.00
40
8.00
100.00
70.18
40
8.00
 
 
合計
443
88.60
57
11.40
500
100.00

PREVAP * PREVCHD の統計量

統計量 自由度 p 値
WARNING: セルの25%において、期待度数が5より小さくなっています。
カイ2乗検定は妥当な検定でないと思われます。
カイ 2 乗値 1 337.9100 <.0001
尤度比カイ 2 乗値 1 209.3011 <.0001
連続性補正カイ 2 乗値 1 328.4425 <.0001
Mantel-Haenszel のカイ 2 乗値 1 337.2342 <.0001
ファイ係数   0.8221  
一致係数   0.6350  
Cramer の V 統計量   0.8221  
Fisher の正確検定
セル (1,1) 度数 (F) 443
左側 Pr <= F 1.0000
右側 Pr >= F <.0001
   
表の確率 (P) <.0001
両側 Pr <= P <.0001

標本サイズ = 500


diabetes vs prevchd

FREQ プロシジャ

度数
パーセント
行のパーセント
列のパーセント
表 : DIABETES * PREVCHD
DIABETES(Diabetic Y/N) PREVCHD(Prevalent CHD (MI,AP,CI))
No Yes 合計
No
408
81.60
89.87
92.10
46
9.20
10.13
80.70
454
90.80
 
 
Yes
35
7.00
76.09
7.90
11
2.20
23.91
19.30
46
9.20
 
 
合計
443
88.60
57
11.40
500
100.00

DIABETES * PREVCHD の統計量

統計量 自由度 p 値
カイ 2 乗値 1 7.8534 0.0051
尤度比カイ 2 乗値 1 6.3873 0.0115
連続性補正カイ 2 乗値 1 6.5483 0.0105
Mantel-Haenszel のカイ 2 乗値 1 7.8377 0.0051
ファイ係数   0.1253  
一致係数   0.1244  
Cramer の V 統計量   0.1253  
Fisher の正確検定
セル (1,1) 度数 (F) 408
左側 Pr <= F 0.9972
右側 Pr >= F 0.0090
   
表の確率 (P) 0.0062
両側 Pr <= P 0.0119

標本サイズ = 500

この マクロでは、代わりに行の変数のリストをスペース区切りでパラメータ「predictor」に渡します。前回学んだCOUNTWとSCAN関数は今回もデータステップの外で使用する必要があるため、%SYSFUNC マクロ関数を使ってこれらの関数を評価する必要があります。COUNTWはリスト内の単語数(この場合はスペースで区切られている)を数えることを思い出してください。SASには既にSCAN のマクロバージョンである%SCANがあり、これを使って行変数のリストを処理できます。残りのコードは前と同じですが、%DOループを使って「predictor」内の行変数のリストを処理する必要があります。

マクロの条件付きロジック#

マクロとマクロ変数を使用すると、非常に柔軟性が高まります。さらにその柔軟性を高めるには、%IFなどの条件付きマクロステートメントを使用します。マクロでの条件付きロジックに使用されるステートメントの一般的な形式は次のとおりです。

%IF condition %THEN action;
   %ELSE %IF condition %THEN action;
   %ELSE action;
   
%IF condition %THEN %DO;
   action;
%END;

これらのステートメントは、標準のSASコードにも同様のものがあるので見覚えがあるかもしれません。しかし、これらと標準のものを混同しないでください。DOループと同様に、通常のIF/THEN/ELSEステートメントはデータステップ内でしか使用できません。%IF/%THEN/%ELSEステートメントは、データステップやPROCステップの外で、他のマクロの中でも使用できます。

#

次のプログラムでは、マクロtwobytwoに「Yes」または「No」の値を取るパラメータを作成することで、PROC FREQからリスク差の出力を要求するかどうかを指定できるようにしています。

%macro twobytwov2(dataset, predictor, response, rd=no);
  %do i=1 %to %sysfunc(countw(&predictor., ' '));
    %let dep = %scan(&predictor., &i.);
    %if &rd. = no %then %do;
      title "&dep. vs &response.";
      proc freq data = &dataset.;    
        table &dep. * &response. / chisq;
      run;
      title;
    %end;
    %else %if &rd. = yes %then %do;
      title "&dep. vs &response.";
      proc freq data = &dataset.;    
        table &dep. * &response. / chisq riskdiff;
      run;
      title;
    %end;
  %end;
%mend;

%twobytwov2(dataset=fghmtemp, predictor=diabetes, response=prevchd, rd=yes);
*%twobytwov2(dataset=fghmtemp, predictor=diabetes, response=prevchd);
SAS 出力

diabetes vs prevchd

FREQ プロシジャ

度数
パーセント
行のパーセント
列のパーセント
表 : DIABETES * PREVCHD
DIABETES(Diabetic Y/N) PREVCHD(Prevalent CHD (MI,AP,CI))
No Yes 合計
No
408
81.60
89.87
92.10
46
9.20
10.13
80.70
454
90.80
 
 
Yes
35
7.00
76.09
7.90
11
2.20
23.91
19.30
46
9.20
 
 
合計
443
88.60
57
11.40
500
100.00

DIABETES * PREVCHD の統計量

統計量 自由度 p 値
カイ 2 乗値 1 7.8534 0.0051
尤度比カイ 2 乗値 1 6.3873 0.0115
連続性補正カイ 2 乗値 1 6.5483 0.0105
Mantel-Haenszel のカイ 2 乗値 1 7.8377 0.0051
ファイ係数   0.1253  
一致係数   0.1244  
Cramer の V 統計量   0.1253  
Fisher の正確検定
セル (1,1) 度数 (F) 408
左側 Pr <= F 0.9972
右側 Pr >= F 0.0090
   
表の確率 (P) 0.0062
両側 Pr <= P 0.0119
列 1 リスクの推定値
  リスク ASE 95%
信頼限界
正確 95%
信頼限界
行 1 - 行 2 の差
行 1 0.8987 0.0142 0.8709 0.9264 0.8672 0.9249
行 2 0.7609 0.0629 0.6376 0.8841 0.6123 0.8741
合計 0.8860 0.0142 0.8581 0.9139 0.8548 0.9125
0.1378 0.0645 0.0115 0.2642    
列 2 リスクの推定値
  リスク ASE 95%
信頼限界
正確 95%
信頼限界
行 1 - 行 2 の差
行 1 0.1013 0.0142 0.0736 0.1291 0.0751 0.1328
行 2 0.2391 0.0629 0.1159 0.3624 0.1259 0.3877
合計 0.1140 0.0142 0.0861 0.1419 0.0875 0.1452
-0.1378 0.0645 -0.2642 -0.0115    

標本サイズ = 500

rdパラメータは「Yes」か「No」を受け取り、デフォルト値は「No」です。「No」を指定した場合(または空白のままにした場合)は、カイ2乗の出力のみが得られます。rdに「Yes」を指定すると、カイ2乗の出力とリスク差の出力の両方が得られます。 マクロ定義でrdのデフォルト値を指定していることに注目してください。デフォルト値を指定すると、位置パラメータを作成することになり、この場合、マクロを呼び出す際にはすべてのパラメータをパラメータ名=パラメータ値の形式で指定する必要があります。

データ駆動プログラム - CALL SYMPUT#

CALL SYMPUTマクロルーチンを使用すると、マクロプログラムがデータを見て、自身で何をすべきかを決定できます。CALL Symputはデータステップから値を取り、その値をマクロ変数に割り当てます。あとでそのマクロ変数をプログラムで使用できます。

CALL SYMPUTには多くの形式がありますが、単一の値を単一のマクロ変数に割り当てる場合は、次の一般的な形式でCALL SYMPUTを使用します。

CALL SYMPUTX("macro-variable", value);

実際に使用するCALLルーチンとしては、CALL SYMPUTではなくCALL SYMPUTXをおすすめします。
CALL SYMPUTで必要になる第二引数を数値から文字値に変換する、先頭や末尾の余分なブランクを除く処理がCALL SYMPUTXでは自動で行われます。
ここで、macro-variableは新しいまたは既存のマクロ変数の名前で、引用符で囲まれています。valueはデータステップ内の変数名で、その現在値をそのマクロ変数に割り当てる値です。

CALL SYMPUTはしばしばIF/THEN/ELSEステートメントで使用されます。例えば

IF Place = 1 THEN
   CALL SYMPUTX("WinningTime", Time);

このステートメントは、「Place」の値が1のときに、マクロ変数「WinningTime」を作成し、その値を変数「Time」の現在値に設定するようにしています。

CALL SYMPUTでマクロ変数を作成しても、同じデータステップ内でその変数を使用することができないことには注意が必要です。その理由は次のとおりです。マクロコードを実行すると、最初にマクロプロセッサによって解決され、次にコンパイルされ、最後に実行されます。実行段階でようやくSASがデータを認識します。CALL SYMPUTは実行段階でデータ値を取得し、後で使用するためにマクロプロセッサにその値を渡します。そのため、CALL SYMPUTをデータステップに配置する必要がありますが、その後のステップでしか使用できません。

#

次のプログラムでは、PROC FREQによって計算された期待度数を使用して、カイ2乗検定のp値とフィッシャーの正確確率検定のp値のどちらを返すかを判断しています。カイ2乗検定を使用するための一般的な規則の1つは、すべての期待度数が5以上であることです。
次のコードは予備的なものであり、マクロを定義する方法を見つけるために実行しています。

/*First, we will need to write and test our code for selecting only the output we want before
  we make our updated macro. We want to run a continuity adjusted chi-square test or 
  Fisher's exact test if the expected counts are too small. We only want the table with row percentages
  and the result of the correct test in our output.*/

*ODS TRACE ON;
proc freq data = fghmtemp;
  table prevap*prevchd / expected chisq;
  ods output CrossTabFreqs = ct ChiSq = chi FishersExact = fet;
run;
*ODS TRACE OFF;

*Lot's of missing data. We only want to use non missing values;
proc print data = ct;
run;

/* Test how to clean the resulting datasets */
/* Remove the missing values and get the cells with epxected countes less than 5 */
title '/* Test how to clean the resulting datasets */';
proc print data=ct;
  where expected < 5 and prevap ^= . and prevchd ne .;
run;
title;

*Create an indicator variable that is 1 if there is at least one cell with expected count less than 5;
%let low_count = 0;
data _null_; *Run through the data set without creating a new one;
  set ct;
  if expected < 5 and prevap ^= . and prevchd ^= . then call symput('low_count', 1);
RUN;

%put &=low_count; *Should show value of 1 in the log file.;

/* Now, that we can check to see if the expected cell counts are less than 5,
 * we need to see how we can clean the chi-square or Fisher tables to print only 
 * the test name and p-value.
 */

proc print data=chi;
run;

*Select the row for the continuity adjusted chi-square test;
data chi2;
  set chi;
  where statistic="Continuity Adj. Chi-Square";
  drop TABLE DF VALUE;
run;

proc print data = chi2;
run;

*Do the same thing for Fisher's exact test;
proc print data = fet;
run;

data fet2 (rename = (cValue1 = Prob));
  set fet;
  Statistic = "Fisher's Exact Test";
  where NAME1 = "XP2_FISH";
  keep statistic cValue1;
run;

data fet2;
  retain Statistic Prob;
  set fet2;
run;

proc print data = fet2;
run;
SAS 出力

FREQ プロシジャ

度数
期待
パーセント
行のパーセント
列のパーセント
表 : PREVAP * PREVCHD
PREVAP(Prevalent Angina) PREVCHD(Prevalent CHD (MI,AP,CI))
No Yes 合計
No
443
407.56
88.60
96.30
100.00
17
52.44
3.40
3.70
29.82
460
 
92.00
 
 
Yes
0
35.44
0.00
0.00
0.00
40
4.56
8.00
100.00
70.18
40
 
8.00
 
 
合計
443
88.60
57
11.40
500
100.00

PREVAP * PREVCHD の統計量

統計量 自由度 p 値
WARNING: セルの25%において、期待度数が5より小さくなっています。
カイ2乗検定は妥当な検定でないと思われます。
カイ 2 乗値 1 337.9100 <.0001
尤度比カイ 2 乗値 1 209.3011 <.0001
連続性補正カイ 2 乗値 1 328.4425 <.0001
Mantel-Haenszel のカイ 2 乗値 1 337.2342 <.0001
ファイ係数   0.8221  
一致係数   0.6350  
Cramer の V 統計量   0.8221  
Fisher の正確検定
セル (1,1) 度数 (F) 443
左側 Pr <= F 1.0000
右側 Pr >= F <.0001
   
表の確率 (P) <.0001
両側 Pr <= P <.0001

標本サイズ = 500


OBS Table PREVAP PREVCHD _TYPE_ _TABLE_ Frequency Expected Percent RowPercent ColPercent Missing
1 表 : PREVAP * PREVCHD No No 11 1 443 407.56 88.60 96.30 100.00 .
2 表 : PREVAP * PREVCHD No Yes 11 1 17 52.44 3.40 3.70 29.82 .
3 表 : PREVAP * PREVCHD No . 10 1 460 . 92.00 . . .
4 表 : PREVAP * PREVCHD Yes No 11 1 0 35.44 0.00 0.00 0.00 .
5 表 : PREVAP * PREVCHD Yes Yes 11 1 40 4.56 8.00 100.00 70.18 .
6 表 : PREVAP * PREVCHD Yes . 10 1 40 . 8.00 . . .
7 表 : PREVAP * PREVCHD . No 01 1 443 . 88.60 . . .
8 表 : PREVAP * PREVCHD . Yes 01 1 57 . 11.40 . . .
9 表 : PREVAP * PREVCHD . . 00 1 500 . 100.00 . . 0

/* Test how to clean the resulting datasets */

OBS Table PREVAP PREVCHD _TYPE_ _TABLE_ Frequency Expected Percent RowPercent ColPercent Missing
5 表 : PREVAP * PREVCHD Yes Yes 11 1 40 4.56 8.00 100.00 70.18 .

OBS Table Statistic DF Value Prob
1 表 : PREVAP * PREVCHD カイ 2 乗値 1 337.9100 <.0001
2 表 : PREVAP * PREVCHD 尤度比カイ 2 乗値 1 209.3011 <.0001
3 表 : PREVAP * PREVCHD 連続性補正カイ 2 乗値 1 328.4425 <.0001
4 表 : PREVAP * PREVCHD Mantel-Haenszel のカイ 2 乗値 1 337.2342 <.0001
5 表 : PREVAP * PREVCHD ファイ係数 _ 0.8221 _
6 表 : PREVAP * PREVCHD 一致係数 _ 0.6350 _
7 表 : PREVAP * PREVCHD Cramer の V 統計量 _ 0.8221 _

OBS Table Name1 Label1 cValue1 nValue1
1 表 : PREVAP * PREVCHD Cell1_FREQ セル (1,1) 度数 (F) 443 443.000000
2 表 : PREVAP * PREVCHD XPL_FISH 左側 Pr <= F 1.0000 1.000000
3 表 : PREVAP * PREVCHD XPR_FISH 右側 Pr >= F <.0001 6.222358E-46
4 表 : PREVAP * PREVCHD       .
5 表 : PREVAP * PREVCHD P_TABLE 表の確率 (P) <.0001 6.125565E-43
6 表 : PREVAP * PREVCHD XP2_FISH 両側 Pr <= P <.0001 6.222358E-46

OBS Statistic Prob
1 Fisher's Exact Test <.0001

前のコードは探索的なものであり、ODSテーブル名を確認し、必要なデータをこれらのテーブルから抽出する方法を確認するためのものです。次のコードが最終的な動作するマクロです。

%macro twobytwov3(dataset, predictor, response);
  %let n = %sysfun(countw(&predictor.));
  %let i = 1;
  %let dep = %scan(&predictor., &i.);
  %do %while(&dep. ^=);
    title;
    ods select none;
    ods output crosstabfreqs = ct ChiSq = chi FishersExact = fet;
    proc freq data = &dataset.;        
      table &dep.*&response. / chisq expected;
    run;

    title "&dep. vs &response.";
    ods select CrossTabFreqs;
    proc freq data = &dataset.;  
      table &dep.*&response. / nocol nopercent;
    run;
    title;

    %let low_count = 0;
    data _null_; *Run through the data set without creating a new one;
      set ct;
      if expected < 5 and &dep. ^= . and &response. ^= . then call symputx('low_count', 1);
    run;

    %if &low_count. = 0 %then %do;
      data chi;
        set chi;
        where statistic="Continuity Adj. Chi-Square";
        drop TABLE DF VALUE;
      run;
  
      proc print data = chi;
      run;
    %end;
    %else %do;
      data fet (rename = (cValue1 = Prob));
        set fet;
        Statistic = "Fisher's Exact Test";
        where NAME1 = "XP2_FISH";
        keep statistic cValue1;
      run;
  
      data fet;
        retain Statistic Prob;
        set fet;
      run;
  
      proc print data = fet;
      run;
    %end;
        
    %let i = %eval(&i. + 1);
    %let dep = %scan(&predictor., &i);
  %end;
%mend;

%twobytwov3(fghmtemp, prevap diabetes prevmi prevstrk prevhyp, prevchd);
SAS 出力

prevap vs prevchd

FREQ プロシジャ

度数
行のパーセント
表 : PREVAP * PREVCHD
PREVAP(Prevalent Angina) PREVCHD(Prevalent CHD (MI,AP,CI))
No Yes 合計
No
443
96.30
17
3.70
460
 
Yes
0
0.00
40
100.00
40
 
合計
443
57
500

OBS Statistic Prob
1 Fisher's Exact Test <.0001

diabetes vs prevchd

FREQ プロシジャ

度数
行のパーセント
表 : DIABETES * PREVCHD
DIABETES(Diabetic Y/N) PREVCHD(Prevalent CHD (MI,AP,CI))
No Yes 合計
No
408
89.87
46
10.13
454
 
Yes
35
76.09
11
23.91
46
 
合計
443
57
500

prevmi vs prevchd

FREQ プロシジャ

度数
行のパーセント
表 : PREVMI * PREVCHD
PREVMI(Prevalent MI (Hosp,Silent)) PREVCHD(Prevalent CHD (MI,AP,CI))
No Yes 合計
No
443
94.46
26
5.54
469
 
Yes
0
0.00
31
100.00
31
 
合計
443
57
500

OBS Statistic Prob
1 Fisher's Exact Test <.0001

prevstrk vs prevchd

FREQ プロシジャ

度数
行のパーセント
表 : PREVSTRK * PREVCHD
PREVSTRK(Prevalent Stroke (Infarct,Hem)) PREVCHD(Prevalent CHD (MI,AP,CI))
No Yes 合計
No
438
89.02
54
10.98
492
 
Yes
5
62.50
3
37.50
8
 
合計
443
57
500

OBS Statistic Prob
1 Fisher's Exact Test 0.0519

prevhyp vs prevchd

FREQ プロシジャ

度数
行のパーセント
表 : PREVHYP * PREVCHD
PREVHYP(Prevalent Hypertension) PREVCHD(Prevalent CHD (MI,AP,CI))
No Yes 合計
No
191
93.63
13
6.37
204
 
Yes
252
85.14
44
14.86
296
 
合計
443
57
500

このプログラムはPROC FREQを使用して期待度数を計算し、ODS OUTPUTを使用してこのテーブルを抽出します。いずれかのセルの期待度数が5未満の場合、CALL SYMPUTはマクロ変数lowcountの値を1に設定します。それ以外の場合は初期値0のままです。このマクロ変数は、%IFステートメントでカイ2乗検定のp値かフィッシャーの正確確率検定のp値を保存して出力するかを判断するのに使用されます。

演習#

  1. 量的変数の値を以下のように4つのカテゴリーに分割するマクロを書いてください。:

    • X < Q1の場合、group = 1

    • Q1 < X < Medianの場合、group = 2

    • Median < X < Q3の場合、group = 3

    • Q3 < Xの場合、group = 4

このマクロの定義は次のようになります。%quartilesmacro(mydata, qvar, round, out);

  • mydata: 量的変数を含むデータセット

  • qvar: 量的変数の名前

  • round: 小数点以下の桁数

  • out: 出力データセット名。この中にカテゴリ化された変数「qvar_cat」が含まれます。例えば、「qvar」が”bmi”の場合、出力変数は「bmi_cat」になります。