Nオブザベーション置きにデータを抜き取って新しいデータセットをつくる_SETとdoループで

今回もコメントでいただいた内容からです。

今、以下のような20オブザベーションのデータセットがあるとします。

data Q1;
 do X=1 to 20;
  output;
 end;
run;


ここから3obsごとに順番にオブザベーションを抽出したい場合、どうしますか?

data A0;
 set Q1;
 if mod(_N_,3)^=0 then delete;
run;

でも詰みですが、以下のコードを見てください。

data A1;
 do i=1 to 3;
  set Q1;
 end;
run;

え?なにこれな感じですが、これを実行すると結果が



となってiをdropすると、まさに3obsおきに抽出できていることになります。

data A2;
 do i=1 to 4;
  set Q1;
 end;
run;

to の後を4にするとどうなるか


4つおきに抽出できます。

なんのこっちゃらいです。

なぜ上記のようなコードで、このような結果になるかを考えます。
3obsおきの例で考えます。

さて

data A3;
 do i=1 to 3;
  set Q1;
  output;
 end;
run;

として、ループの中に明示的なoutputをいれてみます。
すると結果は


20obsが出力されます。「i」に注目すると1-3を繰り返しています。

さて、次に

data A4;
 do i=1 to 3;
  set Q1;
  output;
 end;
output;
run;

としてループの外にoutputをおいてみます。
すると



26オブザベーションの結果が返ってきます。
iの値に注目してください「4」がでてきていますね。
i=4で抽出するとどうなりますか?


となって最初に作った結果と同じですね。

もう、だいたい見えてきましたね。
つまりループの中ではoutputがおきないので、そこで値を上書いて
抽出間隔+1でループを抜けた後(値は最後のが保持)に暗黙のOUTPUTを利用して
出力しているために、結果としてi=○○のdo ループでSETを包むと
○○おきにデータを抽出することと同義になるのですね。

奥が深いですね。




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;



これで、以下のコードを実行すればどうなるか

data A1;
 set Q1;
 if _N_<=tobs2 then set Q2 nobs=tobs2;
 if _N_<=tobs3 then set Q3 nobs=tobs3;
run;

以下の結果になります。


10obsに足りない部分は、一番最後の値で引き延ばされて補完されています。
これは結構使えそうじゃないですか?

コメント有難うございます。

久しぶりにとても清々しい気持ちになれました。
データステップ楽しい




1obsのデータセットの値を他のデータセットに対して引き延ばして結合する

今回は、僕が理屈を理解できていないことについて書きます。
もし説明してくださる方がいらっしゃればコメント等お願いいたします。

以下のように、10obsのデータセットと1obsのデータセットがあるとします。

data Q1;
do X=1 to 10;
 output;
end;
run;














data Q2;
 Y=5; Z=9;
 output;
run;



これに対して

data A1;
 set Q1;
 set Q2;
run;

を実行すると


結果は上記のようになります。
仮に

data A1;
 set Q2;
 set Q1;
run;

と順番を入れ替えても結果は同じです。

これについての理屈はわかります。
SET;SET;はいずれかのデータセットのファイル終端を見つけた時点でオブザベーションの
作成を打ち切るので、結果、常に指定データセットたちの中で最小のオブザベーションに
なる。ということだと思います。

しかし、

data A2;
 set Q1;
 if _N_=1 then set Q2;
run;

として実行すると結果は


となり、1obsの内容が全てに引き延ばされます。

ちなみに

data A2;
 if _N_=1 then set Q2;
  set Q1;
 run;

としても結果は同じです。

このテクニック自体は、SAS社のステートメントリファレンスでも紹介されており
以前から知っていて、何度も使ったことがあるのですが
ふと考えてみると、理屈が説明できない自分に気付きました。

やっていることはSET;SET;なわけで仮に_N_=1でしか実行されないとしても
実行された時点でQ2の1obsを読み込んだ後ファイル終点を見つけて処理が
止まって、完成は1obsになるのでは?と思ってしまいます。
PDVを理解できていない証拠なのですが、どなたか、なぜ完成が10obsになるか
を説明していただけないでしょうか?





【訂正補足】coalesceとcoalescec

間違えました。訂正です。

