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

2018年4月11日水曜日

WHEREやIFでの数値変数に対する範囲指定




4月からSASを始める方も多いので、入門記事的なのも書いてこうと思います。
最後に注意点も書いてるので、そちらも見ていただけたらと思います。


では早速、以下をご覧ください。

* Sample data ;
data DT1;
   do X=1, 1.1, 2, 2.1, 3;
      output;
   end;
run;

  X  
 1 
 1.1 
 2 
 2.1 
 3 


上のデータからXが「1」「2」「2.1」のいずれかのオブザベーションを抽出したいとします。
なんも考えずに書くと以下のようになりますね。

data OUT1;
  set DT1;
  where X=1 or X=2 or X=2.1;
run;
  X  
 1 
 2 
 2.1 



ですが、この書き方はちょっとメンドイです。以下のようにもっと楽な書き方があります。

data OUT1;
  set DT1;
  where X in (1, 2, 2.1);
run;
  X  
 1 
 2 
 2.1 


こんな感じで、ひとつの変数に対して「この値とこの値」または「この値からこの値まで」のオブザベーションをWHEREやIFで抽出したい場合の、範囲の書き方を以下にまとめました。




範囲の書き方

 指定内容 例 例から抽出されるX  注意事項
 ●●以下

・X <= 2
・2 >= X
 2以下


 ●●未満

・X < 2
・2 > X
 2未満

 ●●以上

・X >= 2
・2 <= X
 2以上

 ●●超

・X > 2
・2 < X
 2超

 ●●以上、■■以下  

・X >= 1 and X <= 2
・X between 1 and 2 
 1以上、2以下

・betweenはwhereだけに対応
 (ifでは使えない)
 ●●超、■■未満

・X > 1 and X < 2

 1超、2未満





 ●●  or  ■■  or  …

・X in (1, 2, 2.1)

 1 or 2 or 2.1

 ●●以上、■■以下
  (かつ整数)
・X in (1:2)

 1以上、2以下の整数

 昔の記事で紹介してます。
 http://sas-boubi.blogspot.jp/2014/01/in.html




注意点


注意①

今回は「DATAステップやプロシジャにおけるWHEREやIF」での「数値変数に対する範囲の書き方」に限定。
%IF等のマクロステートメントでは挙動が異なるので、リファレンスや以下の記事などをご確認下さい。


注意②

取扱注意のため、今回紹介しなかった「1 <= X <= 2」や「1 < X < 2」という書き方もありますが。。

DATAステップ、プロシジャ、マクロステートメントなどの機能毎に挙動が異なる場合があります。
特に、%IFなどのマクロステートメントでは挙動が「超絶」異なるので使用NGです。
またこれはSAS独特の書き方で、他言語では通用しません。


注意③

記事の趣旨から外れますが、初学者向けに注意点。
WHEREやIFに「AND」と「OR」の式が混在していると、「AND」→「OR」の順に式が優先される。
  • X=1 or Y=1 and Z=1 だと、「X=1」or「Y=1 and Z=1」という式のまとまりになる。
  • (X=1 or Y=1) and Z=1 のようにカッコで囲むとそこが優先されて、「X=1 or Y=1」and「Z=1」となる。

2018年4月3日火曜日

IF vs SELECT



「IFは使うけど、SELECTは全然使わない、むしろ要らないよ」っていう人がいたので、IFとSELECTを勝負させてみたいと思います。



各ステートメントについては以下で解説してます。

・IF-THEN-ELSE入門 【条件分岐処理】
・SELECT-WHEN入門 【条件分岐処理】




ROUND1.

以下は、変数VAR1の値によって、VAR2の値を設定しています。
data DT1;

   VAR1 = "b";

   * IFステートメントの場合 ;
   if         VAR1 = "a" then VAR2 = 1;
   else if  VAR1 = "b" then VAR2 = 2;
   else if  VAR1 = "c" then VAR2 = 3;

run;


このように、ひとつの変数の値によって処理を分岐する場合にSELECTがつかえます。

data DT2;

   VAR1 = "b";

   * SELECTステートメントの場合 ;
   select (VAR1);
      when ("a")  VAR2=1;
      when ("b")  VAR2=2;
      when ("c")  VAR2=3;
      otherwise   VAR2=.;
   end;

run;

SELECTの方がスッキリしている気がします。なのでSELECTの勝ちです。




ROUND2.

以下は、変数VAR1の1バイト目の値によって、VAR2の値を設定しています。

data DT3;

   length VAR1 VAR2 TEMPVAR $10.;
   VAR1 = "M001";

   * IFステートメントの場合 ;
   TEMPVAR = substr( VAR1,1,1);

   if         TEMPVAR = "M" then VAR2 = "MALE";
   else if  TEMPVAR = "F" then VAR2 = "FEMALE";

   drop TEMPVAR;
