今、以下のデータセットがあるとします。
data Q1;
X='ABCD';output;
X='BCBD';output;
X='ACAC';output;
X='ABAB';output;
X='ADCB';output;
run;
次に以下のデータセットをみます
data Q2;
Y='AB';Z=1;output;
Y='BC';Z=2;output;
Y='BD';Z=3;output;
Y='CA';Z=4;output;
run;
データセットQ1の1オブザベーション目のXは'ABCD'ですが、この文字列は
Q2のY='AB'とY='BC'を部分的に含んでいます。
このように片方のデータセットの文字列がもう一方に含まれている場合に結び付ける結合を考えてみます。
こういった結合になんか決まった名前あるんですかね?
SQLで、僕のレベルでぱっと思いつくのは、
proc sql noprint;
create table A1 as
select X,Y,Z
from Q1,Q2
where X contains Y;
quit;
って感じです。
結果は
パフォーマンスは置いといて、まあとりあえず直積作ってから
一方が一方に含まれているって抽出式です。単純明快。
この方法の結果について、少し気になるのが
Q1のX='ABAB'に対してQ2のY='AB'を1オブザベーションで出力しているところです。
出現回数を考慮するのであれば、ABが2回出現しているので、2オブザベーション出力ということになります。
さて、出現回数分オブザベーションを起こすとなると、SQLでやる場合、かなり難易度があがりそうです(すみません、ノ―アイデアでした。分かる方、コメントお願いします)
まあ、SQLで難しいならデータステップでやりましょう。
data A2;
set Q1;
do i=1 to Q2OBS;
set Q2 nobs=Q2OBS point=i;
do j=1 to count(X,Y);
output;
end;
end;
drop j;
run;
で結果は
です。狙い通りABが2obs起きてますね
直積については
「詰めSAS4回目_直積(デカルト積、単純結合)を作成する」
http://sas-tumesas.blogspot.jp/2013/09/sas4.html
基本的に、なんかわけわからん条件がついた結合は、直積作ってからの条件式や、ループで、
だいたいケリがつきます。データが巨大になってくると、考え物ですが。
よくあるのが、文字列の類似度をスコア化して、一定水準以上の類似が見られる場合は
キーが一致したとみなして結合するような、キー一致条件緩和タイプのマージ処理です。
実践例としては、臨床開発ならフリーテキストで収集された薬剤名に薬剤辞書のコードをふる際の
候補レコードを出すような、まあ名寄せ処理的なケースですね。
基本、今回のコードに
「データステップ100満開 文字列がどれくらい類似しているかを定量化する」
http://sas-tumesas.blogspot.jp/2014/03/100.html
の内容を合わせてやれば簡単です。
以上です。
ちなみにもう9月ですが、ちょうどこのブログを始めたのが昨年の9月だったので
あっという間に1年です。
1-2カ月でネタ切れ閉店の予定が思いのほか長く続いてます
ATTRN関数でwhere条件にかかったオブザベーション数を取得する
オブザベーション数をマクロ変数に格納する方法から、SCL関数を僕が勉強していく話
http://sas-tumesas.blogspot.jp/2014/03/scl.html
でATTRN関数にNOBSを指定してオブザベーション数を取得する話を紹介しました。
関連する話題として、open関数にwhereデータセットオプションをくっつけた場合について
紹介します。
http://sas-tumesas.blogspot.jp/2014/03/scl.html
でATTRN関数にNOBSを指定してオブザベーション数を取得する話を紹介しました。
関連する話題として、open関数にwhereデータセットオプションをくっつけた場合について
紹介します。
data A;
do X=1 to 10;
output;
end;
run;
data A1;
DSID=open(("A(where=((X > 3))"),'i');
NOBS=attrn(dsid,"NOBS");
NLOBSF=attrn(dsid,"NLOBSF");
run;
といった感じでNOBSオプションはあくまでデータセットのオブザベーション数を返し
NLOBSFとすると、実際に抽出されるオブザベーション数がとれます。
例の場合3レコードが抽出でかからなかったということがよくわかります。
で、最後に、ご存知の方がいらっしゃればご教示いただきたいのですが
NOBS NLOBSF 以外にNLOBSというのも指定できるのですが、Helpを読んだりしても使い方が
よくわかりませんでした。
SET;SET;で最大のOBSまで、足りないデータセットの最終オブザベーションを引き延ばす。
僕の中では、革命的発見です。
前の投稿で、なぜSET;SET;なのにif _N_=1で処理の終了を回避できるのかという僕の投げっぱなしの質問に対して、コメントをいただきました。
それは、SASがファイル終端を検知するのは、最終OBSを読み込んだ時点ではなく、その次を読み込もうとしてそれ以上OBSがないとなった時点ではないかと。だとするとデータセットが1obsならファイル終端が検知されるのは_N_=2で処理が行われたその時なので、それを回避すれば、処理は止まらず、値は保持されたまま引き延ばされると。
ならば、ならばですが
今、以下の3つのデータセットがあったとします。
data Q1;
do X=1 to 10;
output;
end;
run;
data Q2;
do Y=1 to 5;
output;
end;
run;
data Q3;
do Z=1 to 3;
output;
end;
run;
前の投稿で、なぜSET;SET;なのにif _N_=1で処理の終了を回避できるのかという僕の投げっぱなしの質問に対して、コメントをいただきました。
それは、SASがファイル終端を検知するのは、最終OBSを読み込んだ時点ではなく、その次を読み込もうとしてそれ以上OBSがないとなった時点ではないかと。だとするとデータセットが1obsならファイル終端が検知されるのは_N_=2で処理が行われたその時なので、それを回避すれば、処理は止まらず、値は保持されたまま引き延ばされると。
ならば、ならばですが
今、以下の3つのデータセットがあったとします。
data Q1;
do X=1 to 10;
output;
end;
run;
data Q2;
do Y=1 to 5;
output;
end;
run;
data Q3;
do Z=1 to 3;
output;
end;
run;
これで、以下のコードを実行すればどうなるか
data A1;
set Q1;
if _N_<=tobs2 then set Q2 nobs=tobs2;
if _N_<=tobs3 then set Q3 nobs=tobs3;
run;
以下の結果になります。
10obsに足りない部分は、一番最後の値で引き延ばされて補完されています。
これは結構使えそうじゃないですか?
コメント有難うございます。
久しぶりにとても清々しい気持ちになれました。
データステップ楽しい
詰めSAS8回目_循環するLAG処理(1つ前のOBSの値を取得するとともに最初のOBSについては最終OBSの値を持つようにする)
例えば
data Q1;
do X=1 to 10;
output;
end;
run;
のようなデータセットがあったとして、そこから、
ひとつ前のオブザベーションの値を変数「LX」にいれる、ただし先頭については
ひとつ前のオブザベーションが存在しないので、循環して最後の値をとってくるという
問題があったとします。
目的とするデータセットは以下の形です。
↓【目的局面図】
さて、どう解くのが最短でしょうか?
ちなみに1手詰めです(1ステップで処理できる)
まず、LAG関数が第一感で思い浮かぶ方が多いかと思います。
data A;
set Q1;
LX=lag(x);
run;
しかし、これだと循環が表現できません。
当然こうなります。
LAG関数の使用は、今回はドツボ行きです
【解法】
data A1;
set Q1 Q1 nobs=tobs;
LX=X;
if _N_<tobs/2 then delete;
set Q1;
run;
nobs=やend=はsetで複数のデータセットを指定した際にまとめての値しかだせません。
なので上記でのnobsの中身は20です。
そこでサブセット化IFで1-9番目のオブザベーションをdeleteしているので
この時点で残っているのは10,11,12,13,14,15,16,17,18,19,20の11オブザベーション
つまり、ケツの1オブザベーションを頭に乗っけた形で11obsのデータセットが一時的に
できているわけです。
そこで、さらにSetを打つわけです。
おさらいですが、Set;Set;は一度でもファイル終端をどこかのデータセットで見つけたら
処理が打ち切られるので、結果として指定されたデータセット中、最小のオブザベーション数で
まとめられるという性質を持っています。
11obsと10obsをSet Setしているわけなので、作成されるデータセットは10obsです。
説明が下手なので、もしピンと来なければ実際に動かしてみてください。
data Q1;
do X=1 to 10;
output;
end;
run;
のようなデータセットがあったとして、そこから、
ひとつ前のオブザベーションの値を変数「LX」にいれる、ただし先頭については
ひとつ前のオブザベーションが存在しないので、循環して最後の値をとってくるという
問題があったとします。
目的とするデータセットは以下の形です。
↓【目的局面図】
さて、どう解くのが最短でしょうか?
ちなみに1手詰めです(1ステップで処理できる)
まず、LAG関数が第一感で思い浮かぶ方が多いかと思います。
data A;
set Q1;
LX=lag(x);
run;
しかし、これだと循環が表現できません。
当然こうなります。
LAG関数の使用は、今回はドツボ行きです
【解法】
data A1;
set Q1 Q1 nobs=tobs;
LX=X;
if _N_<tobs/2 then delete;
set Q1;
run;
nobs=やend=はsetで複数のデータセットを指定した際にまとめての値しかだせません。
なので上記でのnobsの中身は20です。
そこでサブセット化IFで1-9番目のオブザベーションをdeleteしているので
この時点で残っているのは10,11,12,13,14,15,16,17,18,19,20の11オブザベーション
つまり、ケツの1オブザベーションを頭に乗っけた形で11obsのデータセットが一時的に
できているわけです。
そこで、さらにSetを打つわけです。
おさらいですが、Set;Set;は一度でもファイル終端をどこかのデータセットで見つけたら
処理が打ち切られるので、結果として指定されたデータセット中、最小のオブザベーション数で
まとめられるという性質を持っています。
11obsと10obsをSet Setしているわけなので、作成されるデータセットは10obsです。
説明が下手なので、もしピンと来なければ実際に動かしてみてください。
【訂正追補】SASデータセットのオブザベーション数をマクロ変数に格納する方法_call symput
前回の投稿で、データセットのobs数をマクロ変数に格納する際に
data _NULL_;
set A end=eof;
if eof then call symputx('obs1',_N_);
run;
を提示し、問題点として、データセットが空であった場合、マクロ変数が作成されず
0がとれないと挙げたのですが、
data _NULL_;
call symputx('obs',tobs);
if 0 then set A nobs=tobs;
stop;
run;
このようにすればいいのではないかとご指摘をいただきました。
その通りでした。素晴らしいです。
この場合、1行も読み込むことなくマクロ変数を作成しているので
元データセットが巨大で、obs数をとりたいだけの場合などに
余計な読み込みで処理時間を無駄にすることもなく最強です。
data _NULL_で_N_使うのは確かに無駄でした。反省します。
nobs=で任意の変数にデータセットのobs数を格納することができるのですが
どうもこれ、データステップの開始の時点で格納されるらしく、setステートメントの前、
というかコードの先頭ですでに取得可能なんですね、面妖な、、。
if 0 はif _N_=0 などでもよく、ようは絶対に実行されないif文ですね。
絶対に実行されないif文は、実行はさせないけど、データの定義情報(ディスクリプタ部)を
つかいたい時の方法ですね。知らないと、何してんだこいつ的コードですが。
Sさん有難うございます。
でも会社で、おっきな声でブログが、って言わないでください、、、。
data _NULL_;
set A end=eof;
if eof then call symputx('obs1',_N_);
run;
を提示し、問題点として、データセットが空であった場合、マクロ変数が作成されず
0がとれないと挙げたのですが、
data _NULL_;
call symputx('obs',tobs);
if 0 then set A nobs=tobs;
stop;
run;
このようにすればいいのではないかとご指摘をいただきました。
その通りでした。素晴らしいです。
この場合、1行も読み込むことなくマクロ変数を作成しているので
元データセットが巨大で、obs数をとりたいだけの場合などに
余計な読み込みで処理時間を無駄にすることもなく最強です。
data _NULL_で_N_使うのは確かに無駄でした。反省します。
nobs=で任意の変数にデータセットのobs数を格納することができるのですが
どうもこれ、データステップの開始の時点で格納されるらしく、setステートメントの前、
というかコードの先頭ですでに取得可能なんですね、面妖な、、。
if 0 はif _N_=0 などでもよく、ようは絶対に実行されないif文ですね。
絶対に実行されないif文は、実行はさせないけど、データの定義情報(ディスクリプタ部)を
つかいたい時の方法ですね。知らないと、何してんだこいつ的コードですが。
Sさん有難うございます。
でも会社で、おっきな声でブログが、って言わないでください、、、。
詰めSAS4回目_直積(デカルト積、単純結合)を作成する
直積(デカルト積、単純結合)はデータセット同士の全オブザベーションの組み合わせのようなもので3obsのデータセットと4obsのデータセットの直積をとると12obsになります。
実践で使用する機会がそれほど多いとは思いませんが、たまにあったり、また直積を作った方がスムーズに以後の処理ができることもあります。
問題は
data Q1;
input A B;
cards;
1 2
3 4
5 6
;
run;
【データセットQ1】