UPDATEステートメントを使ってみる の中でSQL文


proc sql noprint;
 create table UP3 as
  select coalesce(TRA.X,MAS.X) as X
              ,coalescec(TRA.Y,MAS.Y) as Y
  from MAS full outer join TRA 
           on MAS.X=TRA.X;
quit;

を紹介して
数値にはcoalesce、文字にはcoalescecと書いたのですが、
それはデータステップ内で使用する場合でした。

もちろん上記コードで間違いなく実行できるのですが
SQLプロシジャ内で使用する場合、coalesce関数は本来、標準SQLで型を問わず使用できる
関数なので、それが反映され

proc sql noprint;
 create table UP3 as
  select coalesce(TRA.X,MAS.X) as X
              ,coalesce(TRA.Y,MAS.Y) as Y
  from MAS full outer join TRA 
           on MAS.X=TRA.X;
quit;

のように「c」をつける必要ないのでした。

うかつでした。

UPDATEステートメントを使ってみる

SASで結合といえば、なんだかんだいってやっぱりMergeステートメントが幅を利かせています。
書きようによって、多対多のようなケースを除いて、ほぼ全ての結合を表現できます。
Merge最高!Merge万歳!
が、場合によっては、UPDATEステートメントを使用した方が見通しがよい場合もあるので紹介します。
ちなみに僕はMergeステートメントあまり好きでありません

今データセット「MAS」と「TRA」があったとします。
UPDATEステートメントはマスタ―データセットとトランスザクションデータセットという考え方を持ち、
マスターをトランザクションで更新するといった機能になります。

data MAS;
 X=1;Y='あ';output;
 X=2;Y='ろ';output;
 X=3;Y='';output;
 X=4;Y='に';output;
run;

【MAS】


data TRA;
 X=1;Y='い';output;
 X=2;Y='';output;
 X=3;Y='は';output;
 X=5;Y='ほ';output;
run;

【TRA】







とりあえず X でソートします。
proc sort data=MAS;
 by X;
run;
proc sort data=TRA;
 by X;
run;



上の二つから以下の結果が欲しいとします。








変数Xをキーにして単純にMergeで結合するとTRAの変数Yがnullのため
結果のX=2に対応するYはnullになります。TRAにwhereでY^=''をつければいいのですが
そんなひと手間かけなくても

data UP;
 update MAS
        TRA;
     by X;
run;

で詰みです。
UPDATEステートメントはトランザクションデータセットにおいて値がnullの場合は
マスターデータセットの該当変数を上書きしないという付加機能を有するわけです。

ちなみに

data UP2;
 update MAS
        TRA updatemode=nomissingcheck;
     by X;
run;

のようにupdatemode=nomissingcheckとすると欠損値でも更新するので
すなわち

data MG;
 merge MAS
        TRA;
     by X;
run;

と同じになります。

ちなみにUPDATEと同様の表現が可能なMODIFYステートメントがいるのですが、
こいつは多機能で、深いので、いつか勉強してから紹介します。
LIBNAME EXCELで値とエクセルにだす時以外、あんまり使ったことないので、、

ちなみに今回のケース、SQLなら

proc sql noprint;
 create table UP3 as
  select coalesce(TRA.X,MAS.X) as X
              ,coalesce(TRA.Y,MAS.Y) as Y
  from MAS full outer join TRA 
           on MAS.X=TRA.X;
quit;


こんな感じでしょうか?
僕の好きなcoalesce関数です。coalesceの順番がトランザクション→マスターなところが味噌ですか。
あとfull outer joinは両側外部結合です。どっちかにでもあればとってくるよというやつです





横方向(列方向)にcall sortn,call sortcルーチンを使ってソート処理をかける

SASで並び替えというと、縦方向(行方向)、つまりオブザベーションの値を以て並び替える処理が通常で、あまり横にソートするということは多くないと思われます。
ARRAYで

たとえば以下のようなデータセット

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


があったとして
3つの変数の値の大小を比較して一番小さい値をX、2番目をY、、というように
値を並び替えたいとします。
つまり、欲しい結果は以下のようになります。


この場合


data A1;
 set Q1;
 call sortn(of X--Z);
run;

