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

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ユーザー躍進の一年になりますように!!

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

ひとつ前の記事に書きましたが、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書いてる時は、角交換系の振り飛車の間隔ですね。ハッシュオブジェクトもそれに近いけど、もしかしたら横歩取りの感覚かも。
僕はもちろん振り飛車党です。
結局脱線してるっていう