ラベル means の投稿を表示しています。 すべての投稿を表示
ラベル means の投稿を表示しています。 すべての投稿を表示

means summaryプロシジャのclassステートメント指定変数の欠損値には気を付けてって話

年末、インフルにやられてダウンしてました。
締めの記事としてはたいしたことない内容ですが、means summaryのclass変数に欠損値があるのをうっかりすると、危険だという話です。

data Q1;
CL1=1;CL2=1;X=1;output;
CL1=1;CL2=1;X=1;output;
CL1=1;CL2=.;X=1;output;
CL1=2;CL2=2;X=1;output;
CL1=2;CL2=2;X=1;output;
CL1=2;CL2=.;X=1;output;
run;











というデータセットがあるとします。

まずCL1のみをclass変数に指定してみます。

proc summary data=Q1;
 types CL1;
 class CL1;
 var X;
 output out=A1 n=N;
run;






CL1が1,2の場合ともにN=3と正しい結果になっています。

続いて、CL2もclass変数に加えたうえで、
typesでCL1 CL2それぞれ単品でクラスした場合の結果のみだします

proc summary data=Q1;
 types CL1 CL2;
 class CL1 CL2;
 var X;
 output out=A2 n=N;
run;









はい、CL2の結果については1,2共にN=2で問題ないのですが
CL1の方について,あれ!!N=2で結果がさっきと違う!!
これなんですよね。複数のclass変数を指定して、あるクラスのみを見た時の結果と
そのclass変数単体で指定した場合の結果が異なるという現象です。

これは、classステートメントに複数変数を指定した場合、いずれか一つ以上の変数にnullが存在する場合、そのオブザベーションが全ての集計対象から外れるという仕様によるためです。

これは知らないと結構、恐ろしい仕様です。

nullが入っていても集計から外さないためにはnull自体を値とみなしてクラス変数として成立させる
という方法があります。
具体的にはclassステートメントにmissingオプションをつけるわけです。

proc summary data=Q1;
 types CL1 CL2;
 class CL1 CL2 / missing;
 var X;
 output out=A3 n=N;
run;

すると










こうなるわけです。1obs目がCL2の欠損値がひとつのクラスとして集計されている部分です。

しかし、これはこれで微妙に危険なところがあります。何故かというと、例えば
typesステートメントをつけずに全集計をデータセットに吐く場合

proc summary data=Q1;
 class CL1 CL2 / missing;
 var X;
 output out=A4 n=N;
run;



















こうなるわけですが、このデータセットの1obs目と2obs目について、CL1 CL2の値だけ
見ると両方null nullで見分けがつきませんよね。
_TYPE_の値が違うことから別層での集計であることがわかります。
1obs目はクラスなしの全集計、2obs目はCL2のnullクラスでの集計です。
例えばこのようなデータセットか、CL1 CL2の値で抽出して臨むべき集計結果を取得しようとする
場合、別層のものも同じ条件でかかってしまう恐れがあるわけです。

結局のところ、クラス変数にnullが入る場合で
missing使う場合は、結果をきちんと想像できるようにする。
それが不安な場合は
①class変数を単体で指定して、何回も回す
②欠損値を99などのダミー値にして回す
の方がまだいいかもですね。

また、たまにN数を_FREQ_からとっているプログラムをみますが、
もしN=と_FREQ_が常に同値と思ってやっているなら、それはヤバいです。

data Q2;
CL1=1;CL2=1;X=.;output;
CL1=1;CL2=1;X=.;output;
CL1=1;CL2=.;X=.;output;
CL1=2;CL2=2;X=.;output;
CL1=2;CL2=2;X=.;output;
CL1=2;CL2=.;X=.;output;
run;

proc summary data=Q2;
 types CL1 CL2;
 class CL1 CL2 / missing;
 var X;
 output out=A5 n=N;
run;










_FREQ_は純粋に読み込まれたオブザベーション数で、N=はそのうち、
varで指定された変数の値が非欠損値であるものの数です。

以上。今年も有難うございました!!

今年は無料のSAS Universityがリリースされ、アクセス履歴をみるとacドメイン(大学)が少し増えてきていて、Rが猛威を振るう中、少しでも新規の若いSASユーザーが増えるというのは、僕にとっては幸せを感じるできごとです(Rもできた方が絶対いいと思うけど)。

