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

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(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杯で敗退してしまったので、最近テンション低めです

水準がデータで揃わずスカスカの集計表に立ち向かう_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の問題など
アイデアがある方はご連絡ください