で詰みです。
CALL SORTNルーチンは数値変数を指定することで、値によって
変数の中身をソートしてくれます。


また次のデータセットのように値が文字型の場合はどうでしょうか

data Q2;
length X Y Z $2.;
 X='B';Y='A';Z='C';output;
 X='さ';Y='か';Z='あ';output;
 X='';Y='D';Z='-';output;
run;


この場合はCALL SORTCルーチンを利用して以下のように書けます。

data A2;
 set Q2;
 call sortc(of X--Z);
run;

すると実行結果は


となります。


これを知らないと、ARRAYを使ってループで大小比較したり、
TRANSPOSEしてSORTしてTRANSPOSEして戻すみたいな少し面倒な処理をやってしまいがちです。





indsname オプションで、由来元のデータセット名を取得する

9.2になって追加された機能で、これが一番凄い!と何回も言っていて狼少年状態です
例えば

・proc transposeのidステートメントに複数変数を指定可になったのが最高!!
・データセット名にコロンモディファイアで前方一致をかけれるようになったのが最高!!

多分、他にも言っていた気がします。

でも、今回紹介するindsnameも負けず劣らず使える機能です。
例えば以下のような2つのデータセットA Bがあったとします。

data A;
 do X=1 to 5;
  output;
 end;
run;

data B;
 do X=1 to 5;
  output;
 end;
run;

そこから、データを縦結合して
各オブザベーションの由来元がわかるようにする場合

data C;
 set A(in=ina)
     B(in=inb);
 if ina=1 then DS='A';
 if inb=1 then DS='B';
run;


みたいなコード書いていたと思います。 多分。

それがINDSNAMEオプションを使えば

data D;
 set A B indsname=inds;
  DS=inds;
run;
 


となります。

これは結構凄くないですか?ライブラリ名までとれます。
デバックする時やコードレビュー等で、
一体このオブザベーションはどっから来てんだ?みたいなのを手繰っていきたい時ってあると思います。
もしかしたら、長い人生のうち
導出されたデータセットと導出プログラムはあるが、導出仕様書がないといった
悲惨な現場に遭遇することもあるかもしれません。

そんな時に凄い良いです。

これもっとテクニカルニュースとかで取り上げていいんじゃないでしょうか?
僕は海外のコードを見ていて初めて知りました。
日本のSASの情報環境、ちょっとやばいですよね、なんとかしないと









コロンモディファイアでデータセットオプションを使うと全部にかかる

今回の内容はあまり大したことない話です。
「:」を使った前方一致でデータセットを一括りに処理できるという話を以前しましたが

それに対して、括弧でin=とか keep=とかいわいるデータセットオプションを
つけたらどうなるのか考えたことがなかったので
実際やってみた話です。

data X_1;
do X=1 to 2;
 output;
end;
run;

data X_2;
do X=3 to 4;
 output;
end;
run;

data X_3;
do X=5 to 6;
 output;
end;
run;

data Y_1;
do Y=1 to 2;
 output;
end;
run;

data Y_2;
do Y=3 to 4;
 output;
end;
run;

のようにX_から始まるデータセットが3つ、Y_から始まるデータセットが2つあって
今、新規にAというデータセットを作成する場合、データがXから始まるデータセットから
読まれたものかYからかをフラグ変数を作って判別したいと思います。
導出元のデータセット名を直接取得する方法については次の投稿で紹介します

目的局面は


となります


その場合、以下で詰みなのです。

data A;
 set X_:(in=inx)
      Y_:(in=iny)
 ;
 FROMX=inx;
 FROMY=iny;
run;

これはつまり、データセットオプションが対象となるデータセット全てに分配されたイメージ
要は

data A;
 set X_1(in=inx)
      X_2(in=inx)
      X_3(in=inx)
      Y_1(in=iny)
      Y_2(in=iny)
 ;
 FROMX=inx;
 FROMY=iny;
run;

と同じになったいうわけです。

なので例えば、renameやwhereなんかも同様で

data B;
 set X_:(rename=(X=_X) where=(_X>2));
run;

なら結果は


となります。



SQLで削除や更新されたオブザベーション数を取得する。&SQLOBSは守備範囲が広い