そういう新ユーザーが、SAS面白いって思えるような何かを提供するのが先達の務めだとも少しは思うので(一部のデータステップマニア向けで、あんまりそういう人向けのブログではないのですが…)、来年もまあ、頑張りたいと思います。

SASユーザー躍進の一年になりますように!!

means(summary)プロシジャのclassステートメントに関連してtypes ways nwayの話

たまに質問されるのが、meansやsummaryプロシジャで

ERROR: 適切な処理量を超えるクラス変数の組み合せの種類が
       要求されました。 TYPES または WAYS ステートメント、
       あるいは NWAY オプションを使用して、
       組み合せの種類の生成を制限してください。

ってメッセージがでるんですが、どうしたらいいですか?って感じの質問です。
どうもclassステートメントととbyステートメントの違いがあんまりピンときていない感じで、
とりあえず層別化する変数を全部つっこんだらこうなったって感じが多いです。

実際、classステートメント、ちょっとわかりにくですよね。僕も難儀しました。

記事:MEANSプロシジャのnwayオプションとTYPESステートメントの話
http://sas-tumesas.blogspot.jp/2013/10/meansnawytypes.html

でnwayとtypesについては触れていたのですが、waysについては忘れていたので整理ついでにもう一度やりましょう。


data Q1;
call streaminit(777);
do i = 1 to 20;
 C1=rand('table',1/2);
 C2=rand('table',1/2);
 C3=rand('table',1/2);
 C4=rand('table',1/2);
 X =int(rand('uniform')*10);
 output;
end;
drop i;
run;























データはなんでもいいです。
今回はC1-C4という4つの層別化変数と、集計対象のXというデータセットです。

留意してほしいのが、層別化変数に全て値が入っている、欠損がないという点です。
何故かというとclass変数に欠損が入っていた場合について、これまた何回も質問される間違いがあるんですが、それについては次回説明します。

byでやるなら、

proc sort data=Q1;
 by C1 C2 C3 C4;
run;

として

proc summary data = Q1;
by C1 C2 C3 C4;
var X;
output out=A0 mean= / autoname;
run;

とすれば















ってな感じで、C1からC4について、元のデータに存在している全パターンで集計結果がでています。

一方 classで同じようにC1からC4を指定してまわすと

proc summary data = Q1;
class C1 C2 C3 C4;
var X;
output out=A1 mean= / autoname;
run;

結果は以下










































































はい、全部で74オブザベーションでてます。
ここで、はぁ?なんで?と思われた方はmeans summaryプロシジャでのclassステートメントの
理解ができていないということになります。

どうしてかというと

全体集計(絞りなし)-0クラスレベル
C1の値で絞って集計した結果、C2で絞って集計した結果、C3の値で絞って集計した結果、C4の値で絞って集計した結果 -1クラスレベル
そして、C1とC2の出現している値の全組み合わせで、絞った結果、C1とC3、C1とC4・・・といった2クラスレベルといったように出現しているクラスの全組み合わせで、全レベル分でるわけです。
なので、値のパターンがそれなりにある変数をいくつもclassにしているすると、集計する組み合わせパターンはあっという間に爆発的な数になって、冒頭のようにSASがギブアップメッセージをだすわけです。

そこで、まずはnway

proc summary data = Q1 nway;
class C1 C2 C3 C4;
var X;
output out=A2 mean= / autoname;
run;













class変数の全てを使ったフルクラスレベルの結果のみを使用します。

次にtypesを使うと欲しい組み合わせのみを取得できます。
例えばC1とC2の2クラスレベルでの層別結果だけが欲しいのであれば
(その場合、C3 C4をclassステートメントに指定する意味がなくなりますが…)

proc summary data = Q1;
class C1 C2 C3 C4;
types C1*C2;
var X;
output out=A3 mean= / autoname;
run;







って感じです。


或いは、C1とC2 C3 C4それぞれを組み合わせた2クラスレベルが欲しければ
proc summary data = Q1;
class C1 C2 C3 C4;
types C1*(C2 C3 C4);
var X;
output out=A4 mean= / autoname;
run;













といった感じです。


