2014年10月15日水曜日

全オブザベーション欠損値の変数を削除する


たとえば、以下サンプルデータで、削除したい変数はAとCです。

*** サンプルデータ ;
data DT1;
   A=.; B=1; C=""; D=1; output;
   A=.; B=1; C=""; D=2; output;
   A=.; B=.; C=""; D=3; output;
run;

 A  
 B  
 C  
 D  
  .
  1
  
  1
  .
  1
  
  2
  .
  .
  
  3

欲しい結果
 B  
 D  
  1
  1
  1
  2
  .
  3

そこで、データステップ100万回「freqプロシジャのnlevelsオプション」をヒントにプログラムを組んでみました。


*** ① 全OBS欠損値の変数を特定する ;
ods output NLevels=O_NLEVEL;

    proc freq data=DT1 nlevels;
       tables _all_ / noprint;
       format _all_;
    run;

ods output close;

*** ② 特定した変数をDROPする ;
data _NULL_;
    length  NNONMISSLEVELS 8. ;
    set  O_NLEVEL  end=EOF;
    if  _N_=1 then
          call execute( "DATA DT2; SET DT1 (DROP=" );

    if  NNONMISSLEVELS=0 then
          call execute( TABLEVAR );

    if  EOF then
          call execute( "); RUN;" );
run;


解説

①まず、FREQプロシジャのNLEVELSオプションによって下のようなOUTPUTとデータセットが出来る。

OUTPUT
変数    水準数    欠損水準    非欠損水準
----------------------------------------------
A              1           1             0
B              2           1             1
C              1           1             0
D              3           0             3

データセット
 TableVar 
 NLevels 
 NMissLevels 
 NNonMissLevels 
 A
 1
 1
  0
 B
 2
 1
  1
 C
 1
 1
  0
 D
 3
 0
  3

注目してほしいのは「非欠損水準(NNonMissLevels)」で、これが0ってことはその変数は全オブザベーションが欠損値ということですよね。


②次にプログラムを生成・実行してくれるCALL EXECUTEを使い、上で取得した非欠損水準が0の変数をDROPするプログラムを生成します。

生成されるプログラムは以下のような感じ。

DATA DT2; SET DT1 (DROP=
A
C
); RUN;


同様の処理を沢山のデータセットにやりたい場合は、このプログラムをちょっと改良してマクロ化しちゃえば楽です。

単純に全オブザベーション欠損値の変数が知りたいだけでも、FREQプロシジャ+NLEVELSの組み合わせでいけちゃうので便利です。



📝注意


・変数に format が割り当てられている場合、
formatがあてられた値に対してNLEVELSが動いてしまい、思った動きをしてくれないことがある。そのため、FREQプロシジャのとこで「format _all_」でformatを解除しておいた方が良いです。


・対象のデータセットが空(0オブザベーション)の場合、FREQプロシジャのとこでつまづいてWARNINGが出ちゃってうまく動きません。

その辺も考慮しなきゃいけない場合は、「オブザベーション数=0なら~」みたいな条件分岐する文を追記する必要があります。


・今回のテクニックの中で使用している「_N_」と「END=オプション」は「サブセット化IF」と一緒に使用すると正しく動かなくなりやすいです。
(解説記事:「サブセット化IFでありがちな落とし穴」)


2 件のコメント:

  1. どうもです。
    データセットのO_NLEVELの中身の図の方に誤記があるかもです。
    Dの行について1 1 0になってますが 3 0 3では?
    リストの方はあってるはずです。

    ところで100投稿目、おめでとうございます!!
    一昨日も言いましたけど、これだけ役に立って、丁寧な記事を発信されているのは、本当に素晴らしいです。
    困ったときに役に立つのは100万回ブログよりも忘備録ともっぱらの噂です。

    これからもお互い頑張りましょう。

    返信削除
    返信
    1. あっ寝ぼけてました!
      ご指摘ありがとうございます!

      ひっそりとした100投稿目になると思ってたので、コメントいただけて嬉しいです。
      今回の投稿もSASYAMAさんの投稿からアイディアを頂きましたし、ほんと100万回ブログ参考になります。

      これからもSASYAMAさんのブログタイトルにもなっている新手一生を掲げて、何か面白いアイディアやテクニックを発掘していきましょう!

      削除