以前「SASデータセットのオブザベーション数をマクロ変数に格納する方法」についてcall symputxやSQLのselect into:を紹介したのですが、もしデータセットがSQLのcreate table文で作成されたものであれば、自動マクロ変数の「&SQLOBS」が有効ではないかとコメントをいただきました。

コメント有難うございます!!
(コメントやご意見・ご感想、こういうところを取り上げてほしいとか
こういう処理はどうやっているかなどの相談でもいいので、いつでも募集しています)

その通りだと思います。

data A;
do X=1 to 10;
 output;
end;
run;

今10オブザベーションのデータセット「A」があり
以下のコードでA1を作成した後、&SQLOBSを展開すると
その中には、A1のオブザベーション数が入っています。
特別なコード書いてないのに、勝手に入ってます。
なんと素敵な。

proc sql;
 create table A1 as
  select X
  from A
  where X>3;
quit;

%put &SQLOBS;

それで、ついでに人の褌で今回のネタにさせていただいちゃうのですが
&SQLOBSのいいのは、こいつの性質が「SQLで最後に処理された行数が格納される」という
ちょっと曖昧な感じなところです。

処理されたということは、要は削除したり、更新したりしたすることも含まれるのですね。

ごくごくたまに、何オブザベーションが削除されたのか、また何オブザベーションが条件に合致して更新されたかなどを取得したい時が、ありますが、それがタダで手に入るのであればそんな素晴らしい話はないですよね。

要するに

proc sql;
 delete from A
 where X<5;
quit;

%put &SQLOBS;

なら、4となり。

data B;
do X=1 to 10;
 output;
end;
run;

proc sql;
update B
 set X=X*1.5
 where X<3;
quit;

%put &SQLOBS;

なら、2となるのです。

SQLとSASの機能の合わせ技は、どうしてもこうも心をくすぐるのでしょうか?

放言するならば、
SQLを知らないとSASのデータステッププログラミングの面白さの4割くらい損してるんじゃないかと僕は思います。




外部ファイルへのODS出力のページ設定、縦横

何回も忘れてしまうので、自分の備忘録として書きます。

rtfやpdfに出力するときのページ縦横設定のオプションです。
portraitとかlandscapeとかいわれてもピンとこないです。hとかvでいいのに。



【縦ページ設定】
options orientation=portrait;
ods pdf;
proc gslide;
  note h = 5
  j = c c = red 'ページ設定 縦は'
  j = c c = red 'orientation=portrait'
  ;
run;
quit;
ods pdf close;

【横ページ設定】
options orientation=landscape;
ods pdf;
proc gslide;
  note h = 5
  j = c c = red 'ページ設定 横は'
  j = c c = red 'orientation=landscape'
  ;
run;
quit;
ods pdf close;

EXCEL関数をSASの中で使いたい時、運がよければSASHELPにデフォルトでユーザー定義関数として用意されている可能性があるので使わない手はないという話

たまにEXCELでの○○関数と同じ働きのSAS関数ないかなぁ、まあ思いつかないし、マクロ(またはFCMPプロシジャで作成するユーザー定義関数)にするか~、見たいな時ってありますよね。

実はSASの中にいくつかのEXCEL関数が用意されているのですが、そのままでは使えません。

まず
「ソリューション」→「データ解析」→「FCmp Function Editor」を起動します

FCmp Function Editorの説明については、またいずれ。要はSASは9.2からFCMPプロシジャというもので、ユーザーが自作の関数や、コールルーチンを作成することができ、それをライブラリにいれて、参照すれば、誰でも使えるよっていう機能があって
FCmp Function Editorはそれを手で直接編集・管理できるようなアプリです。


で起動したら、左側にライブラリがいくつか表示されていると思います。
もし、ライブラリの中にユーザー定義関数が作成されていれば、ここから参照できます。

で、作ってないのに、なんかSASHELPの下に表示されているはずなので、それを展開してみます。













すると関数のリストみたいなものがずらずらあって、詳細のところを
みると「EXCEL PRODUCT」(↑上の図でハイライトされている部分)とか書いています。

これは今ハイライトされている「product_slk」というものはEXCELのPRODUCT関数と同じ働きをするものですよという意味です。

ダブルクリックしてみると