続いて、waysの場合は、欲しいクラスレベルのレベルを数字で指定します。
0クラスレベル(つまり全体集計)と、1つのクラス変数のみをそれぞれ使った1クラスレベルの結果が欲しければ

proc summary data = Q1;
class C1 C2 C3 C4;
ways 0 1;
var X;
output out=A5 mean= / autoname;
run;











となります。

あまり、いい説明ではありませんでしたが、これが冒頭のTYPES WAYS NMAYいずれかを使って結果を絞ってくれというSASのお願いに繋がるのでした。

以上

meansプロシジャのweightステートメントを使用した時の分散とか標準偏差の話

最近まで知らなくて、凄いびっくりしたのですが

data Q1;
X=1;output;
X=1;output;
X=1;output;
X=2;output;
X=2;output;
X=2;output;
X=2;output;
X=3;output;
X=3;output;
X=3;output;
run;



















proc means data=Q1 sum  mean var std;
 var X;
run;










となります。

これと同じことを

data Q2;
X=1;WGT=3;output;
X=2;WGT=4;output;
X=3;WGT=3;output; 
run;









というようにウェイト値にして

proc means data=Q2 sum  mean var std;
 var X;
 weight WGT;
run;

weightステートメントに指定すれば、当然同じ結果になると思い込んでいたのですが









うわっ、分散と標準偏差がえらいでかいな!ってびっくりです。

で、教えて貰ったのが

proc means data=Q2 vardef=wdf sum  mean var std;
 var X;
 weight WGT;
run;

です。

これで実行すると









おお、よかった。

どういうことかというと

SASのデフォルトだと、ウェイトが反映された偏差平方和をウェイトが反映されてないN-1で割って分散だしちゃんですよね。





え~、なにそれ。weightステートメント使ってたらデフォルトをwgtに切り替えるぐらいしてよ。メッセージも無しに、そんな使い道のない謎値だされてもって思いました。

医薬品開発の業界にいたときは、freqで何か検定とかするときにweightステートメント使いましたが、あんまり要約統計量出す時に使った記憶がないです。
最近ウェイトバック集計とかする機会が増えて、そこで教わった次第です。


means(summary)プロシジャのautonameオプションの話

今、以下のデータセットがあって

data Q1;
X=1;Y=2;Z=3;output;
X=2;Y=4;Z=3;output;
X=3;Y=2;Z=6;output;
run;









で、

proc means data=Q1 noprint;
 var X Y Z;
 output out=OUT(drop=_TYPE_ _FREQ_) mean=;
run;

とすると、データセットOUTの中身は





となります。

要約統計量=の後、何も指定しなければ、元の集計対象の変数名を上書きして
結果が格納されます。

出したい要約統計量が1つならそれでいいのですが

proc means data=Q1 noprint;
 var X Y Z;
 output out=OUT(drop=_TYPE_ _FREQ_) mean= sum=;
run;

のように2つ指定しても、格納する変数がないので、




上記のようなWarningがでて(University Editionのメッセージは全部英語なんです、、。)
sum=の方は無視されます。


なので

proc means data=Q1 noprint;
 var X Y Z;
 output out=OUT_1(drop=_TYPE_ _FREQ_) 
            mean=MEAN_X MEAN_Y MEAN_Z
            sum=SUM_X SUM_Y SUM_Z;
run;

のように集計結果を格納したい新規変数名を、順番に記述してやれば



となります。


が、はっきり言って、1つ1つ指定するのは、変数が多いと大変面倒なので

proc means data=Q1 noprint;
 var X Y Z;
 output out=OUT_2(drop=_TYPE_ _FREQ_)
        mean= sum=/autoname;
run;

こうしてやります。

すると







「変数名_要約統計量名」のルールに従って、自動的に変数が作成されるのです。
まあ便利!


ちなみに、例えば、XとYの平均、Zの合計だけを出したいとかって感じで、選択したい場合は

proc means data=Q1 noprint;
 var X Y Z;
 output out=OUT_3(drop=_TYPE_ _FREQ_)
            mean(X Y)=
            sum(Z)= /autoname;
run;






OKです。

森下卓九段が今期のNHK杯で敗退してしまったので、最近テンション低めです

縦に欠測値のカウントの話

