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

2018年11月22日木曜日

FIRSTOBS=, OBS=, WHERE を組み合わせたときの挙動




データセットオプション「FIRSTOBS=」と「OBS=」の紹介と、WHEREステートメントを組み合わせて使用した時の挙動について、まとめておきます。



データの状態によっては結果がバグる事があって、その辺の注意点については最後に別記事のリンク貼ってるので参照下さい。



Sample data
data a;
   do i=1 to 10;
     output;
   end;
run;





FIRSTOBS=オプション

・処理(読込み)を開始するオブザベーションを指定できます。

proc print data=a (firstobs=3);
run;




OBS=オプション

・何オブザベーション目まで処理するか(読み込むか)を指定できます。

proc print data=a (obs=3);
run;




FIRSTOBS=, OBS=, WHERE の組み合わせ

ここから本題。以下のプログラムで結果が予測できればバッチリです。

proc print data=a (firstobs=2 obs=3);
  where i>5;
run;


解説
・まずWHEREでオブザベーションを抽出。
・抽出したオブザベーションに対して、FIRSTOBS=とOBS=を適用。
・FISTOBS=とOBS=で共通する範囲にあるオブザベーションが処理対象となる。


以下にイメージを載せておきます。


ちなみに

SETステートメントとかでもこれらのオプションが使えます。

data b;
  set a (obs=3);
run;



注意


以下の記事で、FISTOBS=オプション自体の落とし穴も紹介しています。
FIRSTOBSオプションの落とし穴




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年9月2日日曜日

【小ネタ】抽出条件のないWHEREステートメント





以下のように抽出条件のない空のWHEREステートメントを書いてもエラーになりません。

data a;
   set sashelp.class;
   where;
run;

抽出条件が掛かれていないので、すべてのオブザベーションが読み込まれます。




これが出来るからなんなのか、、っていうと、例えば以下のようなマクロを定義したい場合ですね。

* マクロ定義 ;
 %macro test( wh= );

   data a;
      set sashelp.class;
      where &wh;
   run;

 %mend;


* マクロ実行 ;
 %test( wh=age=13 )  /* マクロ実行:抽出条件を指定する場合 */
 %test( wh= )              /* マクロ実行:抽出条件を指定しない場合 */



抽出条件があればマクロ変数whに条件を指定すればいいし、なければ何も指定しなければいいだけ。
抽出条件がある場合とない場合で%ifとか使って分岐処理とかさせる必要ありません。







ただ、where=データセットオプションでは条件を空にするのはダメのようです。

data a;
   set sashelp.class (where=());
run;

ログ
 ERROR: WHERE式の読み込み中に、構文エラーが発生しました。

 ERROR 22-322: 構文エラーです。次のいずれかを指定してください: 名前, 引用符で囲んだ文字列, 
               数値定数, 日時定数, 欠損値, INPUT, PUT.  

 ERROR 76-322: 構文エラーです。ステートメントを無視しました。

 ERROR 6-185: データセットのオプションリストに')'のかっこがありません。

 ERROR 79-322: )を指定してください。

 NOTE: エラーが発生したため、このステップの処理を中止しました。
 WARNING: データセットWORK.Aは未完成です。このステップは、0オブザベーション、
          0変数で停止しました。
 WARNING: このステップを中止したため、データセットWORK.Aを置き換えていません。


ログが怒りすぎ、なんかかわいいです。



なので私は以下のように書いてSASをごまかしています。

data a;
   set sashelp.class (where=(1));
run;

「1」は真(true)の意味です。
常に条件が真(true)の状態、つまり全オブザベーションが読み込まれます。


「0」ってのもあります。

   set sashelp.class (where=(0));


この「0」は偽(false)の意味です。
常に偽(false)の状態、つまり全オブザベーション読み込まれません。



勿論この書き方はwhereステートメントでも通用します。

data a;
   set sashelp.class;
   where 1;
run;


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」となる。

2017年7月13日木曜日

欠損値の判定【IS NULL】【IS MISSING】【MISSING関数】




WHEREステートメントに、「変数が欠損値」という条件を入れたい場合、以下のように書く方が多いと思います。


