ラベル ステートメント: サブセット化IF の投稿を表示しています。 すべての投稿を表示
ラベル ステートメント: サブセット化IF の投稿を表示しています。 すべての投稿を表示

2023年4月22日土曜日

サブセット化IFでありがちな落とし穴


SASプログラマ歴が長いと、みんなこれ経験してるんじゃなかろうか。


「サブセット化IF」自体は以下記事で解説しています。


😕失敗例1: 「_N_」と組み合わせて失敗しがち


各オブザベーションに1~連番をふった変数を作りたくて、以下のように書いたのですが、、

data dt1;
 set sashelp.class;
 if age=12;
 x = _n_;
 keep age x;
run;


変数Xに1~連番を入れたいのに、なんかおかしい。


解説

  • まずデータステップは内部の動きとして、
オブザベーションの数だけ、データステップをぐるぐるとループ(反復)させています。
ループのたびに1オブザベーションずつ読み込んでいるイメージで、「_N_」はそのループ毎に「+1」した番号が入ります。

  • 次に「_N_」は内部の動きとして、
まずSET文・WHERE文で読み込むオブザベーションに対して、データステップがループ(反復)されますが、自動変数「_N_」は、各反復内でいち早く番号がふられます

番号をふった後で、サブセット化IFにより処理が継続されずに出力もされなかったオブザベーションがあると、番号が連番にならなくなります。



📝特に「IF _N_=1 THEN なんかの処理;」みたいな書き方で失敗しがち。

例えば、以下プログラム(中身はめっちゃ意味のないことやってるのであんまり見ないでください)

data dt2;
 set sashelp.class;
 if age=12;
 if _n_=1 then do;
  dcl hash hs(dataset:"sashelp.class");
  hs.definekey("name");
  hs.definedone();
 end;
 if hs.check()=0 then x2=1;
run;


_N_=1 のオブザベーションがたまたまサブセット化IFによって「処理継続・出力」の対象外になっているので、「IF _N_=1 THEN DO; ~END;」の中の処理も動いていません。

上の例ではエラーが出て分かりやすいですが、書き方によってはエラーが出ない場合もあって失敗に気づかない可能性もあるので要注意です。


😕失敗例2: 「END=オプション」と組み合わせて失敗しがち


最後のオブザベーションにフラグを立てたくて、以下のように書いたのですが、、

data dt3;
 set sashelp.class end=_eof;
 if age=12;
 if _eof then y=1;
 keep age y;
run;


フラグ立ってないじゃないか!


これは「END=オプション」の挙動を勘違いしていると起こりやすいです。

「END=オプション」はSET文・WHERE文で読み込まれる最後のオブザベーションに作用しますが、その最後のオブザベーションがサブセット化IFによって「処理継続・出力」の対象外になっているためです。


あと思いつくのが、以下で紹介している「FIRST.BY変数」「LAST.BY変数」も、サブセット化IFと組み合わせると、同様の原理で意図しない結果になりやすいですね。

「FIRST.BY変数」と「LAST.BY変数」で、グループ毎の最初と最後のオブザベーションを特定する。


2018年10月1日月曜日

【効率を意識したプログラム】WHEREとサブセット化IFの使い分け




「WHERE」と「サブセット化IF」の違いを知って使い分けようっていう話です。



「WHERE」と「サブセット化IF」の比較


Sample data
data test;
   length col1-col200 $200.;
   array ar(*) col:;
   do i=1 to dim(ar);
       ar(i) = "abcdefg";
   end;
   do i=1 to 50000;
      output;
   end;
run;



サンプルデータから「WHEREステートメント」を使ってオブザベーションを抽出したときの処理時間とCPU時間を見てみます。

data out1;
   set test;
   where i<100;
run;

ログ
       処理時間           0.33 秒
       ユーザーCPU時間    0.02 秒
       システムCPU時間    0.32 秒


同様のオブザベーションの抽出を「サブセット化IF」を使って処理時間とCPU時間を見てみます。

data out1;
   set test;
   if i<100;
run;

ログ
       処理時間           0.53 秒
       ユーザーCPU時間    0.12 秒
       システムCPU時間    0.41 秒

WHEREステートメントの方が処理時間が短いですね。何故このような違いが起きたのでしょうか。





「WHERE」と「サブセット化IF」の違い


・実行されるタイミングが違う
 WHERE

 データを読み込む前に実行される
 サブセット化IF 

 データを読み込んだ後のデータステップ内で実行される

WHEREの方が実行されるタイミングが早いんで、処理時間が短くなるというわけですね。



・実行できる環境の違い
 WHERE

 データステップおよび(ほとんどの)PROCステップで実行できる
 サブセット化IF 

 データステップでのみ実行できる





「サブセット化IF」の利点


サブセット化IFが効果的な例として、以下記事の例②で紹介しているようなケース。
【サブセット化IF】条件に一致するオブザベーションのみ処理を継続する。

data out2;
  set dt1;
  c = a + b;
  if c >= 10;
run;

この例ではデータステップ内で計算した変数Cをサブセット化IFで使用しています。
WHEREステートメントだと「where c >= 10」と書くことは出来ませんよね(データを読み込む前には、まだ変数Cが導出されていないので)




2018年8月15日水曜日

【サブセット化IF】条件に一致するオブザベーションのみ処理を継続する。





今回は頻繁に使われる「サブセット化IF」というステートメントについて解説。



構文

IF  条件式  ;

  • データステップ内で使用します。
  • 条件式が成立しない場合、そのオブザベーションに対する残りのプログラムを中止します(そのオブザベーションはデータセットに出力されません)そして即座に次の反復(次のオブザベーションの読み込み)に移行します。
  • 条件式が成立する場合、そのオブザベーションに対する残りのプログラムが実行されます。





* Sample data ;
data dt1;
input a b;
cards;
1 5
2 6
3 7
4 8
;

例①
data out1;
  set dt1;
  if a = 2;
  c = a + b;
run;




やってる事
  • a = 2 のオブザベーションであれば、以降の処理(変数cを導出)して、out1に出力
  • それ以外はそのオブザベーションに対する処理を中止して、out1に出力しない



例②
data out2;
  set dt1;
  c = a + b;
  if c >= 10;
run;


やってる事
    • データステップ内で変数cを導出
    • c >= 10 のオブザベーションをout2に出力
    • それ以外はそのオブザベーションに対する処理を中止して、out2に出力しない


    データステップ内で導出した変数をサブセット化IFに使用できるのがポイント





    その他サブセット化IFの記事