ひとつ前の記事に書きましたが、SAS社様の公式TwitterやFacebookでこのブログを紹介していただきましたので、ちょっとお行儀よく、久しぶりに基本的かつ真面目な話題をします。

各変数ごとに欠測の値が何オブザベーションあるかを1ステップでカウントしたいとします。

要は

data Q1;
X=1;Y='';Z=.;output;
X=2;Y='A';Z=.;output;
X=3;Y='B';Z=.;output;
run;









上記みたいなデータセットから





を作りたいということです。


さて、ぱっと思い浮かびました?意外と難しかったりしませんか?

proc meansが思いついた方もいたと思いますが

proc means data=Q1 noprint;
 var X Z;
 output out=A0(drop=_TYPE_ _FREQ_) nmiss=XMISS ZMISS;
run; 

のように数値変数X Zに対してはnmiss=で欠損値カウントが可能ですが、文字値のYはvarに指定した時点でエラーになってしまいます。

freqが思いついた方もいるかもしれませんが、1ステップで、上記の形のデータセットを作るのは難しいはずです、多分。

基本、データを縦に考える時はSQL使えっていうのは僕の中の原則なので、

proc sql noprint;
 create table A1 as
   select sum(missing(X)) as XMISS
          ,sum(missing(Y)) as YMISS
          ,sum(missing(Z)) as ZMISS
       from Q1;
quit;

は、正解です。上記の回答データセットの結果になります。
missingは欠損値の場合1、非欠損値の場合0なので、それをsumで合計すればよいわけです。
わざわざcase文書かなくても、SASのmissing関数をSQLにぶち込めちゃうところがグレートですね!

次にこれぞSASのデータステップ!!って感じで書くなら

data A2;
 set Q1 end=eof;
 retain XMISS YMISS ZMISS 0;
  if missing(X) then XMISS+1;
  if missing(Y) then YMISS+1;
  if missing(Z) then ZMISS+1;
  if eof;
 drop X Y Z;
run;

で、結果は同じです。1行ずつ順番に読んでいくSASの正道って感じですね。

やっぱ上記のようなステップ書いてる時は、なんか居飛車、それも矢倉とか指してる感覚に似てますよね。逆にSQL書いてる時は、角交換系の振り飛車の間隔ですね。ハッシュオブジェクトもそれに近いけど、もしかしたら横歩取りの感覚かも。
僕はもちろん振り飛車党です。
結局脱線してるっていう




フォーマットを使って集計_マルチラベルフォーマットで連続量を任意の範囲で区切って集計

カテゴリ集計のやり方は様々ですが、連続量を任意の範囲で区切って集計する場合
フォーマットを使うと結構便利です。MULTILABEL FORMATを使えば、値の区間が重複している場合も別途に集計できて、気持ちいいです。

今、以下のように

data Q1;
call streaminit(2014131);
 do i=1 to 1000;
  X=rand('uniform');
  output;
 end;
drop i;
run;






















0から1の間に分布する1000個の値について

 0.2未満
 0.2以上0.6未満
 0.6以上
 0.5未満
 0.5以上

のカテゴリで区切って数を数えてと言われた場合

if X<0.2 then CATE=1; などなどデータステップで集計変数を定義してからプロシジャにかけてもいいですが、面倒です。

しかも区切る範囲がかぶっている(0.1という値は0.2未満で集計され、かつ0.5未満でも集計されなければならない)ので変数を分ける必要があります。

そんな時なら、例えば

proc format; 
value FREQF (multilabel) 
 low-<0.2='0.2未満'
 0.2-<0.6='0.2以上0.6未満'
 0.6-high='0.6以上'
 low-<0.5='0.5未満'
 0.5-high='0.5以上';
quit; 

のように区切る範囲をフォーマットで定義します。 (multilabel) を打てば値が重複していても
定義できます。

その上で

proc means data=Q1 MIN MAX ; 
class X /mlf
var X;
format X FREQF.
run; 

としてやれば













のように一気に集計できます。最小最大値は確認用にだしているだけです。

formatで指定することと、classステートメントに/mlfをつけることを忘れずに。



全てがnullであった時を問題とするブランクチェックについて、データ構造が縦型と横型であった場合の違い

