2017年5月26日金曜日

My Favorite SAS Functions (5) COALESCE / COALESCEC関数


第5回目は、COALESCE / COALESCEC関数。
お気に入り関数トップ3のうちのひとつです(あと2つは WHICHN、CHOOSEN)

この関数は「引数のうち最初に欠損値以外で登場する値を返す」という機能を持っています。


基本的な構文や機能は以下記事で紹介済みなので、そちらをご覧ください。
http://sas-boubi.blogspot.jp/2016/04/coalesce.html

データステップ100万回でも特集されてます。
http://sas-tumesas.blogspot.jp/2013/09/sas3null.html





上の過去記事でさんざん例を紹介済みな感じですが、あらためて。
以下X1~X3の中から最初に欠損値以外で登場する値を返したいとします。

  X1  
  X2  
  X3  
   

  aa  bb  aa  



以下、COALESCEC関数を使わない場合(Before)と、使う場合(After)。

Before
  length Y $2.;
         if X1^="" then Y=X1;
  else if X2^="" then Y=X2;
  else if X3^="" then Y=X3;

After
  length Y $2.;
  Y = coalescec( X1,X2,X3 );


いいですね~このスッキリ感がたまらないですね。

ちなみにCOALESCE関数あるあるだと思うんですが、読み方が最初よく分からなくて、綴りをいちいち調べてた時期がありました。

コアレスと読むそうです。
上にリンク貼ったSASYAMAさんの記事でもコールエッセって読んでたってお話しがありますね。
私はコアレスシって読んでました。



記事一覧

1.CATS関数
5.COALESCE・COALESCEC関数

2017年5月23日火曜日

REPORTプロシジャ入門10:数値変数の落とし穴



前提知識として、まず以下リンク記事をご覧ください。
REPORTプロシジャ入門4:集計【ANALYSIS】




では、落とし穴の例を見ていきましょう。


落とし穴の例


データセットSASHELP.CLASS の NAME, AGE をREPORTプロシジャで出力してみます。

* 例① ;
proc report data=SASHELP.CLASS nowd;
   column NAME AGE;
   define NAME / "名前";
   define AGE  / "年齢";
run;


まぁ、これは特におかしなところはないですね。
では以下のように年齢だけ出力してみるとどうでしょうか?


* 例② ;
proc report data=SASHELP.CLASS nowd;
   column AGE;
   define AGE  / "年齢";
run;


「あれ?1行しか出力されていないうえ、年齢「253」てなんだよ!」って感じですよね。




解説


実は、REPORTプロシジャに指定した ”数値変数” は、以下青字のように「analysis sum」オプションをSASが裏で勝手につけて実行しています。

proc report data=SASHELP.CLASS nowd;
   column AGE;
   define AGE  / analysis sum "年齢";
run;


つまり、AGEを合計して「253」という出力になってしまったわけですね。


以下のように「display」オプションを明示的につければ、この勝手な挙動を制御できます。

proc report data=SASHELP.CLASS nowd;
   column AGE;
   define AGE  / display "年齢";
run;



ちなみに最初の例①の場合は、SASが裏で以下青字のように判断して実行します。

proc report data=SASHELP.CLASS nowd;
   column NAME AGE;
   define NAME / display "名前";
   define AGE    / analysis sum "年齢";
run;


文字変数の場合は特にこちらで明示しない場合「display」が勝手につくわけです。

つまり挙動として、数値変数 AGE だけだと合計してしまうけど、文字変数 NAME を「display」オプションでそのまま表示するようにしているから、AGE も合計されなくて済んでるっていう仕組みです。
(AGE が「全ての行」の合計ではなく、行ごとに「その行の1つの数値だけ」で合計する、という意味のない動きになっていて、結果的に格納値そのままの値になる)



9.特定セルの書式設定【CALL DEFINE】
10:数値変数の落とし穴

2017年5月20日土曜日

My Favorite SAS Functions (4) CHOOSEN / CHOOSEC関数


第4回目は、CHOOSEN / CHOOSEC関数。
これもいい関数ですよ!



構文


    CHOOSEN( Y , X1 , X2 , X3 , … )


・Y番目のXの値を返す。
・第2引数以降に数値型の変数または値を指定する場合はCHOOSEN関数を用いる。
・第2引数以降に文字型の変数または値を指定する場合はCHOOSEC関数を用いる。





以下のように、X1~X3のうち、Y番目の値を取得したいとします。。

  X1  
  X2  
  X3  
  Y  
  XVAL  
  aa    bb  cc  2     bb


以下、CHOOSEC関数を使わない場合(Before)と、使う場合(After)。

