2018年4月26日木曜日

NOT演算子で条件の否定をする




突然ですがうなぎと梅干しは食べ合わせが悪いって子供のころ聞いたことありますよね。
けど最近はそんな事ないって言われてますね、実際どうなのか調べてないんでわからないけど。


てことで、以下にとある人の3日間の晩御飯のデータがあったとします。

data DT1;
input DT:yymmdd10. FOOD1:$20. FOOD2:$20.;
format DT yymmdd10.;
cards;
2018/04/05 うなぎ  たくあん
2018/04/06 ラーメン  味噌汁
2018/04/07 うなぎ  梅干し
;
  DT 
  FOOD1  
  FOOD2  
 2018/04/05   うなぎ  たくあん 
 2018/04/06   ラーメン  味噌汁
 2018/04/07   うなぎ  梅干し



FOOD1にはメインとなる食べ物で、FOOD2には漬物とかおまけ的な食べ物が入力されているとします。
ここで食べ合わせが問題ないと思われるレコードのみを抽出したいとします。
つまり「うなぎと梅干しを一緒に食べたレコード以外」を抽出します。



NOT演算子を使えばこんな感じで分かり易いですが、、

data DT2;
  set DT1;
  where not (FOOD1="うなぎ" and FOOD2="梅干し");
run;
  DT 
  FOOD1  
  FOOD2  
 2018/04/05   うなぎ  たくあん 
 2018/04/06   ラーメン  味噌汁


使わないとちょっと分かりにくい感じになってしまいます。

data DT2;
  set DT1;
  where FOOD1^="うなぎ" or FOOD2^="梅干し";
run;
  DT 
  FOOD1  
  FOOD2  
 2018/04/05   うなぎ  たくあん 
 2018/04/06   ラーメン  味噌汁



SAS初心者で、このNOT演算子の書き方を知らずに、分かりにくい方の書き方してる人がたまにいます。

NOT ( 条件 )」という感じで否定したい条件をカッコで囲ってNOTをつけてあげるだけです。「^( 条件 )」という書き方も出来ます。

2018年4月17日火曜日

PROC FORMAT入門7 : フォーマットカタログの保存




以下のようにフォーマットを定義したとします。

proc format;

   value TEST1_
       1 = "MALE"
       2 = "FEMALE"
   ;
   invalue TEST2_
       "M" = 1
       "F" = 2
   ;
run;


定義したフォーマットはライブラリ「WORK」に「FORMATS」という名前で保存されます。
この「WORK.FORMATS」を「SASカタログ(或いはフォーマットカタログ)」といいます。





フォーマットカタログの保存



WORK以外の場所にフォーマットカタログを保存したい場合は「LIBRARY=オプション」を指定します。

PROC FORMAT  LIBRARY = ライブラリ.カタログ名;
      ~ フォーマット定義 ~
RUN;


例)
library = MYLIB.FORMATS ……「MYLIB.FORMATS」で保存
library = MYLIB.MYFMT      ……「MYLIB.MYFMT」で保存
library = MYLIB                   ……「MYLIB.FORMATS」で保存

※3つ目の例のようにカタログ名を省略すると「FORMATS」という名前で保存されます。





ここまでで、フォーマットカタログを保存する方法は分かりましたが、
WORK以外に保存したフォーマットカタログはそのままでは利用できません。利用するには以下2つの方法があります。





① ライブラリ参照「LIBRARY」を利用する



例えば、以下のプログラムで「TEST1_」というフォーマットを使用しています。

data DT1;
   format X TEST1_.;
   X = 1;
run;



まずSASは、この「TEST1_」をフォーマットカタログから探しにいきます。
デフォルトでは、「WORK.FORMATS」→「LIBRARY.FORMATS」の順に探しに行きます。



「WORK.FORMATS」の次に検索される「LIBRARY.FORMATS」はSASが決めたやつで、

LIBNAME LIBRARY  "フォーマットカタログを保存したパス";

と書けば、このパスに格納されている「FORMATS」と名の付いたカタログが検索されるようになります。




② 「OPTIONS FMTSEARCH」 を利用する



「FMTSEARCH」システムオプションでは、参照するフォーマットカタログと、参照する順番を指定できます。

OPTIONS FMTSEARCH = (  参照する順にフォーマットカタログを列挙  )


例) options fmtsearch=( TEST.MYFMT );

「WORK.FORMATS」→「LIBRARY.FORMATS」→「TEST.MYFMT」の順に検索。
(「WORK.FORMATS」と「LIBRARY.FORMATS」は強制的に最初に検索される。)


例) options fmtsearch=( TEST );

「WORK.FORMATS」→「LIBRARY.FORMATS」→「TEST.FORMATS」の順に検索。
(「TEST」というように、ライブラリ名しか指定しない場合、「ライブラリ.FORMATS」が検索される)


例) options fmtsearch=( TEST WORK );

「LIBRARY.FORMATS」→「TEST .FORMATS」→「WORK.FORMATS」の順に検索。
(「WORK.FORMATS」または「LIBRARY.FORMATS」の検索順を明示的に指定して変えることができる)





長い説明になりましたが、データステップ100万回のSASYAMAさんの記事もあわせて参照下さい。(やっぱSASYAMAさんの記事分かり易い、説明うまいなぁ)
http://sas-tumesas.blogspot.jp/2013/12/libraryfmtsearch.html





PROC FORMAT入門 : 記事一覧


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



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

%IFなどのマクロステートメントでは挙動が異なることがあるようですので、使用する場合はご注意ください。

またこれはSAS独特の書き方で、他言語では通用しません。

他言語でも同じように書いてしまうとSASとは異なるロジックで解釈をして想定外の結果になってしまうので、ご注意ください!

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")  Y=1;
      when ("b")  Y=2;
      when ("c")  Y=3;
      otherwise   Y=.;
   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が勝てる勝負だけ選びました)