生データをチェック・クリーニングする際に、ブランクチェックは頻度の高い処理です。

たとえば今A,B,Cという変数があって、どれか1つ以上でも値が入っていれば、問題なし。
全てが欠損値であるデータをピックアップしたいという状況があったとします。

例えばデータが

data Q1;
IDNO='001';A=.;B=.;C=10;output;
IDNO='002';A=.;B=.;C=.;output;
IDNO='003';A=5;B=.;C=.;output;
run;







こんな感じならwhereで単純に=.をandでつないでもいいし

data A1;
 set Q1;
 if coalesce(A,B,C)=.;
run;

こんな感じでもいいし、簡単です。





少し頭を使うのは、同じ意味であってもデータ構造が

data Q2;
IDNO='001';ITEM='A';VAL=.;output;
IDNO='001';ITEM='B';VAL=.;output;
IDNO='001';ITEM='C';VAL=10;output;
IDNO='002';ITEM='A';VAL=.;output;
IDNO='002';ITEM='B';VAL=.;output;
IDNO='002';ITEM='C';VAL=.;output;
IDNO='003';ITEM='A';VAL=5;output;
IDNO='003';ITEM='B';VAL=.;output;
IDNO='003';ITEM='C';VAL=.;output;
run;











こういう場合です。
1つのIDにつき項目ごとにオブザベーションが別れています。
問題にすべきはA,B,C全てが欠損である002のIDで、それを特定したいわけですが、
さて、どう書きましょうか?

まず1つとしては

proc transpose data=Q2 out=Q2_(drop=_NAME_);
 var VAL;
 by IDNO;
 id ITEM;
run;

とすれば、最初の問題のデータ構造に転置されるので、そうしてから
同じやり方で片付ける方法です。

縦に解く問題が難しければ、横にすればいいし
横に解く問題が難しければ、縦にすればいいというのはSASの定跡ですね。

まあ、でもこの場合、わざわざデータ構造を変えなくても
例えば、

proc means data=Q2 nway noprint;
class IDNO;
var VAL;
output out=A2(where=(S=.)) sum=S;
run;





このようにすれば、IDごとにAからCの合計をだしてくれるわけですが
sumによる加算結果がnullになるのはどんな時でしょうか?
それは対象全てがnullであったときのみです。その他の場合はnullを除いて足し算してくれます。
すなわちsumがnullであればA,B.Cすべてnullであることに他ならないので
上記のコードでID 002を特定できるわけです。
maxとかでももちろんいいです。


他のアプローチとして、

proc sort data=Q2;
 by IDNO VAL;
run;

data A3;
 set Q2;
 by IDNO VAL;
 if last.IDNO and VAL=.;
run;





もアリですね。

ID順、VAL順にソートして、各IDの最後のVALがnullである場合は、そこまでの全てのVALがnullであることと同義ですからね。









水準がデータで揃わずスカスカの集計表に立ち向かう_meansやsummaryのclassdata=とtransposeのid 複数変数を利用して

以前、meansまたはsummaryプロシジャのclassdata=オプションは集計表を作成するうえで役立つと言い詳細はまた紹介すると書きました。
(ちなみにmeansとsummaryの違いは、デフォルトでアウトプット出力するかどうかです。つまりmeansにnoprintをつければsummaryと同じで、逆にsummaryにprintをつければmeansと同じです。)

またtransposeプロシジャのIDステートメント複数づけのおかげで、集計表が作りやすくなったとも言い、実例をいつか紹介すると書きました。

遅くなりましたが、ざっくりとした流れを紹介したいと思います。

今、以下のようなデータセットがあるとします

data Q_1;
GROUP='A';SUBGROUP='X';LEVEL1=2;LEVEL2=1;LEVEL3=5;output;
GROUP='A';SUBGROUP='Z';LEVEL1=1;LEVEL2=0;LEVEL3=4;output;
GROUP='B';SUBGROUP='Z';LEVEL1=1;LEVEL2=0;LEVEL3=3;output;
run;






で、GROUPはA群とB群がいて
それぞれのグループにSUBGROUP X、Y、 Zがあるとします。
そして、3つの変数があり
LEVEL1は1-2の値をとります。
LEVEL2は0-1の値をとります。
LEVEL3は3-5の値をとります。