Before
  length XVAL $2.;
  if Y = 1 then XVAL=X1;
  if Y = 2 then XVAL=X2;
  if Y = 3 then XVAL=X3;


After
  length XVAL $2.;
  XVAL = choosec( Y ,X1,X2,X3 );



こんな感じでスッキリとした文になるんでよく使います。今日も使いました。


他の便利な使い方を過去記事でも紹介してるので是非見てくださいね。
http://sas-boubi.blogspot.jp/2014/03/12_25.html

2017年5月15日月曜日

My Favorite SAS Functions (3) WHICHN / WHICHC関数


第3回目は、一番お気に入りの WHICHN / WHICHC関数です。
お気に入りすぎて、WHICHN関数使ってる夢みたことあります。



構文


    WHICHN( Y , X1 , X2 , X3 … )


・Yと同じ値がXの何番目に初登場するか返す。
・Yと同じ値がXに無い場合、0を返す。
・Yが欠損値の場合、欠損値を返す。
・引数に数値型の変数または値を指定する場合はWHICHN関数を用いる。
・引数に文字型の変数または値を指定する場合はWHICHC関数を用いる。





以下のように、Y の値が X1~X3 の何個目と同じか知りたいとします。

  Y  
  X1  
  X2  
  X3  
  XNUM  
  bb    aa  bb  cc     2


以下、WHICHC関数を使わない場合(Before)と、使う場合(After)。

Before
  if Y = X1 then XNUM=1;
  else if Y = X2 then XNUM=2;
  else if Y = X3 then XNUM=3;


After
  XNUM = whichc( Y ,X1,X2,X3 );



過去記事でもとりあげてるので、是非読んでみてください。


2017年5月11日木曜日

My Favorite SAS Functions (2) CATX関数



第2回目は、CATX関数。
この関数は「文字列同士を区切り記号付きで連結する」という機能を持っています。


構文

  CATX( "区切り文字" , 変数1, 変数2, ・・・ )


  • 変数値の先頭か末尾に半角スペースがある場合は、それらを削除してから結合する
  • 文字変数が欠損値の場合は、その変数はスキップされる (区切り文字もつかない)

基本的な構文や機能は以下記事で紹介済みなので、そちらをご覧ください。
http://sas-boubi.blogspot.jp/2015/11/catx.html





例えば、以下X1~X4までの変数値を連結してYのような文字列を作りたいとします。

  X1  
  X2  
  X3  
  X4  
   
  aa      bb  cc  aa,bb,cc 


今までは、以下Beforeのようなメンドクサイ書き方をする必要ありました。あー面倒だなーって言いながら書いてました。
それがAfterのようにCATX関数を使えば超スッキリと短い文で書けてしまいます。

Before
  length Y $20.;
  array AR(*) X1-X4;
  do i=1 to dim(AR);
     if AR(i)^="" then do;
        if Y = "" then Y=left(AR(i));
        else Y = trim(left(Y)) || "," || trim(left(AR(i)));
     end;
  end;


After
  length Y $20.;
  Y = catx( "," ,X1,X2,X3,X4 );


今でこそ当たり前につかってますが、知ったときは感動しました。



記事一覧

1.CATS関数
2.CATX関数

2017年5月9日火曜日

My Favorite SAS Functions (1) CATS関数



個人的にお気に入りかつ使いこなすと便利な関数を紹介したいと思います。
まずは一番ヘビーに使うCATS関数から。



構文


    CATS( 変数1, 変数2, … )


指定した変数または値を結合する。
(変数または値の両脇の半角スペースは取り除いたうえで結合されます。)





今まで文字を結合する時は、以下Beforeのような書き方が定着しています。
それがCATS関数を使えば超スッキリと短い文で書けてしまいます。

Before
  length Y $20.;
  Y = trim(left(X1)) || trim(left(X2)) || trim(left(X3));


After
  length Y $20.;
  Y = cats(X1,X2,X3);





また、CATS関数にはもう1個便利な機能があります。SASYAMAさんが過去に紹介されてる以下を読んでください。
http://sas-tumesas.blogspot.jp/2015/06/cat.html

CATS関数に指定した変数が数値型の場合、BESTフォーマットを当てて文字値にしてくれるという機能を利用した小技ですね。
気をつけたいのが、BESTフォーマットに変換した値なので、元の変数値とは異なる(丸め処理が行われる)場合があることにご注意ください。

特に整数値を文字変換したい場合に使えます。
例えば「N=10」みたいな感じでN数を表す文字列を作りたい場合、以下Beforeのように書いていたところをAfterのように書けてしまいます。