右側にその関数を生成した、FCMPプロシジャのコードがそのまま入っています。
機能や使い方も全部書かれているので、FCMPプロシジャの勉強する上でも大変役立ちます。
ちなみにこれらは読み取り専用です。
すでに、作成されてSASHELPライブラリの中に入っているとのことなので
早速使ってみましょう!

でも、その前にEXCELのPRODUCT関数を知らないと、感動半減なのでその説明を















のように、ようは引数全部を掛け算した値を返す関数です。


さて、じゃあ使ってみましょう。

options cmplib = sashelp.slkwxl;

data X;
ARRAY  NU{3} _TEMPORARY_ (2 3 4);
 X=product_slk(NU);
run;

proc print;
run;

すると










となって、おぉ同じだ!となるわけです。

ちなみにユーザー定義関数は
options cmplib でユーザー定義関数の塊(正式名しりません)が入っているライブラリと、塊の名前を指定しないと使えません。

塊の名前は生成コードのoutlib=のライブラリ名の後に続いている名前です。

それさえ終われば後は普通の関数と同じように使えます。

FCMPの詳しい説明はまたいずれ





%window マクロウインドウが今でも輝きを失っていないという話

マクロウインドウは、マクロで制御することができる、簡易な(しょぼい)インターフェースを作成することができる機能なのですが、ちょとマイナーだったり古いといったイメージがあります。

よくEXCELをインターフェースにしてVBAでSASを動かす簡易なアプリケーションを作成したり、HTMLとJavascriptとSASを組み合わせた簡易ツールの話を目にしますが、しかし

複雑な機能を持っていたり、見栄えを綺麗にする上ではそれらが活きてくると思いますが、例えば1つ2つユーザーがパラメータを入力して、後はSASが走るだけといったシステムなら今でもマクロウインドウは全然活きてくると思います。
(DMでつかうツールで症例を絞ってロジカルやコンペアツール動かしたり程度のツールなど)

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

data DS1;
ID='T001';SEX='M';VAL=100;output;
ID='T002';SEX='M';VAL=110;output;
ID='T003';SEX='F';VAL=120;output;
ID='T004';SEX='F';VAL=130;output;
run;


それで以下のプログラムがあったとして

proc print data=DS1;
 where SEX="&WH";
run;

&WHに入る「M」「F」などの条件をユーザーに入力させるツールを作成するとします。

%let WH=M;

%window selectselect
#1 @1 "抽出条件の設定"

#3 @8 "男性のデータが欲しい場合はM"
#4 @8 "女性のデータが欲しい場合はF"
#6  @15 WH 20 attr=underline
#8 @8 "入力後にEnterを1回おしてください"
;

%display selectselect;

proc print data=DS1;
 where SEX="&WH";
run;

SASを開いて
このコードを、実行すると












という画面が表示され、デフォルトでMと表示されています。
これは%letでマクロ変数WHにMという値を代入しているからです。

ここはユーザーが消して、Fに変えることもできます。

抽出したい値を入力してエンターキーをクリックすると
その値でマクロ変数WHが更新され、以降のプログラムが実行されます。