ところが、今まだデータが集まっていない、またはデータの収集が打ち切られた等の
理由で、たった3オブザベーションしかありません。

このわずかのデータで
















のようなEXCELの集計表テンプレートに出力する必要があるとします。
つまり、大半のセルは0になります。

どんな言語でも、あるデータをだすのは簡単ですが、ないデータをだすプログラムは難しいものです。とりあえず先にコード全部のせます。

data CLDS;
 do GROUP='A','B';
  do SUBGROUP='X','Y','Z';
   do DUMMY=0 to 20;
    output;
   end;
  end;
 end;
run;

/*================================================
マクロ名:syogi
引数-①dsname 対象データセット
   ②varname 対象変数
   ③minval 対象変数のとりうる最小のカテゴリ数値
   ④maxval 対象変数のとりうる最大のカテゴリ数値
=================================================*/
%macro syougi(dsname,varname,minval,maxval);
 proc means data=&dsname.
     classdata=CLDS(rename=(DUMMY=&varname.) where=(&minval.<=&varname.<=&maxval.))
     noprint nway exclusive;
     class GROUP SUBGROUP &varname.;
     var &varname.;
     output out=&varname._(drop=_TYPE_ _FREQ_) N=COUNT;
 run;

 proc sort;
  by &varname.;
 run;

 proc transpose data=&varname._ out=_&varname.(drop=_NAME_ rename=(&varname.=VAL))  delimiter=_;
  var COUNT;
  id GROUP SUBGROUP;
  by &varname.;
 run;
 %mend;

/*実行*/
 %syougi(Q_1,LEVEL1,1,3)
 %syougi(Q_1,LEVEL2,0,1)
 %syougi(Q_1,LEVEL3,3,5)

 data A_1;
  set _:;
 run;

/*EXCEL出力*/
 libname OUTEX "D:\集計.xlsx" header=no scan_text=no;

data OUTEX."Sheet1$D11:I18"n ;
 set A_1;
 modify OUTEX."Sheet1$D11:I18"n ;
  F1=compress(put(A_X,best.));
  F2=compress(put(A_Y,best.));
  F3=compress(put(A_Z,best.));
  F4=compress(put(B_X,best.));
  F5=compress(put(B_Y,best.));
  F6=compress(put(B_Z,best.));
run;

libname OUTEX clear;











まず、CLDSというのを作っています。
これはクラスデータセットといって、meansのクラスで指定する変数の、フルパターンをクラスデータセットに作成しておくことで、カウントであれば実際のデータの水準が足りなくても0で補完して計算してくれます。

ただ、1つの変数に対しての集計ならいいのですが、複数で、かつ採りうる値が変数ごとに違う場合、変数分それに対応するクラスデータセットをつくってたら煩雑すぎます。

なので、通常、群やサブグループなど固定のクラス変数は固定して、それ以外のカテゴリ値の部分は一端ダミーの変数名にして、大きめにデータセットを作っておきます。

以下がCLDSの中身(一部です)です。






















(一部です)


それで、マクロの中で、実際に集計する対象の変数を指定して、ダミー変数をその変数名にrenameして、かつそのカテゴリ値の採りうる値でwhereでクラスデータセットを絞っています。
以下が、マクロにLEVEL1を指定した時の、meansからoutされたデータセットです。
欠損水準を0で補って、フルセットで集計されています。





















しかしこのままだと縦持ちなのでこれをグループ、サブグループで転置します。

以下は上記のデータセットがtransposeされた後のデータセットです







delimiter=でアンダーバーを追加しています。
たとえば、A_XはグループAのサブグループXの集計結果というわけです。

それでLEVEL1-3までを集計してその結果をコロンモディファイア指定でSetしてつなぎます。
最後にLIBNAME EXCELでだしていますが、DDEでもなんでもいいです。

ざっくりとした例なので、実際に使用される場合は、適宜書き換えてください(とりあえずマクロ名変えましょう)。そのままべたっと貼っても動きません多分。

で注目点はクラスデータと、実際のデータのフォーマットやラベルなどのメタデータが同期していることが必要なのでLENGTHを合わせたり、

proc datasets nolist;
 modify Q_1;
  attrib _all_ informat= format= label='';