run;

TEMPVARという変数にVAR1の1バイト目を入れておいて、最後にこの変数をDROPしています。


このような場合も、SELECTの方がスッキリしてみえます。

data DT4;

   length VAR1 VAR2 $10.;
   VAR1 = "M001";

   * SELECTステートメントの場合 ;
   select (substr(VAR1,1,1));
      when ("M")   VAR2 = "MALE";
      when ("F")    VAR2 = "FEMALE";
      otherwise      VAR2 = "";
   end;

run;

IFの時は、「TEMPVAR = substr(VAR1,1,1);」っていう変数作って、あとでDROPしましたが、
SELECTの時は、「select (substr(VAR1,1,1));」って書いちゃえば、変数作らずに済んでDROPステートメントが不要になります。


これもSELECTの勝ち。



てことで、SELECTの完全勝利ですね。
(ちなみにハンデとしてSELECTが勝てる勝負だけ選びました)

2016年3月17日木曜日

IF-THEN-ELSE入門 【条件分岐処理】


IF-THEN-ELSEステートメントは条件によって処理を分岐する場合に使います。




構文1

IF  条件式  THEN  処理 ;

  • 「条件式」を満たす場合に、指定した「処理」を実行する。




例
data DT1;
   set SASHELP.CLASS;
   if AGE=11 then X=1;
run;
  • 「AGE=11」 だったら、「変数X=1」 にする。





構文2

IF  条件式1  THEN  処理1 ;
ELSE  IF  条件式2  THEN  処理2;
ELSE  処理3;

  • 「条件式1」を満たす場合に、「処理1」を実行。
  • 「条件式1」以外で「条件式2」を満たす場合に、「処理2」を実行。
  • 「条件式1」と「条件式2」以外だったら、「処理3」を実行。



例
data DT2;
   set SASHELP.CLASS;
   if AGE=11 then X=1;
   else if AGE=12 then X=2;
   else if AGE=13 then X=3;
   else X=99;
run;




構文3

IF  条件式  THEN  DO;
      処理1 ;
      処理2 ;
      ~
END;
ELSE  IF  条件式  THEN  DO;
      処理1 ;
      処理2 ;
      ~
END;
ELSE  DO;
      処理1;
      処理2;
END;

  • DO~ENDで囲って処理を複数指定できます。



例
data DT2;
   set SASHELP.CLASS;
   if AGE=11 then do;
          X=1;
          Y=1;
   end;
   else do;
          X=99;
          Y=99;
   end;
run;

  • 「AGE=11」 だったら、「変数X=1」「変数Y=1」 にする。
  • それ以外だったら「変数X=99」「変数Y=99」にする。




2015年6月26日金曜日

データステップ内で、色々なデータセットのオブザベーション数を取得する


(注意!)
今回の方法で正しくobs数を取得できないケースもあるので、記事の最後に貼っているリンク記事も参照下さい。



まずは適当なサンプルデータを用意。

* サンプルデータ ;
data DT1;
   do i=1 to 10;
      output;
   end;
run;

data DT2;
   do i=1 to 20;
      output;
   end;
run;

data DT3;
   A="aa";  output;
   A="bb";  output;
   A="cc";  output;
run;

DT1 は10obs、DT2 は20obsを持つデータセットです。



ここで、DT3を読み込むときに、DT1とDT2のobs数も取得したいとします。

* 各データセットのOBS数を取得 ;
data OUT1;

   set DT3;

   if 0 then set DT1(drop=_ALL_)  nobs= _OBS1;
   if 0 then set DT2(drop=_ALL_)  nobs= _OBS2;

   DT1_OBS = _OBS1;
   DT2_OBS = _OBS2;

run;

  A  
 DT1_OBS 
 DT2_OBS 
  aa 
  10  
  20 
  bb
  10  
  20 
  cc
  10  
  20 


解説

プログラム青字部分がポイントです。
「if 0 then」 の 「0」 は 「false」 の意味になり、then以降の 「set DT1(drop=_ALL_) ・・・」 の部分は実行されません。
。。のはずですが、たとえ実行されなくてもデータセット名の指定があると、コンパイル時にそのデータセットの定義情報だけが読み込まれます。

この性質を利用し、
・まず 「if 0 then set DT1」 でDT1の定義情報だけを読み込みます。
・変数定義も読み込まれてしまうので 「DT1(drop=_ALL_)」 で読み込まれる全変数をdropする。
・「nobs = 適当な一時変数名」で、定義情報に格納されたobs数を一時変数に格納。
といった流れです。


※ 以下記事の通り、特殊な処理をしてる場合は、正しくobs数を取得できないのでご注意ください。
行削除の落とし穴