Before
  length Y $20.;
  Y = "N=" || left(put(X,best.));


After
  length Y $20.;
  Y = cats("N=", X);





記事一覧

1.CATS関数

2017年5月7日日曜日

文字列の検索いろいろ:INDEX関数編



文字列の検索に使えるINDEX系の関数を紹介。



INDEX、INDEXC、INDEXW関数


シングルバイト(半角の英数など)の文字を対象とする場合、上記3つの関数を状況によって使い分けます。
(わりと気難しい関数なので、後段の解説や注意点も参照下さい)

data DT1;
     A  = "abc,def,ghi" ;
     B  = index( trimn(A) , "bc" );
     C  = indexc( trimn(A) , "ae" );
     D  = indexw( trimn(A) , "def" , "," );
run;
  A  
  B  
  C  
   
  abc,def,ghi    2  1  5


📝関数に指定した変数は、場合によって上記のように「TRIMN関数」で包む必要があります。理由はあとで解説します



解説

INDEX( trimn(A) , "bc")
第2引数の "bc" という文字列が変数Aの何バイト目に見つかったかを返す。
(見つからなかった場合は0を返す。)


INDEXC( trimn(A) , "ae" )
第2引数の "ae"を一字ずつ分解し、"a"または"e"という文字が変数Aの何バイト目に見つかったかを返す。
(見つからなかった場合は0を返す。)


INDEXW( trimn(A) , "def" , ",")
変数Aが、第3引数に指定したカンマ「,」区切りで格納されていると解釈される。
つまり変数Aの「abc,def,ghi」は「abc」「def」「ghi」という3つの単語があると解釈される。
この3つの単語のうち、第2引数の「def」と完全一致する単語が5バイト目に見つかったことを表しています。

また、例えば第3引数に「#@」と指定した場合「#」と「@」が区切り文字になります
(「#@」という一続きの文字列が区切り文字になるという意味ではない)

ちなみに変数Aが「abc, def, ghi」みたいに単語間にスペースが入る場合、「abc」「 def」「 ghi」という単語があると解釈し、第2引数の「def」と完全一致するものは無しと判定されてしまうので注意。

さらにもう1つ注意。第2引数の先頭または末尾に含まれる区切り文字は削除されます。
例えば「indexw( "abc@def@ghi", "@def@", "@" )」 は「indexw( "abc@def@ghi", "def", "@" )」という指定に置き換わる。




注意点
  • 日本語などのマルチバイト文字を含む場合は、以下記事の通りうまく動作しないのでご注意ください
  • 以下で解説している通り、場合によって「TRIMN関数」を併用する必要があります。
    • 文字値の末尾にある空白問題
    • 上記リンク記事で解説している通り、変数値の長さがLENGTHより短い場合、末尾に半角スペースが入るので、以下のように想定外の結果を返す場合があります。

/* 失敗例 */
data DT1;
     length A $20.;
     A  = "abc,def,ghi" ;
     B  = index( A , " " );
     C  = indexc( A , " " );
     D  = indexw( A , "ghi" , "," );
run;
  A  
  B  
  C  
   
  abc,def,ghi   12  12   0


てことで「TRIMN関数」で一時的に末尾の半角スペースを取り除いてやります。

/* 成功例 */
data DT1;
     length A $20.;
     A  = "abc,def,ghi" ;
     B  = index( trimn(A) , " " );
     C  = indexc( trimn(A) , " " );
     D  = indexw( trimn(A) , "ghi" , "," );
run;
  A  
  B  
  C  
   
  abc,def,ghi    0  0  9




KINDEX、KINDEXC関数


マルチバイト(日本語などの全角文字など)を含む文字を対象とする場合、頭に”K”がつく上記2つの関数を使います。

また上の方で解説した通り、
関数に指定した変数は、場合によって「TRIMN関数」で包む必要があります。

data DT2;
  A = "あいうえお";
  B = kindex( trimn(A),"うえ");
  C = kindexc( trimn(A),"あお");
run;
  A  
  B  
  C  
  あいうえお    3  1


解説

KINDEX( trimn(A) , "うえ")
第2引数の "うえ" という文字列が変数Aの何文字目に見つかったかを返す。
(見つからなかった場合は0を返す。)


KINDEXC( trimn(A) , "あお" )
第2引数の "あお"を一字ずつ分解し、"あ"または"お"という文字が変数Aの何文字目に見つかったかを返す。
(見つからなかった場合は0を返す。)



重要なポイント

頭に"K"の付く関数とそうでない関数の違いとして、、
INDEX系関数は何バイト目に見つかったかを返すのに対し、
KINDEX系関数は何文字目に見つかったかを返します。