2015年3月13日金曜日

LAG関数とIFN / IFC関数のコンボ技



LAG関数の挙動には注意が必要です。
以下のプログラムを実行してみて、予想した結果になるか見てみましょう。

*** サンプルデータ作成 ;
data DT1;
   do X = 1 to 5;
      output;
   end;
run;

*** 例1: 2個前の値をとってくる ;
data OUT1;
   set DT1;
   Y = lag2(X);
run;
 X 
Y
  1   
  .    
  2
  . 
  3
  1
  4
   
  5
  3  


*** 例2: IFステートメントを含む場合 ;
data OUT2;
   set DT1;
   if X ^= 4 then Y = lag2(X);
run;
 X 
Y
  1   
  .    
  2
  . 
  3
  1
  4
   
  5
  2 




例1は予想通りだと思いますが、例2はどうでしょう?
結果の赤文字で示した部分が「3」になると思ってたら要注意です。


解説


ここから、別記事「LAG関数の動き」で紹介したイメージをもとに説明するので、この別記事を読んでいないと分かりづらい部分あるかも。



まずは以下、「Y = LAG2(X)」と書いたときの内部処理のイメージです。
(自分なりの解釈になっている可能性があるかもしれませんので、あしからず・・)

まず、LAG関数によって内部で「キュー」と呼ばれる仕組みを利用しています。





続いて、「if X ^= 4 then Y = lag2(X)」というように、IFステートメントと組み合わせたときの内部処理のイメージ。

(こちらも自分なりの解釈になってる可能性あり)



LAG関数が実行されないと、キューの中身が更新されないことによるものですね。





ここでもし、「IF条件が通った時だけ、2行前の値を取ってきたい」という場合は、以下のようにLAG関数が毎回実行されるように工夫します。

data OUT3;
   set DT1;
   Y2 = lag2(X);
   if X^= 4 then Y = Y2;
   drop Y2;
run;

 X 
Y
  1   
  .    
  2
  . 
  3
  1
  4
  .  
  5
  3  



ここからちょっとした裏技を紹介。
上記の処理は、LAG関数とIFN・IFC関数を組み合わせることによって、より簡潔に書くことが出来ます。

data OUT3;
   set DT1;
   Y = ifn( X^=4, lag2(X), . );
run;

 X 
Y
  1   
  .    
  2
  . 
  3
  1
  4
  .  
  5
  3  



解説


IFN・IFC関数の構文は以下の通り。

 IFN(  条件式 ,  条件がTRUEの場合の戻り値 ,  条件がFALSEの場合の戻り値  )

 ・・・ 戻り値が数値の場合はIFN、文字の場合はIFC関数を使います。

そしてこの関数、1つ面白い性質があります。

この関数が実行されるとき、
第1引数の条件式の結果がなんであろうと、第2・第3引数は、内部で両方とも処理(計算)が行われているようです。


つまり今回の 「ifn( X^=4, lag2(X), . )」 は、
条件式「X^=4」の結果がTRUEだろうがFALSEだろうが、内部では毎回こっそり第2引数の「lag2(X)」が実行されています。
毎回LAG関数が実行されることで、2行前の値を取ってくることができているわけです。

この性質、他にも使えそうですね。なんか思いつきそうで思いつかないですが。





2 件のコメント:

  1. a_stat_programmer2016年10月10日 8:59

    "内部処理のイメージ"の図が非常に理解するのにありがたかったです。またifnの内部挙動の性質もディープで(笑)、いいことを教えて頂きました。ありがとうございました。

    返信削除
  2. コメント有難うございます!
    LAG関数のこの辺の動きはわりと間違えやすい部分で、私自身も昔痛い目をみました。
    このマニアックな内容の記事がお役に立てて良かったです!

    返信削除