ラベル do over の投稿を表示しています。 すべての投稿を表示
ラベル do over の投稿を表示しています。 すべての投稿を表示

あったらいい関数やルーチンがないから取り敢えずこう書いてますの話

今回は僕が日頃感じることの多いSAS関数とコールルーチンへの不満についてです。
といっても別にそんな大したことじゃなく
普通に提供されてそうな機能なのに、実際なくて困るからとりあえず僕はこう書いてるけど、
みなさん普段どうしてるんだろうってことを紹介する話です。

なので、え?それは○○関数でできるよ、とか9.3とか4で引数追加されてできるようになったたよとか
あれば是非、てめえは間違っているとご指摘ください。


まずはデータ

data Q1;
length A B C D $10.;
A='い';B='ろ';C='は';D='に';output;
A=''  ;B=''  ;C='は';D='に';output;
A=''  ;B=''  ;C=''  ;D=''  ;output;
A='い';B='ろ';C=''  ;D=''  ;output;
A='ろ';B='ろ';C='ろ';D='ろ';output;
run;












まず一番、よくあるのが、指定した複数の文字値の中で、欠損でないものの数をカウントしたいというケースです。
数値型であればn関数があります。欠損値であれば、数値文字両方対応のcmissがあります。
ただ、文字型の非欠損値のカウントがないんですよね。
で、SAS公式もそのことはわかっているので、以下のように案内してます。
http://support.sas.com/kb/46/235.html

でも、紹介されてる代替コードが(僕としては)微妙すぎ。
文字変数の数から欠損値の数ひけば、非欠損値の数でしょっていう、まあそりゃそうですけど
わざわざ複数ステップで書くの面倒だし、全文字変数が対象じゃなくて、一部の変数を対象にする場合
絞りをいれる必要のあるプログラムなので、ちょっとって感じです。
今後のバージョンでそういう関数を追加します的なことも書いてますが、今現在ないと思うので、いつになるやら。

なので、僕は以下のように書いてます。

data A1;
set Q1;
array AR $ B C D;
CN=0;
do over AR;
 CN + ^missing(AR);
end;
run;












本当はfcmpでそういう関数作りたかったんですが、現状のfcmpだと引数に
temporary配列しかとれないので、結構使い勝手が悪いんですよね。

リンクサイト kickstamp の
【SAS】PROC FCMPプロシージャーでの可変長引数の利用について

http://kickstamp.hatenablog.com/entry/2013/07/18/124954

で、紹介されているようにマクロ噛ませばできるんですが、たかが非欠損値数えるだけで
そこまでするのも何か負けた感じで嫌だな~って。

次に、これは文字型に限った話じゃないんですが、指定した複数の変数の中で条件に合致するものの数が欲しいというケースです。excelのcountif関数みたいなイメージです。
縦にそういうことしたい場合はsqlとかでいいんですが、横にだしたい時も多いので困ります。

ので僕は以下のようにしています

data A2;
set Q1;
array AR $ B C D;
CN=0;
do over AR;
 CN + AR in ('ろ');
end;
run;











最後に同じような内容ですが、以前掲示板にも書いたことがあるのですが
http://tumesas.progoo.com/bbs/tumesas_topic_pr_39.html

複数変数を対象に特定の値を他の値に変換したいという処理です。
とりあえず、今は以下のように書いてます。

data A3;
set Q1;
array AR $ B C D;
do over AR;
 AR = ifc(AR='ろ','は',AR);
end;
run;










つまり今は関数やルーチンがない場合、だいたい配列&ループ処理でまかなっているわけです。
ただ基本的に僕の考え方として、配列もマクロもハッシュオブジェクトも、或いはプロシジャによる処理も極力いれずに、ありものの関数やルーチンの
組み合わせで解決できることは、極力そうしたいと思ってます。(割に変なコードばっか書いてますが…)
まあ、あんまり関数のお化けみたいなコードになるぐらいなら、素直に配列書きますけど、用意された機能でシンプルにできるならそれに越したことはないと思ってます。

数値(文字)変数にのみ一括で特定の値を割り当てる話

直接ご質問いただいた内容へのご回答になります。

データセット中の文字型変数の値はそのままで、数値型変数のみ全て1に置き換える処理を書きたいのですが、指定するデータセットに含まれる数値変数の数や名前は可変で、全てが文字型変数であることも考えられる状況です。エラーにならない方法を教えてください。
また、欠損値にする方法もあれば併せて教えてください。

といった内容でした。

多分、他にも方法ありそうなんですが、僕がぱっと思いついたのは

data Q1;
X=2;Y='A';Z=3; output;
X=1;Y='B';Z=4; output;
run;






といったデータセットがあったとして

data A1;
 set Q1;
 array AR _numeric_;
 do over AR;
  AR=1;
 end;
run;






でいかがでしょうか?数値変数のX Zだけ値が変わってます。

「非明示添字配列と do over LOOPの利用」
http://sas-tumesas.blogspot.jp/search/label/do%20over

で紹介した方法を応用しています。

_numeric_(省略形は、_num_)はデータセット中の全ての数値変数、
_character_(省略形は、_char_)は文字変数、
_all_は文字通り全てです。
ちなみに、仮に全てが文字型変数で、数値型がなかったとしてもエラーにはなりません
「WARNING: 要素なしで配列を定義します。」はでますが。



次に、欠損値ということなら、配列組むまでもなく

data A2;
set Q1;
dummy=.;
call missing (of _numeric_);
drop dummy;
run;

でいけます。

dummyをかましているには、この場合は、数値変数が全くないと、引数不足でcall missingが
エラーになっちゃうからです。








非明示添字配列と do over LOOPの利用

ARRAYステートメントを初めて覚える時、
 
array 配列名 {配列要素数} 以下、$や長さや配列要素内容や初期値といった感じで、

とりあえず array 配列名 {配列要素数} までは鉄板と覚えてしまいがちですが
それは正確には明示添字配列の定義法であり、
実は{配列要素数}を省略して定義することができます。

例えば、以下のようなデータセットがあっととします

data Q1;
X=1;Y=2;Z=3;output;
run;





XからZまでを配列として、配列要素全てに+1をする場合、
書き方はいくらでもありますが、例えば

data A0;
 set Q1;
 array AR{*} X--Z;
 do i=1 to dim(AR);
  AR{i}=AR{i}+1;
 end;
 drop i;
run;





と書いたとします。
ここでは{*}と要素数を定数にしていませんが、do to loopの終端条件で
dim関数でARの要素数を取得しているので問題ありません。

ただ、よくよく考えると、配列を定義して、その配列全部におんなじ処理を一括で
かける今回のような場合、要素番号ってあんまり役にたってません。

そこで実は以下のようなコードが成立します。

data A1;
 set Q1;
 array AR X--Z;
 do over AR;
  AR=AR+1;output;
 end;
run;

結果は同じです。
 array AR X--Z;と要素番号定義をすっとばしています。
 do i=1 to dim(AR);が do over AR;となっています。do overは全要素に対して順にループするという
 do i=1 to dim(AR);と同義になります。

ちなみに明示添字配列で定義した配列にdo overを使用すると以下のエラーメッセージです。




「ERROR: 明示添字配列に DO OVER ステートメントは使用できません。」

ただし、たとえば以下のコードのように非明示添字配列で定義しても
要素番号処理は裏でちゃんと勝手に行われているので

data A2;
 set Q1;
 array AR X--Z;
  A=AR{1};
  B=dim(AR);
run;




エラーになったりはしません。