%window
でウインドウを定義します
(#が表示される行、@がカラム位置、attrで書式など属性を付与できます)

%displayで定義したウインドウを表示できます。

実行後endsas;等で自動で閉じるようにしておけば、このSASファイルはもう
立派なツールじゃないでしょうか?

詳しくはマクロ言語:リファレンスを参照してください。
簡易なインターフェースと書きましたが、コードを細かく書けば
かなりしっかりしたものも作れるみたいですよ?




Create tableだけがSQLプロシジャでデータセットを作る方法ではないという話

今適当な、データセットがあったとします。

data Q1;
X=1;Y=2;output;
X=3;Y=4;output;
X=5;Y=6;output;
run;

そこで以下のSQLを実行すれば

proc sql noprint;

 create table A1 as
  select X
  from Q1
  where X<2;

 create table A2 as
  select X
  from Q1
  where X>2;

 create table A3 as
  select Y
  from Q1;

quit;

データセットA1,A2,A3が作成されます。

SQLプロシジャ内でcreate table データセット名 as 以下クエリを書けば
結果がデータセットになります。

SQLプロシジャでデータセットを作成するには上記のやり方しかないと思われている方も多いですが、そんなことはないです。
抜け道のようですが、SQLプロシジャはあくまでSASのプロシジャで、アウトプットを伴うプロシジャです。であれば、

ods listing close;
proc sql;
ods output SQL_Results=A1;
  select X
  from Q1
  where X<2;

ods output SQL_Results=A2;
  select X
  from Q1
  where X>2;

 ods output SQL_Results=A3;
  select Y
  from Q1
;
quit;

当然、ODSの対象であり、ODSの対象であれば ods outputでデータセットにできるということです。
上記のコードで同じ結果を得ることができます。

これをどう活かすかを考えます。

例えばSASのデータステップでは

data A4 A5 A6;
 set Q1;
 if X=1 then output A4;
 if X=2 then output A5;
 if X=3 then output A6;
run;

のように一回のデータステップで、複数のデータセットを作れます。
しかし、create table文では、1回で同様の操作はできません(多分、、)

ですがODSを利用すれば

proc sql;
ods output 
  SQL_Results=A4(where=(X=1))
  SQL_Results=A5(where=(X=3))
  SQL_Results=A6(where=(X=5))
;
  select X
  from Q1
;
quit;

このように実現可能です。

また、データステップでは、call symput等で、データセットを作成すると同時に
何らかの値をマクロ変数に格納する処理も同時に行えます。

ですがSQLで同様のことを1文ではできません。

がODSを利用すれば

proc sql;
ods output SQL_Results=A7;
 select X into:MX
 from Q1
 where Y=4;
quit;


のように、非常に幅の広いことができるようになります。
邪道といえば邪道かもしれませんが、せっかく、SASでSQLを使っているので
SASから利用できる部分はガンガン使っちゃっていいんじゃないでしょうか?
(DB内部やほかのシステムににSQL文を移植したりする予定がなければですが)








配列名に対してinで抽出をかける

以下のようなデータセットがあったとします。
(作成法は適当です。
多分、0.4の確率で'A',0.5で'B',0.1で'C'となる3変数で構成されるデータセットになっているはずです)

data Q1;
call streaminit(12345);
 do i=1 to 15;
  X_1=strip(translate(put(rand('table',0.4,0.5,0.1),best.),'ABC','123'));
  X_2=strip(translate(put(rand('table',0.4,0.5,0.1),best.),'ABC','123'));
  X_3=strip(translate(put(rand('table',0.4,0.5,0.1),best.),'ABC','123'));
  drop i;
 output;
end;
run;





X_1 X_2 X_3を配列AXとする時に、配列AX内のいずれかの要素が値'C'をとる
オブザベーションのみ出力せよ。
といった問題があるとします。

欲しいのは以下のデータセットです




最近まで、本当に知らなかったんですが

data A;
 set Q1;
 array AX{*} X_:;
   if 'C' in AX then output;
run;

これで詰みなんですね。値 in 配列名 で通るなんて、びっくりですよ。

基本的な知識だと言われたのですが、こんなんどこで習うの?知らなかったの僕だけですか?
どうぜ、そういうことって、他にもいっぱいあって、

知ってれば簡単に済む処理を、日々、無駄なコードをこねくりまわして
作っているだろうな、僕は! と切なくなりました。

てっきり

data A0;
set Q1;
 array AX{*} X_:;
  if count(cats(of AX{*}),'C')>0;
run;

とか

data A1;
 set Q1;
  array AX{*} X_:;
  do i=1 to dim(AX);
  if AX{i}='C' then do;
    output;
    leave;
   end;
  end;
 drop i;
run;

が正解かと思ってましたよ。











DIF関数とLAG関数_lag2(x)とlag(lag(x))は同じだけどdif2(x)とdif(dif(x))は違うみたいな話

DIF関数、最近まで知りませんでした。1つ前のオブザベーションの値と今の値との差を返す関数で、dif(x)はx-lag(x)と同義だそうです。
LAG関数は1つ前のオブザベーションの値を返します。lag2(x)とすれば2オブザベーション前のxの値,lag3(x)なら3つ前といったようにlagの後の数字を変えることで調整が効きます(ただし負の値で先のオブザベーションの値をとったりはできませんので、はい残念)
lag2(x)はlag(lag(x))と同じです。1つ前の1つ前は2つ前というわけです。
(脱線ですがlagはif文の中に直接記述すると正しく機能しない性質があるので条件式に使いたい場合は一度割り当てステートメントで新規変数に入れてからそれを使いましょう。知らないと非常に追いにくいエラーの代表的な例です)

ではdif(dif(x))はどんな値を返すのか?また、dif(lag(x))やlag(dif(x))はどんな意味なのか?
それを見てみましょう。

適当にデータセットを作ります。

data Q1;
do i=1 to 10;
 X=ceil(ranuni(777)*20);output;
end;
drop i;
run;


これに対して以下のプログラムを実行します。
頭の体操が好きな方は、結果を予想してからどうぞ。

data A1;
 set Q1;
  DIFX=dif(X);
  DIFDIFX=dif(dif(X));
  DIF2X=dif2(X);
  LAGX=lag(X);
  LAGLAGX=lag(lag(X));
  LAG2X=lag2(X);
  DIFLAG=dif(lag(X));
  LAGDIF=lag(dif(X));
run;






となるわけです。



SAS書籍紹介④_SQL関連

SASはSQLプロシージャを持っていて、SQLを実行することができます。標準SQLの機能についてはほぼ完全に備えているため、製品依存ではないSQL関連の書籍を読めば、そのSQLをそのまま使うことができます。

まあ「SAS 9.3 SQLプロシジャ ユーザーガイド」が日本語版SASオンラインヘルプでPDFでダウンロードできるので、それを読めという向きもありますが、、全くSQLを知らない人が読んで身につけていくような構成にはなってないと思います。

なので、個人的な意見100%ですが、いくつかのSQL関連の書籍を、こう読んでいけば理解が速いんじゃないかという順番通りに紹介していきます。

①SQL ゼロからはじめるデータベース操作
 著者:ミック
 出版社:翔泳社 (2010/6/29)



②達人に学ぶ SQL徹底指南書
 著者:ミック
 出版社:翔泳社 (2008/2/7)


で多分、①と②の間のあたりか、②を読み終えた後に

SAS 9.3 SQLプロシジャ ユーザーガイド
SAS Certification Prep Guide: Advanced Programming for SAS 9
SASハンドブック(共立出版)」←いずれ紹介
SASプログラミング(共立出版)」←いずれ紹介
統計解析ソフト SAS」←以前紹介済

のうち、肌に合うものを読めば、もうだいたい、書きたいことを書けると思います


さらに深みにいくなら



③プログラマのためのSQL 第4版
 著者:ジョー・セルコ (著), ミック (監修, 翻訳)
 出版社:翔泳社 (2013/5/14)



④SQLアンチパターン
Bill Karwin (著), 和田 卓人(監訳) (翻訳), 和田 省二(監訳) (翻訳), 児島 修 (翻訳)
出版社: オライリージャパン (2013/1/26)



⑤SQLパズル 第2版 プログラミングが変わる書き方/考え方
 著者:ジョー・セルコ (著), ミック (監修, 翻訳)
 出版社:翔泳社 (2007/11/02)


とかでしょうか。僕も③以降は全部ちゃんと読み込んで理解しているかと言われれば
そうではないのですが、、。


20150505 追記
「プログラマのためのSQL 第4版 すべてを知り尽くしたいあなたに」
Joe Celko (著), ミック (翻訳, 監修)
出版社: 翔泳社; 2013/5/23

詰め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です。

説明が下手なので、もしピンと来なければ実際に動かしてみてください。







SETINITプロシジャ PRODUCT_STATUSプロシジャ %sasinstallreporterでSASのプロダクトライセンス環境を確認する

NESUG 2012発表の論文「Determine What SAS® Version and Components Are Available 」
David D. Chapman, Chapman Analytics LLC, Alexandria, VA に書かれていた内容の一部紹介になります。

自身のSAS実行環境のプロダクトライセンスの状況を確認したい時や、プログラムをやりとりする相手がいる場合、相手の実行環境を確認してもらいたい時ってあると思います。

基本的には
proc setinit;
run;

を実行すればライセンス期限と共にインストールされているプロダクト情報がログにでます。

また、SASのガイドやリファレンスで、稀に、この機能はSAS9.2 のPhase2からこうなっています。のように製品のマイナーバージョンの記載があることがあります。そういった際に

proc PRODUCT_STATUS;
run;

より細かい情報を入手できます。

あと、究極はSAS Note 20390 (http://support.sas.com/kb/20/390.html)で提供されている
マクロをコピペして実行することです。

このマクロのいけているところは、SAS XML Mapper
やODS Graphics EditorのようにインストールCDから任意に選択してインストールしたり
あとでサイトからダウンロードしてインストールできるSASのアプリケーションの状態をチェックできる
ところだと思います。





describe tableの出力をコピーしてcreate tableして中身はinsert文にして、人にコードを渡す場合に必要なデータセットもその中で作るコードにしたいといった処理

投稿タイトルの意味がわかりませんよね。すみません

例えば、データベースからデータをひっぱってきて、それに対するプログラムを書いたとします。
それを他の場所にいる人に渡すとき、その人の場所からはデータベースにアクセスできないとします。
そうなると、実行しながらコードレビューしてもらうためには、当然データセットもつけなければいけないわけです。でもほんとにちょっとしたデータセットだといちいち添付するのも面倒だし、コードの中で作らしちゃえと思う時ありますよね。

input+cardsで作ればいいんですが、僕嫌いなんです。だからいつも

data DS;
x=1;y=2;output;
x=3;y=4;output;
run;

みたいな書き方で作ります。筋のいいコードではないです。

ただ、データセットにフォーマットやらラベルやら長さが細かく設定されていると
それの通りにデータセットを作成するのは面倒ですよね。proc contentsしてアウトプットから
定義情報コピペでもいいのですが、

僕が一番いいと思うやり方を紹介します。ただし、この方法の評判はめっぽう悪く、
余計面倒だと言われたこともあるので、これがいいと思えるのは僕だけの可能性があります。

なので読んでいて、こいつ何してんだと思われたら流してください。

今WORKに
データセット名[BASE]という下記のデータがあって





これを同じものをコードで作成したいとします。

まず
proc sql;
 describe table BASE;
quit;

と実行します。

するとログに













とこんな風にでます。
SQLのdescribe tableはCONTENTSプロシジャのSQL版みたいな感じで
そのデータセットの定義情報を教えてくれます。
しかもいいのが、そのデータセットをそのまま作成できるコードの形式で教えてくれるところです。

ようするに、上のログの create table ~ );までをコピーして
SQLプロシジャの中につっこんで実行すれば、そのままBASEと同じ定義情報をもったデータセットが作成されるのです。

ただし、このままでは空のデータセットで、中身はありません。
データを作成する部分を作成しなければなりません。

まず、

proc datasets nolist;
 modify BASE;
  attrib _all_ format= ;
quit;

で、後の処理でちょっと都合が悪いのでフォーマットを飛ばします。

そして

data _NULL_;
 set BASE;
 put 'values(' X ',"' Y '",' Z ')';

run;

とします。文字型変数に対してはコーテーション包みを忘れないようにします。
上を実行するとログには











こんな感じででます。
このvaluesの部分が欲しいのです。

さて後は、SQLプロシジャの中で
まず最初のdescribe tableででたログをコピペして
バッファサイズはいらないので消して、
その下に insert into BASE と一文打ってから
あとは上のputでだしたvaluesの部分をコピペして、最後にセミコロンでしめれば
はい、データセットを完全に再現するプログラムのできあがり!
(ユーザー定義フォーマットつけている場合は、formatプロシジャを前もって書いてくださいね)

proc sql noprint;
create table WORK.BASE
  (
   X num label='Xのラベル',
   Y char(100) label='Yのラベル',
   Z num format=YYMMDDS10. label='Zのラベル'
  );
 insert into BASE
 values(1 ,"A ",5022 )
 values(2 ,"C ",5122 )
 values(3 ,"D ",6022 )
;
quit;

基本コピペなので超楽に感じるんですけど、やっぱり途中でコチョコチョ、コード足しているんで
めんどうですかね?
じゃあマクロにでもしてください。そんなに難しくないはず