data Q2;
input C $ D $;
cards;
A B
C D
E F
;
run;
【データセットQ2】
実践で使用する機会がそれほど多いとは思いませんが、たまにあったり、また直積を作った方がスムーズに以後の処理ができることもあります。
問題は
data Q1;
input A B;
cards;
1 2
3 4
5 6
;
run;
【データセットQ1】
data Q2;
input C $ D $;
cards;
A B
C D
E F
;
run;
【データセットQ2】
の二つから
のようなデータセットを作ることです。
【解法1】
data A1;
set Q1;
do i=1 to Q2OBS;
set Q2 nobs=Q2OBS point=i;
output;
end;
run;
まずはデータステップで詰みです。nobs=オプションはデータセットの総obs数をとれるのですが
それをsetステートメントの前に使用できるというのは意外です。それをループ終点にして
point=でダイレクトアクセスする度に明示的にOutputすることで、Q1のSetによって、Q1が1obs読まれる度にi=1からQ2の最後のobsまでOutputされ、それがQ1のSetが全obs完了するまで行われるので結果として直積が作成されます。2つのデータセットのサイズによって変わりますが
基本的にはこれが最も効率の良い作成法だと思われます。コードもコンパクトにまとまってます。
【解法2】
proc sql noprint;
create table A2 as
select *
from Q1,Q2;
quit;
直積はSQLの基本ともいえるので(単純結合というぐらいですし)、SQLで詰ませます。
cross join等の方が綺麗な書き方なのかもしれませんが、とりあえず一番単純な書き方で実現です。SQLを使う場合の注意点は、ログにnoteとしてデカルト積が生成されたことを知らせるメッセージが必ずでるので、ログの内容に厳格に注意を払う仕事では留意する必要があります。
【解法3】
data A3;
if _N_=0 then do;
set Q1 Q2;
end;
if _N_=1 then do;
declare hash hq(dataset:'Q2');
declare hiter hi('hq');
hq.definekey('C','D');
hq.definedata('C','D');
hq.definedone();
end;
set Q1;
rc=hi.first();
if rc=0 then output;
do while(rc=0);
rc=hi.next();
if rc=0 then output;
end;
drop rc;
run;
ハッシュ反復子でやってみました。ハッシュはメモリにデータを展開するので、作成が高速化するか
と思ってやったのですが、コードが悪いのか、いくつかテストした条件ではデータステップやSQLの
1.5~2倍遅い結果しかだせていません。効率悪いはコードは長くて意味不明だわでいいとこなしな
感じですが、恐らくQ1がかなり巨大でQ2もそこそこの大きさのデータセットにして、オプションで
ハッシュの割当サイズを適度にすれば、速くなったりする気がします。いや、そもそも直積にハッシュはあまり意味がないのか。すみません、勉強中です。
登録:
コメント (Atom)