・数値変数の場合
  where X = .;

・文字変数の場合
  where X = ””;



別の書き方として、以下のように書くことも出来ます。
  where X is null;        /* is null */
  where X is missing;  /* is missing */
  where missing(X);    /* missing関数 */


変数が欠損値”以外”の場合はnotをつけます。
  where X is not null;        /* is null */
  where X is not missing;  /* is missing */
  where not missing(X);    /* missing関数 */



📝この3つの書き方は

  • 欠損値および特殊欠損値に反応します(特殊欠損値については、あまり使用されていない&それ故に使用すると混乱を招きがちなので、本記事では説明を省略。。)
  • 変数が数値型だろうが文字型だろうが、関係なく通るので便利です
  • ただし「is null」と「is missing」は、WHEREステートメントでした使えない書き方です

IF文とかで使いたい場合は、missing関数が使えます。
  if missing(X) then Y=1;



2015年5月17日日曜日

WHEREステートメントの落とし穴


「WHEREステートメント」 と 「WHERE=データセットオプション」 を組み合わせると、想定外のデータになってしまうかもしれないです。


*** サンプルデータ作成 *************;
data DT1;
   A=1; B="aa"; output;
   A=2; B="bb"; output;
run;

 A 
   1 
 aa  
   2  
 bb  

data DT2;
   A=1; output;
   A=2; output;
run;

 A 
  1 
  2  

*** 注意が必要な例 **********;
data OUT1;
  merge DT1(where=(B="aa"))  DT2;
  by A;
  where A=2;
run;

 A 
   1 
 aa  
   2  
   


結果のデータセットは想定通りだったでしょうか?
↓こんな感じになると思った方もいると思います。

 A 
   2  
       


これは、
  • 「WHERE=データセットオプション」が指定されたデータセットは、そのWHERE条件だけが適用され、
  • それ以外のデータセットは「WHEREステートメント」の条件が適用される。


つまり、今回の例で、、

  merge DT1 (where=(B="aa"))  DT2;
  by A;
  where A=2;

  • DT1は「where=(B="aa")」の条件が適用され、
  • DT2は「where A=2」の条件が適用されて、マージされます。


ちなみに、SAS9.3からログに以下のようなWARNINGが出るようになっていますが、SAS9.2ではWARNINGが出ないので、この問題に気づきにくいです。

WARNING: WHEREステートメントは最後のSET/MERGE/UPDATE/MODIFYステートメントのデータセットに適用できません。
データセットがオープンエラー、または既にWHEREデータセットオプションを指定しています。



2014年11月25日火曜日

WHEREステートメントのLIKE演算子でエスケープ文字を設定する。



まずWHEREステートメントのLIKE演算子について、

データステップ100万回「whereステートメントでのみ使用できる演算子_contains,between,like
で分かり易い説明があるのでご覧ください。



そして、今回紹介するエスケープ文字の設定について。

Sample data
data DT1;
   VAR = '100%'; output;
   VAR = '100'; output;
run;

VAR
  100% 
  100


例えば上のデータから、末尾に「%」という文字が含まれるやつを抽出したい場合、

data DT2;
   set DT1;
   where VAR like '%#%' escape '#';
run;

VAR
  100% 

というように書けます。

escape '#'」で、「#」という文字をエスケープ文字として設定し、
この「#」の次に書かれた「%」をワイルドカードではなく、単なる文字としての「%」に変えています。


エスケープ文字は、「$」とか「\」とか自分で適当に設定できます。

ちなみに、
本題とそれるけど例えば「where VAR like "%abc"」みたいに、検索文字列をダブルクォーテーションで囲むと、検索文字列中の「%abc」がマクロとして解釈されてしまう。
マクロとして解釈されたくない場合は、検索文字列をシングルクォーテーションで囲むこと。



2014年1月24日金曜日

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


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

変数値または式の結果が

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

として評価される。




解説

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

*** サンプルデータ作成 ***********;
data TEST1 ;
  input V1 @@;
cards ;
1 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」みたいな小数指定は無理)。