2015年4月28日火曜日

IFステートメントとDOループは繋げて書ける。


タイトルの通りですが、つい最近まで繋げて書けないと思ってました。

* Sample Data ;
data DT1;
   do A=1 to 3;
      output;
   end;
run;

 A 
  1 
  2  
  3  

* IF THEN DOループ ;
data DT2;
   set DT1;

   if A=2 then do B=1 to 3;
      output;
   end;

   else output;
run;

 A 
  B  
  1 
  . 
  2  
  1 
  2  
  2 
  2  
  3 
  3  
  . 

「IF文による条件に当てはまった時だけ、DOループをまわす」って感じになります。

これ知るまでは以下みたいに書いてました。

data DT2;
   set DT1;

   if A=2 then do;
      do B=1 to 3;
         output;
      end;
   end;

   else output;
run;


SAS覚え中の人に言ったら、「知ってた」って言われたんで多分知らなかったの私だけっぽい。

2014年1月24日金曜日

条件式(IF/WHERE)のちょっと効率的な書き方。


IF/WHEREステートメントには以下の真偽ルールが存在する。

変数値または式の結果が

 ・ 0、NULLの場合 ・・・ 「0.false」
 ・ 上記以外 ・・・ 「1.true」

として評価される。




解説

これはどういう事なのか、以下のサンプルプログラムを実行してみると分かる。

*** サンプルデータ作成 ***********;
data TEST1 ;
  input V1 @@;
cards ;
. 0 1 2 3
;








*** サンプルプログラム ***********;
data TEST2;
  set TEST1;
  if  V1  then  V2=1;
run;



真偽ルールに従い、変数「V1」が「true」となったものに対して、「V2=1」となっている事が分かる。



活用例①

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

data DT2 ;
  do A=2 to 4 ;
     output ;
  end ;
run ;

data DT3 ;
  do A=1 to 3 ;
     output ;
  end ;
run ;

DT1

DT2
 
DT3


上記3データセットを変数「A」をキーにマージしたいとする。
その後たとえば、、
「A」の値が、「DT2」にあって「DT3」にないものを抽出したい場合、
IN=オプションを使って、通常以下のように書く。

data OUT1;
  merge DT1
            DT2 (in=INDT2)
            DT3 (in=INDT3)
  ;
  by A;
  if  INDT2=1 and INDT3=0 ;
run;




これは真偽ルールを元に書き直すと、
 if  INDT2  and  ^INDT3 ;
となる。


「^」は「NOT」の略で、真偽ルールが逆になる。つまり、、
  ・0、NULLの場合 ・・・ 「1.true」
  ・上記以外 ・・・ 「0.false」
となる。

この真偽ルールと論理演算子「^」の組み合わせは便利で、
使用する場合は以下記事を参照下さい。
論理演算子「^」「^^」によるフラグ変数の作成


活用例②

*** サンプルデータ作成 ******;
data DT4 ;
  input B$ ;
cards;
ABCD
EFGH
;
run;



上記データセットに対して、
変数「B」に"C"という文字が含まれているレコードを抽出したい場合、
INDEX関数を使って、通常以下のように書く。

data OUT2 ;
  set DT4 ;
  if index(B, "C") > 0 ;
run ;



これを真偽ルールに従って書き直すと、
 if index(B, "C") ;
と書くだけでよい。


まとめ

「0/1」や「NULL/1」と入ってる有無変数やフラグ変数に対して有効的に働き、
論理演算子「^」と組み合わせることでスマートに書けるので便利です。

2014年1月21日火曜日

条件式(IF/WHERE)におけるINオペレータの小技




以下のようなデータセットがあったとする。

data DT1;
    do V=1 to 6 ;
        output;
    end;
run;



そこから、変数「V」が「1, 3, 4, 5」のどれかだったら、
レコードを抽出したり何らかの処理を行いたい場合、以下のように書くことが出来ます。

data DT2;
    set DT1;
    if  V in (1, 3, 4, 5) ;
run;



さらに「コロン」を使うと便利です。
「3 : 5」とコロンを挟むことで、「3~5の整数」と範囲指定することができます。

data DT2;
  set DT1;
  if  V in (1, 3 : 5) ;
run;


IFじゃなくて 「WHERE V in (1, 3 : 5);」
とWHEREに置き換えて実行すると、どのような内部処理が行われてるかログに表示されます。

それによると 「WHERE  (V=1)  or  ( (V=INT(V))  and  (V>=3 and V<=5) ) ;」
みたいな長ったらしい文に置き換えて内部処理してることが分かります。

「1または3~5の整数」 という条件になってますね。



注意

コロンによる指定は整数限定(「0.1 : 0.5」みたいな小数指定は無理)。