ハッシュオブジェクトで、definedataメソッドの対象となるのがデータセット内の全変数である場合、全部列挙しなくても、all:'Y'が効くという話と、メソッドのkey指定の変数名とdefinekeyで定義している名前が違ってもいけるよという細かい話

名人戦に電王戦の最終局と目が離せませんね!SASしてる場合じゃないですね!

久しぶりの更新です。

さて、実は最近、ハッシュオブジェクトに関する質問が結構きます。
(コメントや掲示板も使っていただけると嬉しいですが)

日本でも遅ればせながら少しずつ普及してきているんでしょうか?
誰にも頼まれてないのにハッシュオブジェクトを日本に普及させようと目論む狂信者の僕としては結構嬉しいです。(海外のSASプログラマーの興味は既にDS2にいってるのかもしれませんが、、)

今までは割とざっくりと、こんなメソッドがあって、こんなことができますみたいな話が多かったのですが、少しずつ細かい部分についても書いていきたいと思います。

同じ結果を導くのに、結構書き方が何通りもあって、よく言えば柔軟性があり、悪く言えば紛らわしいんですね。

例えば

data Q1;
ID=1;A=1;B=2;C=3;D=4;E=5;F='A';output;
ID=2;A=2;B=3;C=4;D=4;E=5;F='B';output;
ID=4;A=3;B=4;C=5;D=4;E=5;F='C';output;
run;

というデータがあって、

data A1;
if 0 then set Q1;

declare hash h1(dataset:'Q1');
h1.definekey('ID') ;
h1.definedata('ID','A','B','C','D','E','F');
    h1.definedone();

do i = 1 to 5;
ID= i;
rc = h1.find();
if rc ^= 0 then do;
call missing(of ID--F);
end;
output;
end;

drop rc i;
run;

という処理を書くとします。
今回は文法のお話で
データにも、処理の内容にも特に意味はないのでデータセット内のキャプチャは省略です。
ハッシュ学習中の方は結果を予想してから実際に動かしてみてください。

上記のコードを書いていて、まず鬱陶しいのが、
h1.definedata('ID','A','B','C','D','E','F'); の部分ですね。
データステップと違って、クォートしてカンマでつないで指定なのがとても面倒です。

続いて

ID= i;
rc = h1.find();

のようにデータステップ中の変数とハッシュオブジェクトのkeyの変数名を合わせてから
空括弧でメソッドを指定するという書き方ももちろんOKなのですが、無駄に割り当てをしなくても
実は書くことができます。


その2点について改善したのが下のコードになります。

data A2;
if 0 then set Q1;

declare hash h1(dataset:'Q1');
h1.definekey('ID') ;
h1.definedata(all:'Y');
    h1.definedone();

do i = 1 to 5;
rc = h1.find(key : i);
if rc ^= 0 then do;
call missing(of ID--F);
end;
output;
end;

drop rc i;

run;

まず

h1.definedata(all:'Y'); の部分ですね。これによってIDからFまで全ての変数を指定したのと同じことになります。
ただし、注意なのはこの方法は
declare hash h1(dataset:'Q1'); のように、ハッシュオブジェクト定義時にdataset:で定義と同時にデータセットを取り込む書き方の場合しか使えません。
ハッシュオブジェクトはdeclare hash h1()のように空で作ってから、そのあとaddメソッドなどで中身をいれることもできますが、その場合definedataで全変数と言われても何の全変数やねん!となるので無理なわけです。

また、ついでにちょっと関係ない話ですが、上記の2つのコードとも if _N_ = 1 then がないのはなんで?と思われた方いらっしゃいますか?もしそう思われたら、結構ハッシュに慣れてますね。
ハッシュオブジェクトの定義は1ステップ内で1度行えばよく、1obs読み込むごとにやると効率が悪いのでif _N_ = 1 then do; end;の間にdeclareステートメント以下定義部分を入れることが多いのですが、今回はそもそもsetでデータセット読み込んでないので_N_=1しかないから、省略してるんですね。以上。

さて続いて

rc = h1.find(key : i); の部分に注目してみます。これは変数 i の値が動的に与えられ、それによってハッシュオブジェクト内のID変数が検索されるのですね。

ここでやりがちなのが
rc = h1.find(key : 'i'); とコーテーションで包んでしまうことです。ハッシュ定義時の指定がクォート方式なので大変ややこしいのですが、それをすると全く違う意味になってしまいます。

それは "i"という文字列で検索しろという命令になってしまいます。今回IDにiなんて文字は入ってませんし、そもそも数値型なので、エラーになってしまいます。

ハッシュオブジェクトのkeyが文字値の場合はエラーにならない分、余計にタチが悪く、こっちは動的に検索してるつもりが、実は全て固定値で検索してたっていうことになってしまいます。


さて、こう説明すると、だいたい次に聞かれるのが、じゃあ ID A C F とかって全部じゃなくて指定したい時はやっぱり一個一個、クォートしてコロンなの?という質問です。

基本的には、そうなんです!ということなんですが、一応以下のように書いて、all:'Y'に持ってくこともできます。データセットの指定にデータセットオプションが聞くので、オプションで絞ったうえで全部指定にすれば結果的に部分指定していることになるってことですね。


data A3;
if 0 then set Q1(keep=ID A C F);

declare hash h1(dataset:'Q1(keep=ID A C F)');
h1.definekey('ID') ;
h1.definedata(all:'Y');
    h1.definedone();

do i = 1 to 5;
rc = h1.find(key : i);
if rc ^= 0 then do;
call missing(of ID A C F);
end;
output;
end;

drop rc i ;

run; 


おわりです!

0 件のコメント:

コメントを投稿