quit;

などで、余計なメタデータを消しておいた方がいいです。
classdata=使うといつもエラーになって、あきらめますという話を聞くのですが
大半はここが原因です。

あと最後に、若干ネタ切れ感がでてきたので、取り上げてほしい部分や、詰めSASの問題など
アイデアがある方はご連絡ください



MEANSプロシジャのnwayオプションとTYPESステートメントの話

たとえば以下のように、投与群(A群、B)、性別(男性、女性)、年齢カテゴリ(高齢者、非高齢者)という3水準と体重をもった100オブザベーションのデータセットがあるとします。(体重の作り方は超適当なので気にしないでください)


data TEST;
call streaminit(1234);
 do SUBJID=100 to 199;
  if rand('uniform')<0.5 then ARM='A群';else ARM='B群';
  if rand('uniform')<0.3 then SEX='男性';else SEX='女性';
  if rand('uniform')<0.7 then AGE='非高齢者';else AGE='高齢者';
  if SEX='男性' then WEIGHT=round(rand('normal')*15+65,0.1);
  if SEX='女性' then WEIGHT=round(rand('normal')*15+45,0.1);
  output;
 end;
run;




これに対して、以下のようにMEANSあるいはSUMMARYのclassに3水準を指定して
平均値を求めるようにしてデータセット作成を実行するとどうなるでしょうか?

proc means data=TEST noprint;
 class ARM SEX AGE;
 var WEIGHT;
 output out=OUT_1 mean=MEAN;
run;





























上記のように27オブザベーションで統計量が算出されます。

0水準の場合(全体)=1
1水準の場合を3変数に対して=2×3=6
2水準の場合を3変数から2変数を選ぶ組み合わせ全てに対して=3C2×4=12
3水準の場合 2×2×2=8

なので1+6+12+8=27となるわけです。

全部の組み合わせについて計算してくれるのは凄いのですが、これらの結果全てを使用したくて
プロシジャを実行することは少なく、だいたいは欲しい部分は決まっていると思います。

今欲しいのが一番高い水準→3水準であった場合nwayオプションを指定すると
_TYPE_の値が一番高い部分のみ抽出してくれます。

proc means data=TEST nway noprint;
 class ARM SEX AGE;
 var WEIGHT;
 output out=OUT_2 mean=MEAN;
run;



また、例えば群×性別×年齢と群×年齢の組み合わせの結果が欲しいといった場合は
TYPESステートメントで欲しい水準の組み合わせを指定します。

proc means data=TEST noprint ;
 class ARM SEX AGE;
 types ARM*SEX*AGE ARM*AGE;
 var WEIGHT;
 output out=OUT_3 mean=MEAN;
run;

すると


でめでたしめでたし





MEANSプロシジャのOUT=で作成されるデータセットの要約統計量が入る変数名について

例えば以下のような適当なデータがあったとします。

data Q_1;
call streaminit(1011);
 do i=1 to 100;
  A=ceil(rand('uniform')*10);
  B=ceil(rand('uniform')*2);
  C=ceil(rand('uniform')*5);
  D=ceil(rand('normal')*10);
 output;
 end;
drop i;
run;



例えばAでグループ化して平均B,C,Dそれぞれの変数について平均をとりたければ

proc means data=Q_1 noprint nway;
 class A;
 output out=OUT_1 mean= X Y Z;
run;

でいいわけです。



この時、作成されたデータセットOUT_1について
Bの平均は変数X Cの変数はY DはZに格納されます。
変数の位置関係が重要になるので注意してください。


さてここで

proc means data=Q_1 noprint nway;
 class A;
 output out=OUT_2 mean=;
run;

のようにmean=で何も指定しないとどうなりますか?
エラーにならず


Bの平均はB Cの平均はC Dの平均はDです。
出したい要約統計量が一つであれば、こっちの書き方の方がわかりやすいかもしれませんね。

ちなみにもし、出したいのがBとDの平均とCの合計が欲しいといった
わがままも下のように( )で指定して実現可能です。

proc means data=Q_1 noprint nway;
 class A;
 output out=OUT_3 mean(B D)=BMEAN DMEAN SUM(C)=CSUM;
run;












創意工夫は無限大、meansプロシジャは優秀ですね