下書きのまま、すっかりあげ忘れていた記事があったのでアップします。
今回の話は、マクロ変数の展開についてです。
さて今
%let M=いろは;
data Q;
X='&M';
run;
といったコードを実行するとどうなるでしょうか?
やりたいこととしては、マクロ変数Mに「いろは」という値を入れて、それを後続のデータステップで
展開したいわけですが、この処理は期待どおりの結果になりません。
このように、マクロ変数が展開されません。
どうしてかというと、シングルコーテーションを使っているからです。
シングルコーテーション内のマクロ変数は展開されないというルールなんですね。
この場合は、ダブルコーテーションを使うのが正解でした。
さて、ここまではSASマクロを少しでも知っている人であれば、常識と言っていいことでしょう。
ただ、SASを長くやっていると、dmステートメント内の記述や、DDE等外部アプリケーションとの連携のため、シングルコーテーションを使う縛りがある上で、尚且つその中でマクロ変数を展開したいといったケースが生じることがあります。
無理。仕様だからしようがないといって諦めるのもそれはそれで一局の将棋ですが、新手一生の升田幸三はこう言いました。
「もう一歩突っこんで、不可能を可能にする努力、―将棋を創作し、また、勝負を勝ちきるには、この“えぐる”という修練が必要である」
というわけで、諦めずに、えぐってみましょう。
まず、思いつくのは
data E;
X=%bquote('&M');
run;
ですね。シングルコーテーションに、マクロ変数展開を拒絶する能力があるというのならば、それを無効化して、意味のない記号文字に変えてしまえという発想です。
これをクォート処理っていいます。クォート処理を行う関数は山ほどあって、対象とする記号の種類や、処理のタイミングによって使い分けるのですが、詳しく書くと量がやばいことになるので、今回はしません。
%bquoteはシングルコーテーションもクォート対象なので今回使っています。
おまけですが%strは、デフォではシングルはクォート対象外ですが、
data E;
X=%str(%'&M%');
run;
のように%を対象の前につけることで、同じ効果を得られます。
で、先ほどのコードを実行すると
となって、もれなくエラーになりました。
マクロ変数の展開はできているみたいですが、なぜでしょう?
わかりにくいんですが、ヒントはエラーメッセージです。
例えばですが、
data E;
X= $ABC$;
run;
というコードを実行すると、
あれ、同じメッセージだ!
どういうこと?
これはABCという文字列をくくっている$ドルマークに何の意味もないから、SASが、「お前が何をしたいのかさっぱりわからん、意味のある記号を使って、成立しているコードを書いてくれ」
っていう意味なんですね。
さて、これで先ほどの問題がわかりました。
data E;
X=%bquote('&M');
run;
は確かに、シングルコーテーションのマクロ展開拒絶能力を無効化したことで、&Mの内容は展開できました。
しかし同時に、シングルコーテーションの、括った値を文字列として扱うという能力も無効化していたのです。
シングルコーテーションが全く意味のない役立たずの無意味文字になりさがったわけです。
なるほど、やっと解決法が見えてみました。
つまりマクロを展開する際は、シングルコーテーションを無効化(クォート)し、展開が終わったら再度有効化(クォート解除)すればいいわけです。
そこで、強制的にクォートを解除する関数%unquoteで
data A;
X=%unquote(%bquote('&M'));
run;
とすれば
です。
ちなみに、残念ながらこれは別に僕が考えた新手ではなく、割と有名な定跡なんですけどね。
ods escapechar=で、Unicodeの特殊文字を使ったり、文字を修飾したりする話
深夜にカイジのアニメ(再放送)がやっていて、それを見ていると、無性にSASで星マークをprintしたくなりました。
さて、まずSASで特殊文字をアウトプットに出力したい場合、unicodeで対応する文字を検索します
(例は:http://www.decodeunicode.org/で探しました)
そこで、出力したい文字を見つけたら、上の星でいうと「2605」という文字列に注目します。
そしたら、今回の大事な呪文
ods escapechar='^';
を実行します。これによって^はエスケープ文字となります。
ここで指定したエスケープ文字に様々な表現を足すことで、拡張的な文字表現ができます。
で、以下のようにして
data Q1;
X="^{style [color=yellow] ^{unicode '2605'x}}";
KAIJI=repeat(X,2);
run;
proc print data=Q1;
var KAIJI;
run;
さて、まずSASで特殊文字をアウトプットに出力したい場合、unicodeで対応する文字を検索します
(例は:http://www.decodeunicode.org/で探しました)
そこで、出力したい文字を見つけたら、上の星でいうと「2605」という文字列に注目します。
そしたら、今回の大事な呪文
ods escapechar='^';
を実行します。これによって^はエスケープ文字となります。
ここで指定したエスケープ文字に様々な表現を足すことで、拡張的な文字表現ができます。
で、以下のようにして
data Q1;
X="^{style [color=yellow] ^{unicode '2605'x}}";
KAIJI=repeat(X,2);
run;
proc print data=Q1;
var KAIJI;
run;
実行すると
みたいな感じです。
※追記:html出力がデフォでない場合はods rtf;やods html;、ods pdf;と、対応するods html close;やods rtf close; ods pdf close;でプロシジャ部分を囲んで出力ファイル形式を指定ください。通常のlistingアウトプットでは展開されません
※追記:html出力がデフォでない場合はods rtf;やods html;、ods pdf;と、対応するods html close;やods rtf close; ods pdf close;でプロシジャ部分を囲んで出力ファイル形式を指定ください。通常のlistingアウトプットでは展開されません
例えば、
data Q2;
X="^{unicode '2721'x}";output;
X="^{unicode '2603'x}";output;
X="^{unicode '263A'x}";output;
run;
は、データセットの中身は当然
って感じで、意味不明なんですが、ひとたび出力するとunicodeが解釈されて
proc print data=Q2;
run;
絵文字みたいな感じでかわいいですね。
最後に真面目な話
ods escapecharを実務で使う場合は、
data Q3;
X=cats('χ','^{super 2}');output;
X=cats('H','^{sub 2}','O');output;
run;
proc print data=Q3;
run;
みたいに上付き文字とか下付き文字とかを入れたい場合ですかね?
主にタイトルとかフットノートとか、ラベルで使うことが多いと思います。
(やり方は同じです)
あ、最後に、ちょっと帰省等、用事があって、しばらく更新止まります。
また今まで書いた記事で、最近ちゃんとラベルの設定してなかったんですけど
その辺をちょっと、ちゃんと整理していきます。
あ、最後に、ちょっと帰省等、用事があって、しばらく更新止まります。
また今まで書いた記事で、最近ちゃんとラベルの設定してなかったんですけど
その辺をちょっと、ちゃんと整理していきます。
詰めSAS12回目:数字が1文字ずつに分解された、数列から100番目の値を取得する
リンクさせていただいたサイト「うずまき (3rd Gen)」の管理人じゅんさんに、あるプログラマ向けチャレンジサイトの問題を教えて貰いました。
ただ、何のサイトか書いて、そのまま問題文載せて、解答例を示してしまうと、ちょっとまずいかなと思うので、問題を参考にした上で内容を変更しています。結構ありがちで一般的な問題だと思いますが。
そもそもSASで解く人なんて皆無だから問題ないと思いますけど、、。
問題:
4,6,8,1,0,1,2,1,4,.......と続く数列のN番目の値について、Nをマクロ変数で与えて実行すれば、値が表示されるようなSASコードをかけ。そして、取り敢えずN=100のときの値を示せ
あ~!こういうの見るとダメですよね。将棋好きに詰め将棋みせるのと同じで、どれだけ忙しくても考えることを避けれない。
ようするに4から+2ずつしていくけど、値が2桁以上になったら、1桁ずつ分解されるっていうルールの数列ですね。
僕が書いたのは以下の感じです。
%let N=100;
data _NULL_;
file print;
retain K 0;
do i=1 to &N;
X=put(2+(2*i),best. -L);
do j=1 to length(X);
Y=char(X,j);
K+1;
if K=&N then do;
put Y;
return;
end;
end;
end;
run;
答えは
[1]です。
なにをしているかをわかりやすくするため、
%let N=10;
data A1;
file print;
retain K 0;
do i=1 to &N;
X=put(2+(2*i),best. -L);
do j=1 to length(X);
Y=char(X,j);
K+1;
output;
if K=&N then do;
return;
end;
end;
end;
run;
とすると
Kが実際何番目かのカウンタです。Xが加算ででてくる値で、それを1桁ずつ分解したのはYです。
Kが設定したNになった時点でループを打ち切ります。
さて、じゅんさんが提示されたコード(問題を変えたことにより、影響のあった箇所等のみかえています。)は
%let x=100;
data A2;
length line2 $ 32767;
DO num=6 TO 4+&x*2 BY 2;
numvar=num;
line=PUT(numvar,best12.);
IF num=6 THEN DO;
line2='4'||line;
line2=COMPRESS(line2,' ');
END;
IF num NE 6 THEN DO;
line2=TRIM(line2)||TRIM(line);
line2=COMPRESS(line2,' ');
END;
END;
ans=SUBSTR(line2,"&x",1);
run;
PROC PRINT data=A2;
var ans;
run;
で、結果は同じです。
なるほど、オブザベーション起こすわけではなく、1変数内で横に連結していくわけですね。問題のイメージと処理のイメージが合致しています。1変数内の文字値制限を超えない限りは、こちらの方が自然かもしれません。
さて、多分、同じ思いの方が多いと思いますが、この問題、めっちゃIML向きですよね。
というかSAS向きでないのか?
一応書いてみたのが
%let N=100;
proc iml;
X=compress(rowcat(char(shape(t((2:&N+1)*2),1))));
Y=substr(X,&N,1);
print X Y;
quit;
ただ、何のサイトか書いて、そのまま問題文載せて、解答例を示してしまうと、ちょっとまずいかなと思うので、問題を参考にした上で内容を変更しています。結構ありがちで一般的な問題だと思いますが。
そもそもSASで解く人なんて皆無だから問題ないと思いますけど、、。
問題:
4,6,8,1,0,1,2,1,4,.......と続く数列のN番目の値について、Nをマクロ変数で与えて実行すれば、値が表示されるようなSASコードをかけ。そして、取り敢えずN=100のときの値を示せ
あ~!こういうの見るとダメですよね。将棋好きに詰め将棋みせるのと同じで、どれだけ忙しくても考えることを避けれない。
ようするに4から+2ずつしていくけど、値が2桁以上になったら、1桁ずつ分解されるっていうルールの数列ですね。
僕が書いたのは以下の感じです。
%let N=100;
data _NULL_;
file print;
retain K 0;
do i=1 to &N;
X=put(2+(2*i),best. -L);
do j=1 to length(X);
Y=char(X,j);
K+1;
if K=&N then do;
put Y;
return;
end;
end;
end;
run;
[1]です。
なにをしているかをわかりやすくするため、
%let N=10;
data A1;
file print;
retain K 0;
do i=1 to &N;
X=put(2+(2*i),best. -L);
do j=1 to length(X);
Y=char(X,j);
K+1;
output;
if K=&N then do;
return;
end;
end;
end;
run;
とすると
Kが実際何番目かのカウンタです。Xが加算ででてくる値で、それを1桁ずつ分解したのはYです。
Kが設定したNになった時点でループを打ち切ります。
さて、じゅんさんが提示されたコード(問題を変えたことにより、影響のあった箇所等のみかえています。)は
%let x=100;
data A2;
length line2 $ 32767;
DO num=6 TO 4+&x*2 BY 2;
numvar=num;
line=PUT(numvar,best12.);
IF num=6 THEN DO;
line2='4'||line;
line2=COMPRESS(line2,' ');
END;
IF num NE 6 THEN DO;
line2=TRIM(line2)||TRIM(line);
line2=COMPRESS(line2,' ');
END;
END;
ans=SUBSTR(line2,"&x",1);
run;
PROC PRINT data=A2;
var ans;
run;
で、結果は同じです。
なるほど、オブザベーション起こすわけではなく、1変数内で横に連結していくわけですね。問題のイメージと処理のイメージが合致しています。1変数内の文字値制限を超えない限りは、こちらの方が自然かもしれません。
さて、多分、同じ思いの方が多いと思いますが、この問題、めっちゃIML向きですよね。
というかSAS向きでないのか?
一応書いてみたのが
%let N=100;
proc iml;
X=compress(rowcat(char(shape(t((2:&N+1)*2),1))));
Y=substr(X,&N,1);
print X Y;
quit;
処理過程がわかりやすいようにX Y二つに分けていますが、答えはYです。
ただ、多分X=の部分もっと短くかけると思うのですが、まだIMLに慣れていなくてわかりませんでした。どなたか教えてください。
proc corrのアウトプットで、相関係数の値やp値の値に基づいて色を変える方法
最近交流させていただいております方から、「膨大な数の変数の相関をみる場合があるのですが、相関係数の値が一定以上、P値の値が一定以下の際に、そこがわかるようにアウトプットに色づけしたい」といった趣旨の質問がありました。
プロシジャのアウトプットをダイレクトにいじるのは、あんまやったことないから無理です~!って逃げるところでしたが、ちょっと頑張ってみました。
確かにproc corrかけると、対象変数の相関行列がでてくるんですが、変数多いと、目が滑って見落としちゃうことあります。
まず
data Q1;
A=2;B=1;C=2;D=3;E=9;F=9;output;
A=4;B=2;C=3;D=8;E=2;F=22;output;
A=6;B=1;C=3;D=2;E=13;F=9;output;
A=8;B=4;C=6;D=3;E=23;F=1;output;
A=10;B=5;C=7;D=5;E=8;F=8;output;
run;
プロシジャのアウトプットをダイレクトにいじるのは、あんまやったことないから無理です~!って逃げるところでしたが、ちょっと頑張ってみました。
確かにproc corrかけると、対象変数の相関行列がでてくるんですが、変数多いと、目が滑って見落としちゃうことあります。
まず
data Q1;
A=2;B=1;C=2;D=3;E=9;F=9;output;
A=4;B=2;C=3;D=8;E=2;F=22;output;
A=6;B=1;C=3;D=2;E=13;F=9;output;
A=8;B=4;C=6;D=3;E=23;F=1;output;
A=10;B=5;C=7;D=5;E=8;F=8;output;
run;
適当にデータを作ります。
で、
ods trace on;
proc corr data=Q1;
run;
ods trace off;
とすると
と、いっぱい出てきます。
今回、いじくりたいのは3つめの相関行列の表です。
ods traceつけて実行したので、ログには各アウトプットの情報がでています。
そこから相関行列に該当するものを探します。
(ods tarce onについて詳しくは
みっけ! そこで、テンプレートの箇所をみます。
このテンプレートこそが、解析結果をアウトプットするためのデザイン情報、スタイルシートみたいなもんなんですね。
そこで、テンプレートを特定したら、次にそのテンプレートの中身を展開して確認します
proc template;
source Base.Corr.StackedMatrix;
run;
templateプロシジャでsourceの後に、確認したい先ほどのテンプレート名を指定して実行
ログに以下のようなものがでてきます。
ふむふむ、成程。さっぱりわからん。読めるか、こんなもん。
まあ、GraphTemplateLangage(GTL)やってる方なら知っているかもしれませんが、これはグラフじゃない方ののテンプレートランゲージなんですね。
これをカスタマイズするのか~。まあ頑張って読んでみます。
大切なのは、どこの記述が、アウトプットのどこに紐づいているかを見極めることです。
じっくり見てると、なんとな~く
column (RowName RowLabel) (Matrix) * (Matrix2) * (Matrix3) * (Matrix4);
の箇所が、全体の構造に関係してる気がしませんか?勘で。
さっきのアウトプットと見比べてみると、column(RowName RowLabel)はどうも、縦にA B C D E F、横にA B C D E Fとなっている、アウトプットの一番外側の部分ではないか?と当たりがつきます(強引)。そうすると、次に(Matrix)とあるものは、もしかして相関係数の行列に対応しているのでは?と推測されます(強引)、じゃあ次の(Matrix2)はP値か!
じゃあ(Matrix3)と(Matrix4)はなんなんだ? あ、そうか、corrプロシジャにはたくさんオプションが付けられる!その指定によっては出現する階層じゃないのか!(もはや、こじつけ)
ということがわかります。まあ、詳しくはods関係をじっくり勉強してください。
そして、もう一つさっきのTemplateプロシジャの出力で重要ななのが、NOTEの部分で、このcorrプロシジャの相関行列をだすテンプレートはSASHELPのTMPLISTって中に格納されているよって部分です。このことを心に留めていてください。
さて、実際にこっからテンプレート改造手術を開始する前に下準備
proc format;
/*相関係数の色*/
value rf low--0.7='RED'
-0.7<--0.4='BLUE'
-0.4<--0.2 ='GREEN'
-0.2-<0.2='WHITE'
0.2-<0.4='GREEN'
0.4-<0.7='BLUE'
0.7-<1='RED'
1='WHITE';
/*P値の色*/
value pf low-0.01='RED'
0.01<-0.05='BLUE'
0.05<-high='BLACK';
run;
まあそのまんまですね、相関係数の値によって変わるフォーマット、P値によって変わるフォーマットを先んじて作っておきます。
そしたら
proc template;
edit Base.Corr.StackedMatrix;
column (RowName RowLabel) (Matrix) * (Matrix2);
edit matrix;
cellstyle _val_ as {backgroundcolor=rf.};
end;
edit matrix2;
style={foreground=pf.};
end;
end;
run;
これで手術完了です。
edit Base.Corr.StackedMatrix;は、カスタマイズする元のテンプレート名を指定
column (RowName RowLabel) (Matrix) * (Matrix2);
は、今回はここまでの定義以上は必要ないので、こうしてます
edit matrix;からend;までが、相関係数の部分をいじりますよって意味です。
cellstyle _val_ as {backgroundcolor=rf.}; これはセルの値に基づいて、セルのスタイルを変更してください、背景色はrfフォーマットで定義しますって意味です。
edit matrix2;からも同様で、今度はセル全体ではなく、値の書式のみを変更する書き方です。
そして実行すると、このテンプレートはWORK.templateというところに保存されます。え?SASHELPじゃないの?って思うかもしれませんが、SASHELPのテンプレートをうっかり変な風に変えちゃったら、今後のcorrプロシジャのアウトプット全部変になって大変でしょう?
だからWORKに複製が作られて、そこが改造されるようになってるんです。
で
ods path work.template(update) sashelp.tmplmst;
ods html file='/folders/myfolders/sample.html';
proc corr data=Q1;
ods select PearsonCorr;
run;
ods html close;
です。あ、ちなみにfile=の記述は、なぜかUniversityではこれでhtmlつくらないと、僕の環境でうまくいかなかったのでつけてるだけで、本来いらないです。
リスト出力以外ならods pdf;でもods rtf;でもods htmlでもなんでもいいです。
ods path work.template(update) sashelp.tmplmst;
これは、WORKにある、カスタマイズしたテンプレートがあるかを先に見ろよ、あったらそっち使えよ!って命令です。
結果は
です。相関係数の絶対値が0.7以上ならセルが赤、0.4以上なら青、0.2以上は緑になります。
P値は5%以下なら青字、さらに1%以下なら赤字になります。
カスタマイズしたテンプレートを消す場合
proc template;
delete base.corr.stackedmatrix;
run;
でOKです。
fractフォーマットで分数表示
今、以下のコードを実行してみます。
data Q1;
X=1/3;output;
X=1/5;output;
X=2/9;output;
X=30/4;output;
run;
data Q1;
X=1/3;output;
X=1/5;output;
X=2/9;output;
X=30/4;output;
run;
そして、出来たデータセットは
のように、割り切れない場合は、表示有効桁まで、値が続くわけです。
このデータセットにfractフォーマットをつけて表示するとどうなるでしょうか?
proc datasets nolist;
modify Q1;
format X fract.;
run;
quit;
として、再度開いてみると
お~、こうなるわけですね。
場合によっては、こっちで報告した方が分かりやすい場合もあるかもですね。
ケチな話、セル幅がコンパクトに済むし。
どうして今回、この話題にしたかというと、最近、知り合いの小学生の勉強を見たことがあって、
計算ドリルみたいなやつの答えあわせする羽目になったんですが、答えのページ切り取ったのがどっかいってなかったんですね。
やってた問題っているのが、ひたすら分数を約分させるっていう、ストレステストみたいな単純問題でした。
で、面倒なので、解いてる横で
data Q2;
X=58/126;output;
X=35/190;output;
X=90/20;output;
X=13/130;output;
format X fract.;
run;
こんな感じでSAS書いて、実行しました。
で、便利ですね!
9.4からODS EXCELでエクセルファイルが作れる話
連続投稿。
次は9.4の話です。持ってない方、すみません。家でuniversityで試してください。
SASからEXCELファイルを生成する方法はたくさんあります。
ただODS経由で、生成する場合、今まではods tagsets.excelxp というものがあったのですが、
弱点として
・純粋なXLS形式ではない(XML)ため、開くときにメッセージがでたりする。
・XLSX形式は生成できないこともあり、ファイルサイズが大きくなりがち
・グラフなどの画像の挿入にテクニックがいる(できなくはないらしいですが)
などなどがありました。
(ただ、EXCEL周りは難しいところが多いので、僕の話をあまり信用しないでください)
特にグラフ等の画像をEXCELに挿入するのは、中々悩ましいところで、僕は以前DDEでやってました。
それも、一旦画像を外部ファイルとしてどこかに保存し、それをddeでコピーしてエクセルに
ペタッと貼るというコードだったので、あまりスマートではないなぁと思ってました。
ところが9.4からODS EXCELという、名前そのままの機能が実装されました。
(またしても、もっと早く実装してよ!絶対要望あっただろ!アドイン製品ばっか売ろうと...まあこの辺にしておきましょう)
9.4をどうにか会社にぶち込みたい方は、素人にもわかりやすく便利な機能で、見た目おおっ!てなるので、これを武器に上司をうまく丸め込んでみるとか?(Universityでデモ作れるし)
さて使い方ですが、僕もまだ今日初めてなので、基本だけ
data Q1;
X='い';Y=1;Z=3;output;
X='い';Y=2;Z=1;output;
X='い';Y=3;Z=2;output;
X='ろ';Y=1;Z=2;output;
X='ろ';Y=2;Z=4;output;
run;
という適当なデータがあったとして、コードは
ods excel file='/folders/myfolders/example.xlsx'
style=pearl
options(sheet_interval='none'
heet_name='シート名'
embedded_title='yes');
ods text='自由にテキスト入れれます';
proc print data=Q1 noobs;
run;
proc freq data=Q1;
tables X*Y;
run;
ods graphics / height=400 width=700;
proc sgplot;
series x=y y=z/group=x;
run;
ods excel close;
結果は
と、要はods excelとods excel closeの間にあるプロシジャ出力が
全部エクセル化されるよってことです。
DDEのように、セル番地を指定して、値を出力といった感じではなく、
デザインは完全にプロシジャのアウトプットに依存します。
きめ細かい書式のリスト出力が欲しいのであれば、proc reportとかでなんとか頑張りましょう。
ちなみに、このODS EXCELはエクセルがインストールされてない環境で使えます。
実際に上のサンプルは、入ってないやつで動かして、キングソフトとかで開いたものです。
次は9.4の話です。持ってない方、すみません。家でuniversityで試してください。
SASからEXCELファイルを生成する方法はたくさんあります。
ただODS経由で、生成する場合、今まではods tagsets.excelxp というものがあったのですが、
弱点として
・純粋なXLS形式ではない(XML)ため、開くときにメッセージがでたりする。
・XLSX形式は生成できないこともあり、ファイルサイズが大きくなりがち
・グラフなどの画像の挿入にテクニックがいる(できなくはないらしいですが)
などなどがありました。
(ただ、EXCEL周りは難しいところが多いので、僕の話をあまり信用しないでください)
特にグラフ等の画像をEXCELに挿入するのは、中々悩ましいところで、僕は以前DDEでやってました。
それも、一旦画像を外部ファイルとしてどこかに保存し、それをddeでコピーしてエクセルに
ペタッと貼るというコードだったので、あまりスマートではないなぁと思ってました。
ところが9.4からODS EXCELという、名前そのままの機能が実装されました。
(またしても、もっと早く実装してよ!絶対要望あっただろ!アドイン製品ばっか売ろうと...まあこの辺にしておきましょう)
9.4をどうにか会社にぶち込みたい方は、素人にもわかりやすく便利な機能で、見た目おおっ!てなるので、これを武器に上司をうまく丸め込んでみるとか?(Universityでデモ作れるし)
さて使い方ですが、僕もまだ今日初めてなので、基本だけ
data Q1;
X='い';Y=1;Z=3;output;
X='い';Y=2;Z=1;output;
X='い';Y=3;Z=2;output;
X='ろ';Y=1;Z=2;output;
X='ろ';Y=2;Z=4;output;
run;
という適当なデータがあったとして、コードは
ods excel file='/folders/myfolders/example.xlsx'
style=pearl
options(sheet_interval='none'
heet_name='シート名'
embedded_title='yes');
ods text='自由にテキスト入れれます';
proc print data=Q1 noobs;
run;
proc freq data=Q1;
tables X*Y;
run;
ods graphics / height=400 width=700;
proc sgplot;
series x=y y=z/group=x;
run;
ods excel close;
結果は
と、要はods excelとods excel closeの間にあるプロシジャ出力が
全部エクセル化されるよってことです。
DDEのように、セル番地を指定して、値を出力といった感じではなく、
デザインは完全にプロシジャのアウトプットに依存します。
きめ細かい書式のリスト出力が欲しいのであれば、proc reportとかでなんとか頑張りましょう。
ちなみに、このODS EXCELはエクセルがインストールされてない環境で使えます。
実際に上のサンプルは、入ってないやつで動かして、キングソフトとかで開いたものです。
掲示板質問の回答例:現在使用中のFootnoteの最終番号を取得する方法
掲示板の方に、質問をいただきました。有難うございます!掲示板に返事書こうと思ったのですが、結構やってて面白い内容だったので記事にしました。
(掲示板:http://tumesas.progoo.com/bbs/tumesas_topic_pr_6.html)
ただ、本当にこの方法でいいのかわからないので、何かアイデアがあればコメントでも掲示板にでもメッセージください。
さて本題。質問は
なるほど、、。そういえば考えたことない、、。
検索しても、それっぽいのが出てこないので、自分の頭で考えてみました。
以下になります。
まず
data Q1;
X=1;
run;
title 'タイトル1';
footnote 'フットノート1';
footnote2 'フットノート2';
footnote3 'フットノート3';
proc print;
run;
を実行すると、当然
こうなるわけですね。
さて次のプロシジャ実行時に、すでにfootnote3まで使ったことを取得し、footnote4が展開されるようにしたいわけですね。仮にfootnote6まで使っていればfootnote7みたいな感じです。
さて、定義したタイトルやフットノートの情報はどこにいるでしょうか?
SASHELPライブラリの中には、SAS社が用意したサンプルデータセットと共に、SASの様々な定義情報がリアルタイムで更新されるビューが用意されてます。
(ちなみにSASHELPのサンプルデータセットを間違えて全部消したおバカさん(僕です)はSAS社のWebページから再ダウンロードできます)
この中のVTITLEを開けてみると
発見!
typeのTはタイトル、Fはフットノートです。
後は、もう簡単ですね
proc sql noprint;
select max(number) into:fno
from dictionary.TITLES
where type='F' and text^='';
quit;
です。
sashelp.VTITLEはSQLの中ではdictionary.TITLESと書けます。そういうものなんです。
これでマクロ変数fnoには3が入ります。
そして以下
footnote%eval(&fno+1) 'プログラマー名:SASYAMA';
proc print;
run;
を実行すると
となって、確かに最後にプログラマー名がでてきます。
こんな感じです。
まあ、改善点としては、定義可能最大値が10なので、footnote10が既に定義されている場合や、全く定義されていない状態で実行した場合エラーにならないような分岐が必要ですが、そこまではやってません。
ods selectのPRESISTの話と、ヒストグラム周りの細かい話
統計グラフ全国コンクールというのがあって、小学生から高校生が色んなグラフを発表する催しです。10月18日の「統計の日」に展示されたというニュースをみて、調べると、Web上でもアップされてました。(統計情報研究開発センターはこういう活動もされてるんですね)
http://www.sinfonica.or.jp/tokei/graph/g62/index_l.html
なんか、結構ぐっときました。グラフ大事ですね。小学生とかが頑張って、手書きで棒グラフとか書いてんのに、プログラム回して一瞬でかける大人が、ついつい面倒くさがって描かないとか駄目ですね。
それに、別に小難しい凝ったグラフじゃなくて、本当に単純な棒グラフとか円グラフでも、立派に物事の本質を表現してますね。
というわけで、前フリとさほど関係ないんですが、ヒストグラムの細かい話します。
適当にデータ作ります。
data Q1;
call streaminit(777);
do i=1 to 200;
X=rand('normal',100,20);
output;
end;
run;
まず、何も余計なことしていない出力です。何も指定しないと、よきにはからえで、区間幅を決めてくれます。ヒストグラムの適切な区切りについては、色々あるのでリファレンス等を参照してください。(区間の取り方で、様子と与える印象が随分変わってしまうので、注意が必要です。)
注目なのは、目盛りの位置です。30にあって、次42なんですが、この場合、境界は36にあるんですね。24-36で一区間、36-48で一区間(ちょうど36がどっちに入るかは後で説明します)
このように境界値と境界値の間に目盛りがあるのは、midpointsという指定で、デフォルトがこちらです。
二つ目はmidpoints=(0 to 200 by 20)とした出力
このように全体の幅と、区間を指定することができます。
この場合、境界値はmidpointsで指定した値の間になります。
続いてendpoints=(0 to 200 by 20)です。midpointsとどこが違うか、注意深く目盛りを見てみましょう。
とこのように、指定した値そのものが境界値になってるわけですね!
ちなみに、実行後に
といったメッセージ(英語なのはUniversityだけかな?)がでる場合、データがプロットの範囲外にハミってるよという警告なので注意しましょう。
ちなみにNMIDPOINTS=、NENDPOINTS=というオプションもあって、そっち方は区間幅を指定しなくても区切りの数を指定することで、幅は自動で出してくれます。
さて実は3つのプロシジャに全てouthistogram=というものをつけていましたが、これはヒストグラムの情報をデータセット化したものを出力する機能になります。
3つそれぞれの結果を貼ります。
と、どの区間にどれだけのデータがあるか一目瞭然ですね。
オプションによって変数名が変わることに注意しましょう。
次ににsgplotでヒストグラムを書く場合
title "SGPLOTのデフォルト";
proc sgplot data=Q1;
histogram X / binwidth=20 binstart=0 showbins;
xaxis values=(0 to 200 by 20);
run;
一緒ですね。詳細なオプションまではまだ手が回ってません。
最後に、実際よく使う、グループごとのヒストグラムを比較するようなグラフを作って終わりにします。ついでに正規分布曲線もはめときましょう。意味ないけど。
data Q3;
set Q1;
if _N_<=70 then GP='A';
else if _N_<=140 then GP='B';
else GP='C';
run;
ods select histogram(PRESIST);
title;
proc univariate data=Q3;
class GP;
histogram X /nrows=3 normal odstitle="classとnrowで並べて表示";
run;
http://www.sinfonica.or.jp/tokei/graph/g62/index_l.html
なんか、結構ぐっときました。グラフ大事ですね。小学生とかが頑張って、手書きで棒グラフとか書いてんのに、プログラム回して一瞬でかける大人が、ついつい面倒くさがって描かないとか駄目ですね。
それに、別に小難しい凝ったグラフじゃなくて、本当に単純な棒グラフとか円グラフでも、立派に物事の本質を表現してますね。
というわけで、前フリとさほど関係ないんですが、ヒストグラムの細かい話します。
適当にデータ作ります。
data Q1;
call streaminit(777);
do i=1 to 200;
X=rand('normal',100,20);
output;
end;
run;
で、まずはunivariateプロシジャで描きますが、univariateプロシジャでhistorgam指定しても、要約統計量など、欲しいプロット以外のものもでてくるのでods selectを使います。ods select histogram;とすれば、histogramの名前を持つアウトプットしかでません。ただし、この命令は直後のプロシジャ出力にしか有効ではありません。今回は、ステートメントを調整しながらいくつも書くので、次のようにします。
ods select histogram(PRESIST);
proc univariate data=Q1;
histogram X /odstitle="デフォルト"
outhistogram=A1;
run;
proc univariate data=Q1;
histogram X / midpoints=(0 to 200 by 20) odstitle="Midpointsで中間点指定"
outhistogram=A2;
run;
proc univariate data=Q1;
histogram X / endpoints=(0 to 200 by 20) odstitle="Endpointsで終点指定"
outhistogram=A3;
run;
ods select ALL;
(PRESIST)をつけると、次のods selectが出るまで、その縛りが有効になります。
ods select ALL;でデフォルトの状態に戻ります。
さて、3つunivariateプロシジャを実行しました。
先に出力されたヒストグラムを確認してみます。
まず、何も余計なことしていない出力です。何も指定しないと、よきにはからえで、区間幅を決めてくれます。ヒストグラムの適切な区切りについては、色々あるのでリファレンス等を参照してください。(区間の取り方で、様子と与える印象が随分変わってしまうので、注意が必要です。)
注目なのは、目盛りの位置です。30にあって、次42なんですが、この場合、境界は36にあるんですね。24-36で一区間、36-48で一区間(ちょうど36がどっちに入るかは後で説明します)
このように境界値と境界値の間に目盛りがあるのは、midpointsという指定で、デフォルトがこちらです。
二つ目はmidpoints=(0 to 200 by 20)とした出力
このように全体の幅と、区間を指定することができます。
この場合、境界値はmidpointsで指定した値の間になります。
続いてendpoints=(0 to 200 by 20)です。midpointsとどこが違うか、注意深く目盛りを見てみましょう。
とこのように、指定した値そのものが境界値になってるわけですね!
ちなみに、実行後に
といったメッセージ(英語なのはUniversityだけかな?)がでる場合、データがプロットの範囲外にハミってるよという警告なので注意しましょう。
ちなみにNMIDPOINTS=、NENDPOINTS=というオプションもあって、そっち方は区間幅を指定しなくても区切りの数を指定することで、幅は自動で出してくれます。
さて実は3つのプロシジャに全てouthistogram=というものをつけていましたが、これはヒストグラムの情報をデータセット化したものを出力する機能になります。
3つそれぞれの結果を貼ります。
と、どの区間にどれだけのデータがあるか一目瞭然ですね。
オプションによって変数名が変わることに注意しましょう。
次ににsgplotでヒストグラムを書く場合
title "SGPLOTのデフォルト";
proc sgplot data=Q1;
histogram X / binwidth=20 binstart=0 showbins;
xaxis values=(0 to 200 by 20);
run;
binwidthで区間幅、binstartで、区間の開始点を指定し、全体の表示幅はxaxisで指定します。
で、見ての通りunivariateと同じでmidpints形式になっているのがわかります。
残念ながらsgplotにはendpointsに該当する指定がないので、どうにかやりたい場合は以下のようにスタート地点をずらします。
title "SGPLOTで強引にEndpoints表現";
proc sgplot data=Q1;
histogram X / binwidth=20 binstart=10 showbins;
xaxis values=(0 to 200 by 20);
run;
つづいて、
data Q2;
do X=2,3,4,6,8;
output;
end;
run;
といったデータセットがあったとします。
2種類の指定でunivariateを回すので
2 3 4 6 8という値で、3がどこの区間に含まれるかに注目してください。
ods select histogram(PRESIST);
title;
proc univariate data=Q2;
histogram X /odstitle="デフォルト";
run;
proc univariate data=Q2;
histogram X /odstitle="RTINCLUDE"
RTINCLUDE;
run;
ods select ALL;
まず、一つ目の出力は
境界値3が、どっちのバーに含まれてるかをみると、右側(大きいほう)ですね。
で、続いてRTINCLUDEをつけると
と、左側に入ります。
ちなみにSGPLOTの場合、
やはり何も指定しないと右側に組み込まれ
title "SGPLOTのデフォルトはboundry=upper";
proc sgplot data=Q2;
histogram X;
run;
title "SGPLOT boundry=lowwer";
proc sgplot data=Q2;
histogram X /boundry=lowwer;
run;
boundry=lowwerとすると左側に入ります。(画像は省略)
続いては、バーに、値をラベルとして表示する方法です。
その次にヒストグラムはunivariateでもsgplotでも、Y軸はパーセントがデフォルトですが、
それを度数に変更する方法も併せて実行してみます。
ods select histogram(PRESIST);
title;
proc univariate data=Q1;
histogram X /barlabel=percent odstitle="パーセントラベルの表示";
run;
proc univariate data=Q1;
histogram X /vscale=count barlabel=count odstitle="スケールを度数にしてラベルも度数で表示" ;
run;
ods select ALL;
まず一つ目の出力 barlabel=percentでパーセントの値を表示させます
続いて、2つ目、vscale=countで、Y軸の尺度を度数に変え、barlabel=countで表示するのも度数にしています。
SGPLOTで、尺度を度数にする場合は
title "SGPLOTでスケールを度数";
proc sgplot data=Q1;
histogram X/scale=count;
run;
とすると、
値の表示はどうするんだろう?ちょっとわかりませんでした。
もう疲れたんで、とりあえずここまで。どなたか教えてくれたらうれしいです。
一緒ですね。詳細なオプションまではまだ手が回ってません。
最後に、実際よく使う、グループごとのヒストグラムを比較するようなグラフを作って終わりにします。ついでに正規分布曲線もはめときましょう。意味ないけど。
data Q3;
set Q1;
if _N_<=70 then GP='A';
else if _N_<=140 then GP='B';
else GP='C';
run;
ods select histogram(PRESIST);
title;
proc univariate data=Q3;
class GP;
histogram X /nrows=3 normal odstitle="classとnrowで並べて表示";
run;
symbolimageでグラフのシンボルに好きな画像を使う方法。例えば散布図で将棋盤を表現
SAS9.4から、SG系グラフでsymbolimageを使うことができ、プロットのシンボルを自由な画像にすることができます。
印象としては、実装遅くない?って感じですよね。結構大事な機能だと思うので9.2から入れりゃいいのに、、。
で、今回はGTL&SGRENDERで散布図書きます。
で、どうせなら、散布図の概念に捉われない使い方をしたいと思います。
今、以下のようなデータセットがあったとします。
data Q1;
X=1;Y=9;KOMA='▲香車';output;
X=2;Y=9;KOMA='▲桂馬';output;
X=3;Y=9;KOMA='▲銀将';output;
X=4;Y=9;KOMA='▲金将';output;
X=5;Y=9;KOMA='▲王将';output;
X=6;Y=9;KOMA='▲金将';output;
X=7;Y=9;KOMA='▲銀将';output;
X=8;Y=9;KOMA='▲桂馬';output;
X=9;Y=9;KOMA='▲香車';output;
X=2;Y=8;KOMA='▲飛車';output;
X=8;Y=8;KOMA='▲角行';output;
X=1;Y=7;KOMA='▲歩兵';output;
X=2;Y=7;KOMA='▲歩兵';output;
X=3;Y=7;KOMA='▲歩兵';output;
X=4;Y=7;KOMA='▲歩兵';output;
X=5;Y=7;KOMA='▲歩兵';output;
X=6;Y=7;KOMA='▲歩兵';output;
X=7;Y=7;KOMA='▲歩兵';output;
X=8;Y=7;KOMA='▲歩兵';output;
X=9;Y=7;KOMA='▲歩兵';output;
X=1;Y=1;KOMA='△香車';output;
X=2;Y=1;KOMA='△桂馬';output;
X=3;Y=1;KOMA='△銀将';output;
X=4;Y=1;KOMA='△金将';output;
X=5;Y=1;KOMA='△王将';output;
X=6;Y=1;KOMA='△金将';output;
X=7;Y=1;KOMA='△銀将';output;
X=8;Y=1;KOMA='△桂馬';output;
X=9;Y=1;KOMA='△香車';output;
X=8;Y=2;KOMA='△飛車';output;
X=2;Y=2;KOMA='△角行';output;
X=1;Y=3;KOMA='△歩兵';output;
X=2;Y=3;KOMA='△歩兵';output;
X=3;Y=3;KOMA='△歩兵';output;
X=4;Y=3;KOMA='△歩兵';output;
X=5;Y=3;KOMA='△歩兵';output;
X=6;Y=3;KOMA='△歩兵';output;
X=7;Y=3;KOMA='△歩兵';output;
X=8;Y=3;KOMA='△歩兵';output;
X=9;Y=3;KOMA='△歩兵';output;
run;
印象としては、実装遅くない?って感じですよね。結構大事な機能だと思うので9.2から入れりゃいいのに、、。
で、今回はGTL&SGRENDERで散布図書きます。
で、どうせなら、散布図の概念に捉われない使い方をしたいと思います。
今、以下のようなデータセットがあったとします。
data Q1;
X=1;Y=9;KOMA='▲香車';output;
X=2;Y=9;KOMA='▲桂馬';output;
X=3;Y=9;KOMA='▲銀将';output;
X=4;Y=9;KOMA='▲金将';output;
X=5;Y=9;KOMA='▲王将';output;
X=6;Y=9;KOMA='▲金将';output;
X=7;Y=9;KOMA='▲銀将';output;
X=8;Y=9;KOMA='▲桂馬';output;
X=9;Y=9;KOMA='▲香車';output;
X=2;Y=8;KOMA='▲飛車';output;
X=8;Y=8;KOMA='▲角行';output;
X=1;Y=7;KOMA='▲歩兵';output;
X=2;Y=7;KOMA='▲歩兵';output;
X=3;Y=7;KOMA='▲歩兵';output;
X=4;Y=7;KOMA='▲歩兵';output;
X=5;Y=7;KOMA='▲歩兵';output;
X=6;Y=7;KOMA='▲歩兵';output;
X=7;Y=7;KOMA='▲歩兵';output;
X=8;Y=7;KOMA='▲歩兵';output;
X=9;Y=7;KOMA='▲歩兵';output;
X=1;Y=1;KOMA='△香車';output;
X=2;Y=1;KOMA='△桂馬';output;
X=3;Y=1;KOMA='△銀将';output;
X=4;Y=1;KOMA='△金将';output;
X=5;Y=1;KOMA='△王将';output;
X=6;Y=1;KOMA='△金将';output;
X=7;Y=1;KOMA='△銀将';output;
X=8;Y=1;KOMA='△桂馬';output;
X=9;Y=1;KOMA='△香車';output;
X=8;Y=2;KOMA='△飛車';output;
X=2;Y=2;KOMA='△角行';output;
X=1;Y=3;KOMA='△歩兵';output;
X=2;Y=3;KOMA='△歩兵';output;
X=3;Y=3;KOMA='△歩兵';output;
X=4;Y=3;KOMA='△歩兵';output;
X=5;Y=3;KOMA='△歩兵';output;
X=6;Y=3;KOMA='△歩兵';output;
X=7;Y=3;KOMA='△歩兵';output;
X=8;Y=3;KOMA='△歩兵';output;
X=9;Y=3;KOMA='△歩兵';output;
run;
ちなみに▲は先手を表し、△は後手を表す将棋の符号です。
で、任意のフォルダに、フリー素材画像のサイトから駒の画像をとってきて保存します。
先手と後手で画像の向きがひっくり返りますが、ここは単純に反転した画像を用意して使います。
(使用したサイト:http://sozai.7gates.net/docs/japanese-chess/)
(使用したサイト:http://sozai.7gates.net/docs/japanese-chess/)
でコードは
ods path(prepend) work.templat(update);
proc template;
define statgraph syogi;
begingraph;
symbolimage name=S_OU image='/folders/myfolders/koma/▲王将.png';
symbolimage name=S_HISYA image='/folders/myfolders/koma/▲飛車.png';
symbolimage name=S_KAKU image='/folders/myfolders/koma/▲角行.png';
symbolimage name=S_KINN image='/folders/myfolders/koma/▲金将.png';
symbolimage name=S_GINN image='/folders/myfolders/koma/▲銀将.png';
symbolimage name=S_KYOUSYA image='/folders/myfolders/koma/▲香車.png';
symbolimage name=S_KEIMA image='/folders/myfolders/koma/▲桂馬.png';
symbolimage name=S_HU image='/folders/myfolders/koma/▲歩兵.png';
symbolimage name=G_OU image='/folders/myfolders/koma/△王将.png';
symbolimage name=G_HISYA image='/folders/myfolders/koma/△飛車.png';
symbolimage name=G_KAKU image='/folders/myfolders/koma/△角行.png';
symbolimage name=G_KINN image='/folders/myfolders/koma/△金将.png';
symbolimage name=G_GINN image='/folders/myfolders/koma/△銀将.png';
symbolimage name=G_KYOUSYA image='/folders/myfolders/koma/△香車.png';
symbolimage name=G_KEIMA image='/folders/myfolders/koma/△桂馬.png';
symbolimage name=G_HU image='/folders/myfolders/koma/△歩兵.png';
discreteattrmap name='kifmap';
value '▲王将' / markerattrs=(symbol=S_OU);
value '▲飛車' / markerattrs=(symbol=S_HISYA);
value '▲角行' / markerattrs=(symbol=S_KAKU);
value '▲金将' / markerattrs=(symbol=S_KINN);
value '▲銀将' / markerattrs=(symbol=S_GINN);
value '▲香車' / markerattrs=(symbol=S_KYOUSYA);
value '▲桂馬' / markerattrs=(symbol=S_KEIMA);
value '▲歩兵' / markerattrs=(symbol=S_HU);
value '△王将' / markerattrs=(symbol=G_OU);
value '△飛車' / markerattrs=(symbol=G_HISYA);
value '△角行' / markerattrs=(symbol=G_KAKU);
value '△金将' / markerattrs=(symbol=G_KINN);
value '△銀将' / markerattrs=(symbol=G_GINN);
value '△香車' / markerattrs=(symbol=G_KYOUSYA);
value '△桂馬' / markerattrs=(symbol=G_KEIMA);
value '△歩兵' / markerattrs=(symbol=G_HU);
enddiscreteattrmap;
discreteattrvar attrvar=KOMAC var=KOMA attrmap='kifmap';
layout overlay /yaxisopts=(reverse=true griddisplay=ON
linearopts=(tickvaluesequence=(start=1 end=9 increment=1)))
xaxisopts=(reverse=true griddisplay=ON
linearopts=(tickvaluesequence=(start=1 end=9 increment=1)));
scatterplot x=X y=Y /group=KOMAC markerattrs=(size=25pt);
endlayout;
endgraph;
end;
proc sgrender template=syogi data=Q1;
run;
結果は
てな、感じです。まだマス目がうまく表現できてないので、今後の課題ですね。
最後に「新手一生」を掲げられた升田幸三の言葉を2つほど
『文化なんてしょせん無駄なもんなんだ。でもその明かりが、届く範囲の人に何かをもらたしている。将棋も同じじゃないか』
『若い頃、全く生産性のない「将棋指し」という職業に寂莫とした思いをしたことがあります。国家に、国民に、いかほどの貢献をしているだろうかと。でも、文化というものはムダから発展していくのだ、ということを会得してからは「将棋指し」に打ち込めるようになりました。』
IMLでセルオートマトンのコード紹介
他人様のネタですが、とても勉強になったので紹介。
SAS IMLのテクニック紹介ブログにセルオートマトンの表現法があがっていました。
有名なルール30ですね、なぜかある種の巻貝の模様と一致するという神秘。
http://blogs.sas.com/content/iml/2014/10/17/wolframs-rule-30/
いや、このコード、IML初心者の僕には、かなりキツいです。
取り敢えず、Nを5とかにして、rest print;入れて、全部中身を見ながら追ってなんとか、、。
肝心の法則式のところは、もはや、へ~、これでできてんだ。て程度の理解ですが。
しかし、凄い!
でもSAS Universityだとやたら時間かかるんだけど、僕のPCが貧弱なのか?
面白いな~、これ凄い面白い。
なんかIMLのデータ操作のエッセンスが詰まってるんで、これIML入門者用の教材にいいんじゃないですかね?
IML関連のテキストは、すぐ統計解析にいってしまって、基本操作が消化不良になりがちな気がしますから、こういうのいいかも。
こういうIMLの特性を活かした面白いコード持ってる人がいらっしゃれば、貼っていただければ嬉しいです。
SAS IMLのテクニック紹介ブログにセルオートマトンの表現法があがっていました。
有名なルール30ですね、なぜかある種の巻貝の模様と一致するという神秘。
http://blogs.sas.com/content/iml/2014/10/17/wolframs-rule-30/
いや、このコード、IML初心者の僕には、かなりキツいです。
取り敢えず、Nを5とかにして、rest print;入れて、全部中身を見ながら追ってなんとか、、。
肝心の法則式のところは、もはや、へ~、これでできてんだ。て程度の理解ですが。
しかし、凄い!
でもSAS Universityだとやたら時間かかるんだけど、僕のPCが貧弱なのか?
面白いな~、これ凄い面白い。
なんかIMLのデータ操作のエッセンスが詰まってるんで、これIML入門者用の教材にいいんじゃないですかね?
IML関連のテキストは、すぐ統計解析にいってしまって、基本操作が消化不良になりがちな気がしますから、こういうのいいかも。
こういうIMLの特性を活かした面白いコード持ってる人がいらっしゃれば、貼っていただければ嬉しいです。
SQLのサブクエリ(副問い合わせ)は習うより慣れろ?どこにでも書けるから取り敢えず書いてみようの話
サブクエリはSQL文の中にさらに括弧に入ったSQL文がいるよってやつです。
そのサブクエリの使い方がよくわかりませんと言われます。
話を聞いていると、特に相関サブクエリ、つまり主クエリとサブクエリに関係があって、主クエリの結果に対してサブの結果が連動して作用するようなやつが特に苦手な人多いなぁと思います。
SQL、特にそういう複雑なものについては、一通り理屈を本などで勉強したら、あとはもう、たくさん書いて、人が書いたものもたくさん読んで、理屈で理解するよりかは、イメージを体染みにつけるのが結局近道だと思います。
まず、SQLって何?って方はSAS忘備録のSQL入門を1から順番に読んでいくことをお勧めします。
http://sas-boubi.blogspot.jp/2014/04/sql1select.html
でサブクエリですが、よく、SELECT句、WHERE句、FROM句、どこに書いたらいいかわからないと質問されますが、乱暴にいっちゃうと、取り敢えず、どこに書いても何とかなるよ!です。
例えば
data Q1;
GRPNAME='A';ID='001';VAL=100;output;
GRPNAME='A';ID='002';VAL=90;output;
GRPNAME='A';ID='003';VAL=120;output;
GRPNAME='A';ID='004';VAL=80;output;
GRPNAME='A';ID='005';VAL=150;output;
GRPNAME='B';ID='006';VAL=70;output;
GRPNAME='B';ID='007';VAL=60;output;
GRPNAME='B';ID='008';VAL=180;output;
GRPNAME='B';ID='009';VAL=110;output;
GRPNAME='B';ID='010';VAL=190;output;
GRPNAME='C';ID='011';VAL=60;output;
GRPNAME='C';ID='012';VAL=120;output;
GRPNAME='C';ID='013';VAL=130;output;
GRPNAME='C';ID='014';VAL=50;output;
GRPNAME='C';ID='015';VAL=200;output;
run;
上記のようなデータがあるとします。A、B、Cの3つのグループに5人ずつ人がいて、VALという何らかのスコアを持っているとします。
上記のデータから、自分の所属するグループの平均スコアより、高いスコアを持っている人を抽出しなさいという問題を考えます。
まず基本的な考え方として、グループごとの平均をだすSQLは
proc sql;
select GRPNAME,avg(VAL) as AVGVAL
from Q1
group by GRPNAME;
quit;
な感じで、結果は
これをサブクエリとしてどう使うかですが、
まずはWHERE句に突っ込むパターン
proc sql noprint;
create table A1 as
select GRPNAME,ID,VAL
from Q1 as A
where VAL>(select avg(VAL)
from Q1 as B
group by GRPNAME
having A.GRPNAME=B.GRPNAME);
quit;
同じテーブルをあたかも違うテーブルとみなす自己参照はQ1 as A やQ1 as Bのように仮名(alias)を付与します。ちなみにasは省略可能ですが、慣れないうちは逐一つけた方がいいです。
having A.GRPNAME=B.GRPNAMEがポイントで、これによって、主クエリののGROUPNAMEの値と同じ値をもったサブクエリの集約結果が条件式にはまります。
つづいてSELECT句に突っ込むパターン
proc sql noprint;
create table A2 as
select GRPNAME,ID,VAL
,(select avg(VAL)
from Q1 as B
group by GRPNAME
having A.GRPNAME=B.GRPNAME) as AVGVAL
from Q1 as A
where VAL>calculated AVGVAL;
quit;
抽出されているIDを見ると先ほどの結果と同じであることがわかります。
基本的に考え方は先ほどの例と同じですが、ポイントはwhere VAL>calculated AVGVAL;の
calculatedの部分ですね。
calculatedの説明はSAS忘備録の「SQLの小技CALCULATEDキーワード」http://sas-boubi.blogspot.jp/2013/12/sqlcalculated.htmlをみていただければわかりますが、基本select句で新規に定義した変数でwher句の条件はかけないのですが、calculatedをつけるとOKなわけです。
さて最後にFROM句に突っ込む場合
proc sql noprint;
create table A3 as
select A.GRPNAME,ID,VAL
from Q1 as A
left outer join
(select GRPNAME,avg(val) as AVGVAL
from Q1
group by GRPNAME) as B
on A.GRPNAME=B.GRPNAME
where A.VAL>B.AVGVAL;
quit;
結果は
と、やはり同じです。
これは特に説明の必要ないですね。集計した結果と元の結果を結合して、それをFROMで指定して、抽出してるだけです。
さて、このように、どこに書いても、同じ結果を導くことができました。
あまり悩まずにどんどん書いて、どんどん失敗もして、勉強していきましょう。
ちなみにの話ですが、サブクエリは、処理速度の面からいうと最善ではない場合も多いので
少し留意しておきましょう。
そのサブクエリの使い方がよくわかりませんと言われます。
話を聞いていると、特に相関サブクエリ、つまり主クエリとサブクエリに関係があって、主クエリの結果に対してサブの結果が連動して作用するようなやつが特に苦手な人多いなぁと思います。
SQL、特にそういう複雑なものについては、一通り理屈を本などで勉強したら、あとはもう、たくさん書いて、人が書いたものもたくさん読んで、理屈で理解するよりかは、イメージを体染みにつけるのが結局近道だと思います。
まず、SQLって何?って方はSAS忘備録のSQL入門を1から順番に読んでいくことをお勧めします。
http://sas-boubi.blogspot.jp/2014/04/sql1select.html
でサブクエリですが、よく、SELECT句、WHERE句、FROM句、どこに書いたらいいかわからないと質問されますが、乱暴にいっちゃうと、取り敢えず、どこに書いても何とかなるよ!です。
例えば
data Q1;
GRPNAME='A';ID='001';VAL=100;output;
GRPNAME='A';ID='002';VAL=90;output;
GRPNAME='A';ID='003';VAL=120;output;
GRPNAME='A';ID='004';VAL=80;output;
GRPNAME='A';ID='005';VAL=150;output;
GRPNAME='B';ID='006';VAL=70;output;
GRPNAME='B';ID='007';VAL=60;output;
GRPNAME='B';ID='008';VAL=180;output;
GRPNAME='B';ID='009';VAL=110;output;
GRPNAME='B';ID='010';VAL=190;output;
GRPNAME='C';ID='011';VAL=60;output;
GRPNAME='C';ID='012';VAL=120;output;
GRPNAME='C';ID='013';VAL=130;output;
GRPNAME='C';ID='014';VAL=50;output;
GRPNAME='C';ID='015';VAL=200;output;
run;
上記のようなデータがあるとします。A、B、Cの3つのグループに5人ずつ人がいて、VALという何らかのスコアを持っているとします。
上記のデータから、自分の所属するグループの平均スコアより、高いスコアを持っている人を抽出しなさいという問題を考えます。
まず基本的な考え方として、グループごとの平均をだすSQLは
proc sql;
select GRPNAME,avg(VAL) as AVGVAL
from Q1
group by GRPNAME;
quit;
な感じで、結果は
これをサブクエリとしてどう使うかですが、
まずはWHERE句に突っ込むパターン
proc sql noprint;
create table A1 as
select GRPNAME,ID,VAL
from Q1 as A
where VAL>(select avg(VAL)
from Q1 as B
group by GRPNAME
having A.GRPNAME=B.GRPNAME);
quit;
で結果は
同じテーブルをあたかも違うテーブルとみなす自己参照はQ1 as A やQ1 as Bのように仮名(alias)を付与します。ちなみにasは省略可能ですが、慣れないうちは逐一つけた方がいいです。
having A.GRPNAME=B.GRPNAMEがポイントで、これによって、主クエリののGROUPNAMEの値と同じ値をもったサブクエリの集約結果が条件式にはまります。
つづいてSELECT句に突っ込むパターン
proc sql noprint;
create table A2 as
select GRPNAME,ID,VAL
,(select avg(VAL)
from Q1 as B
group by GRPNAME
having A.GRPNAME=B.GRPNAME) as AVGVAL
from Q1 as A
where VAL>calculated AVGVAL;
quit;
で結果は
抽出されているIDを見ると先ほどの結果と同じであることがわかります。
基本的に考え方は先ほどの例と同じですが、ポイントはwhere VAL>calculated AVGVAL;の
calculatedの部分ですね。
calculatedの説明はSAS忘備録の「SQLの小技CALCULATEDキーワード」http://sas-boubi.blogspot.jp/2013/12/sqlcalculated.htmlをみていただければわかりますが、基本select句で新規に定義した変数でwher句の条件はかけないのですが、calculatedをつけるとOKなわけです。
さて最後にFROM句に突っ込む場合
proc sql noprint;
create table A3 as
select A.GRPNAME,ID,VAL
from Q1 as A
left outer join
(select GRPNAME,avg(val) as AVGVAL
from Q1
group by GRPNAME) as B
on A.GRPNAME=B.GRPNAME
where A.VAL>B.AVGVAL;
quit;
結果は
と、やはり同じです。
これは特に説明の必要ないですね。集計した結果と元の結果を結合して、それをFROMで指定して、抽出してるだけです。
さて、このように、どこに書いても、同じ結果を導くことができました。
あまり悩まずにどんどん書いて、どんどん失敗もして、勉強していきましょう。
ちなみにの話ですが、サブクエリは、処理速度の面からいうと最善ではない場合も多いので
少し留意しておきましょう。
登録:
投稿 (Atom)