結構、よく質問されるのが、データステップ中で新しく追加された変数か、
読み込んだデータセットに元からあった変数どうかを判別したいみたいな話です。
いちいち、データセットの仕様書つくるの面倒くさいから
先にプログラム書いちゃって、その実行ついでに新規変数がどうやって生成されているかを
取得して、仕様書を自動生成しちゃえば楽だよなぁみたいな、おバカなこと考えたことありません?
え、いや、僕はないですけどね。
ところが、SASの場合、これが結構ハードで厳しい。
手抜きするつもりが、真面目にやる1000倍くらい時間がかかったりするのもよくある話です
しかも、いまだに完全に納得できる方法が思いついてません
誰か教えてください。
とりあえず、前後のデータセットをコンペアしてその結果を読み込むという
面倒すぎる方法は除外して、1ステップ内で、本処理のついでにできる方法に限定します。
僕が考えた案を2つほど。
以下のデータセットがあります。
変数 a b c dがあります。
data Q1;
a=1;b=2;c=1;d='A';
run;
まず一つ目の方法は以下です。
data A1;
set Q1;
array an _numeric_;
array cn _character_;
/*新しい変数 e f g 作る*/
e=1;
f='b';
g=a+b;
/*チェックしてみる*/
flg_a=ifc(vinarray(a),'元','新');
flg_b=ifc(vinarray(b),'元','新');
flg_c=ifc(vinarray(c),'元','新');
flg_d=ifc(vinarray(d),'元','新');
flg_e=ifc(vinarray(e),'元','新');
flg_f=ifc(vinarray(f),'元','新');
flg_g=ifc(vinarray(g),'元','新');
put (flg_a--flg_g) (/=);
run;
判定結果をputしているので、それを見てみると
追加した3つの変数が正しく判定されているのがわかります。
なにをしているかというと、最初に数値型、文字型それぞれ全変数を
配列に定義しちゃいます。
で、その後、割り当てステートメントがはいるわけですが、
ここで新規に割り付けられた変数は、最初に定義している配列には含まれていない
わけです。
そこで、判定の箇所でvinarray関数という、引数の変数が何かしらの配列に属しているか
どうかを1 , 0で返すニッチな関数で判定します。
配列に入ってないということは、新しい変数だろってノリで。
しかしまあ、お気づきの方もいるでしょうが、これは新しく作る変数を配列に定義しているような
プログラムでは正しく判定できない限定的な方法ですね。
e f gが何の配列にもたまたま定義されていないからそれでいけてるわけです。
同じく、新規変数をarrayで生成したりしても判別不能になります。
(他にも弱点はありますがそれは後で)
次に思いついたのはハッシュオブジェクトです。
data A2;
set Q1;
/*この時点での全変数の情報を一旦ハッシュオブジェクトにいれる*/
if _N_=1 then do;
declare hash h1();
h1.definekey('vname');
h1.definedone();
array an _numeric_;
array cn _character_;
do over an;
vname=vname(an);
h1.add();
end;
do over cn;
vname=vname(cn);
h1.add();
end;
drop vname;
end;
/*新しい変数 e f g 作る*/
e=1;
f='b';
g=a+b;
/*チェックしてみる*/
flg_a=ifc(h1.check(key:vname(a)),'新','元');
flg_b=ifc(h1.check(key:vname(b)),'新','元');
flg_c=ifc(h1.check(key:vname(c)),'新','元');
flg_d=ifc(h1.check(key:vname(d)),'新','元');
flg_e=ifc(h1.check(key:vname(e)),'新','元');
flg_f=ifc(h1.check(key:vname(f)),'新','元');
flg_g=ifc(h1.check(key:vname(g)),'新','元');
put (flg_a--flg_g) (/=);
run;
結果は同じなので割愛。
最初のレコードの時点のPDVの変数名情報をハッシュオブジェクトに格納して
ステップの終わりで、checkメソッドで、存在するかどうかを判定する方法
(さっきと逆に、あれば0,なければエラーコードとして0以外の数字が戻ります)
これならば、ステップの途中で新規変数に配列つかったりしてても問題なしです。
ただ、2つの方法とも、致命的に弱点なのがあくまでステップ開始時の
PDVにある変数を、元からある変数だと考えている点です。
つまり length f $100.;といったようにlengthやformatステートメントなど
データを読み込む前に箱を定義しちゃうようなものがはいると、fは実際は元データセットに無いけど
元からある変数ってことになるんですね。
ここが僕の限界。
う~ん、どうするのが正解なんでしょう。
contentsプロシジャとかsqlとかで、ガチで大元のデータセット情報読み込むコードを
作ってステップ内でdosubl関数で回すとか?
あるいは新規データセットのPDVに影響受けずに変数名をとれるのかな?
if _N_=0の状態でvnextルーチン回したりできんのかな?
誰か教えてください
ひとつのマクロステップ内で重複しなければ何でもいい一時的な値をもつマクロ変数&sysindex。 たとえばデータステップ内でツール的に利用するマクロなんかで、配列名やハッシュオブジェクト名みたいに1データステップ内で同名存在ができないものを生成するケースなどに使えます って話
(追記-最初にあげたときマクロの内容や画像が派手に間違えてました。ごめんなさい。
同時に指摘いただいた4名の方々有難うございます。ちゃんと見てる人いるんですね、反省します..)
こないだ電車乗ってたら、ブログに長いタイトルつける奴は仕事できない奴だっていってるおじさんがいました。要点がまとめれていないからだそうです。僕もそう思います。
さて、もう言いたいことはタイトルで言ってしまったので、これ以上何もないのですが説明します。
例えば以前の記事で、SASには現状、文字型の非欠損値をカウントする変数がないって話をしました
http://sas-tumesas.blogspot.jp/2015/02/blog-post.html
それをマクロをサブルーチン的に使って解決しようと考えたとします。
以下のようなデータがあり
data Q1;
A='';B='a';C='a';D='a';E='a';output;
A='a';B='';C='a';D='';E='a';output;
A='';B='a';C='';D='a';E='';output;
A='a';B='a';C='a';D='a';E='a';output;
run;
以下のようにマクロを組んでみました。
%macro ar(varlist);
array AR $ &varlist;
CN=0;
do over AR;
CN + ^missing(AR);
end;
drop CN;
%mend ar;
早速そのマクロを使って、変数A B Cのうち値があるものの数をカウントしてみます。
options mprint;
data A1;
set Q1;
%ar(A B C)
count_A_B_C=CN;
run;
はい、できました。
ちなみにoptions mprintをつけたのでログにはこんな感じで展開されたものが
でています
そこで調子にのって、同じステップなの中でC D Eに対してもやってみようとします
data A2;
set Q1;
%ar(A B C)
count_A_B_C=CN;
%ar(C D E)
count_C_D_E=CN;
run;
はい、エラーになりました。
これはarマクロの中でarrayステートメントで配列名「AR」を生成してるんですが
arマクロを2回以上入れると、マクロが展開されたとき、1つのデータステップの中で配列「AR」を
複数回宣言することになります。そしてそれはSASの文法上NGなので通りませんぜと。
じゃあ、どうするか。
このマクロにとって、配列は、カウント処理の際のみに必要なものであって
CNが計算できた後は意味がなく、配列の名前についてもどうでもいいわけです。
たとえば、引数の変数名を全部文字連結して配列名にするようにしてもいいですが
配列名は32文字までなので、長い変数をたくさん指定すればエラーになってしまいます。
乱数とかでもいいですが、たまたま衝突する可能性は、まあ一応ゼロではありません。
じゃあ、パラメーターひとつ増やして、使用時に毎回配列名も指定させます?
使い捨ての名前にそこまで手を煩わせたくないな~
じゃあ、どうするのか?
ここでsysindexを使います。こいつはsasを開いてから閉じるまでの
sasセッションの中で、呼び出される都度+1された連番が入るので
決して重複しないという性質をもっています
以下のようにマクロを書き直しました。
%macro ar2(varlist);
array AR&sysindex $ &varlist;
CN=0;
do over AR&sysindex;
CN + ^missing(AR&sysindex);
end;
drop CN;
%mend ar2;
では再度実行してみます。
data A3;
set Q1;
%ar2(A B C)
count_A_B_C=CN;
%ar2(C D E)
count_C_D_E=CN;
run;
無事できました。
ログを見てみると、さっきと違ってAR5とAR6というように
勝手に配列名に連番が付与されていることで、名前の衝突が回避されていることが
わかります。
ちなみに、なぜ5から始まっているかというと
まず最初何もしていない状態が1で
最初データセットA1を作った際に一回%arを呼び出したので
ここで+1されて2になってます。
続いて、結局エラーになりましたがA2で二回呼び出しているので
+1 +1 で4になってます。
だから今回5なんですね。ユーザーが意識しようがしまいが内部的に自動カウント
されているわけです。
SASはfcmpプロシジャの機能がアレなんで、マクロを関数やサブルーチンなどステップ内での
ツール的に使いたいケースも多いんですが、そういう時に知ってると役立ちます
同時に指摘いただいた4名の方々有難うございます。ちゃんと見てる人いるんですね、反省します..)
こないだ電車乗ってたら、ブログに長いタイトルつける奴は仕事できない奴だっていってるおじさんがいました。要点がまとめれていないからだそうです。僕もそう思います。
さて、もう言いたいことはタイトルで言ってしまったので、これ以上何もないのですが説明します。
例えば以前の記事で、SASには現状、文字型の非欠損値をカウントする変数がないって話をしました
http://sas-tumesas.blogspot.jp/2015/02/blog-post.html
それをマクロをサブルーチン的に使って解決しようと考えたとします。
以下のようなデータがあり
data Q1;
A='';B='a';C='a';D='a';E='a';output;
A='a';B='';C='a';D='';E='a';output;
A='';B='a';C='';D='a';E='';output;
A='a';B='a';C='a';D='a';E='a';output;
run;
以下のようにマクロを組んでみました。
%macro ar(varlist);
array AR $ &varlist;
CN=0;
do over AR;
CN + ^missing(AR);
end;
drop CN;
%mend ar;
早速そのマクロを使って、変数A B Cのうち値があるものの数をカウントしてみます。
options mprint;
data A1;
set Q1;
%ar(A B C)
count_A_B_C=CN;
run;
はい、できました。
ちなみにoptions mprintをつけたのでログにはこんな感じで展開されたものが
でています
data A2;
set Q1;
%ar(A B C)
count_A_B_C=CN;
%ar(C D E)
count_C_D_E=CN;
run;
はい、エラーになりました。
これはarマクロの中でarrayステートメントで配列名「AR」を生成してるんですが
arマクロを2回以上入れると、マクロが展開されたとき、1つのデータステップの中で配列「AR」を
複数回宣言することになります。そしてそれはSASの文法上NGなので通りませんぜと。
じゃあ、どうするか。
このマクロにとって、配列は、カウント処理の際のみに必要なものであって
CNが計算できた後は意味がなく、配列の名前についてもどうでもいいわけです。
たとえば、引数の変数名を全部文字連結して配列名にするようにしてもいいですが
配列名は32文字までなので、長い変数をたくさん指定すればエラーになってしまいます。
乱数とかでもいいですが、たまたま衝突する可能性は、まあ一応ゼロではありません。
じゃあ、パラメーターひとつ増やして、使用時に毎回配列名も指定させます?
使い捨ての名前にそこまで手を煩わせたくないな~
じゃあ、どうするのか?
ここでsysindexを使います。こいつはsasを開いてから閉じるまでの
sasセッションの中で、呼び出される都度+1された連番が入るので
決して重複しないという性質をもっています
以下のようにマクロを書き直しました。
%macro ar2(varlist);
array AR&sysindex $ &varlist;
CN=0;
do over AR&sysindex;
CN + ^missing(AR&sysindex);
end;
drop CN;
%mend ar2;
では再度実行してみます。
data A3;
set Q1;
%ar2(A B C)
count_A_B_C=CN;
%ar2(C D E)
count_C_D_E=CN;
run;
勝手に配列名に連番が付与されていることで、名前の衝突が回避されていることが
わかります。
ちなみに、なぜ5から始まっているかというと
まず最初何もしていない状態が1で
最初データセットA1を作った際に一回%arを呼び出したので
ここで+1されて2になってます。
続いて、結局エラーになりましたがA2で二回呼び出しているので
+1 +1 で4になってます。
だから今回5なんですね。ユーザーが意識しようがしまいが内部的に自動カウント
されているわけです。
SASはfcmpプロシジャの機能がアレなんで、マクロを関数やサブルーチンなどステップ内での
ツール的に使いたいケースも多いんですが、そういう時に知ってると役立ちます
9.4から登場した拡張属性(Extended Attribute)はあんまり話題になんないねって話
昔、とある場所でデータセットをみていた際、変数に全部に長~いラベルがついてました
例えば、[XX_日付型_データAA由来] のようにその変数の元の参照先や、詳細な型について記載されてました。
誰がみても、その変数の詳細がわかるようにと、義務づけられてるそうです。
まあ、確かにSASって文字型と数値型しかないので、普通にプロシジャに数値型全指定でかけたら
日付もIDも集計してくれたりするし、データセットいじってる内に、この変数ってどっからきたの?みたいなこともあります。
まあ、定義書で管理すればいいんじゃないって気がしてましたし、
ラベルでそういうのを管理するのは限界があるよな~って思いました。
データセット自体にXMLのように自由に属性つけれれば管理がはかどるのにって思う方もいるのでしょう。
そんなあなたに朗報。9.4から自分で好きに属性を定義して、それをデータセットに付与することが
できるようになりました。
それが拡張属性です!
9.4で登場した新機能のひとつですが、DS2やFedSQLに輪をかけて影が薄い!!
誰も話題にしてないこと、この上なしです。
例えば以下のようなデータセットがあるとします。
data Q1;
x=1;y=19562;z=320;output;
x=2;y=19566;z=280;output;
x=3;y=19569;z=120;output;
format y yymmdds10.;
run;
これに、その変数の説明と、参照元、型の補足(日付なのか、IDとかみたいに質的なものなのかとか)を
それぞれ
detail source cate という適当な属性を作って付与してみようと思います。
以下を実行します
proc datasets nolist;
modify Q1;
xattr add var x (detail="ユーザーID" source='登録情報' cate="ID")
y (detail="XX日" source='20XX年情報' cate='日付')
z (detail="売り上げ(千円)" source='売り上げ情報' cate="量的変数")
;
run;
quit;
拡張属性はいまのとこ、proc datesetsでしか作れないみたいです。attribステートメントとかでも
作れればいいのにね。
文法は上記のとおりで、xattr add varで変数に対する拡張属性を付与できます。
ちなみにvarのところをdsにすると、データセットにも属性を定義できます。
脱線しますが、ラベルにもあまり使われていないですが、データセットラベルというのが
昔からありました。
「データセットラベルの話」
http://sas-tumesas.blogspot.jp/2014/08/blog-post_31.html
で、話を戻して、拡張属性を付与したら、そのデータセットをcontentsプロシジャで確認してみましょう。
(ods ooutputは後で使うのでおまけです)
ods output ExtendedAttributesVar=xattr_ds;
proc contents data=Q1;
run;
このように、付与した拡張属性を参照することができました。
例えばこれを使って、拡張属性で「cate」の値が「量的変数」の変数のみを
meansにかけたいな~と思ったら、さっきods outputでデータセット化しておいたものを
使って
data _null_;
length target $1000.;
set xattr_ds end=eof;
retain target;
where ExtendedAttribute='cate' & AttributeCharValue='量的変数';
target=catx(' ',target,attributevariable);
if eof then call symputx('target',target);
run;
proc means data=Q1;
var ⌖
run;
上記のようにかけるわけですね。
拡張属性、バリバリ利用してますって方がいれば、どんなことしてるか是非教えてほしいです
例えば、[XX_日付型_データAA由来] のようにその変数の元の参照先や、詳細な型について記載されてました。
誰がみても、その変数の詳細がわかるようにと、義務づけられてるそうです。
まあ、確かにSASって文字型と数値型しかないので、普通にプロシジャに数値型全指定でかけたら
日付もIDも集計してくれたりするし、データセットいじってる内に、この変数ってどっからきたの?みたいなこともあります。
まあ、定義書で管理すればいいんじゃないって気がしてましたし、
ラベルでそういうのを管理するのは限界があるよな~って思いました。
データセット自体にXMLのように自由に属性つけれれば管理がはかどるのにって思う方もいるのでしょう。
そんなあなたに朗報。9.4から自分で好きに属性を定義して、それをデータセットに付与することが
できるようになりました。
それが拡張属性です!
9.4で登場した新機能のひとつですが、DS2やFedSQLに輪をかけて影が薄い!!
誰も話題にしてないこと、この上なしです。
例えば以下のようなデータセットがあるとします。
data Q1;
x=1;y=19562;z=320;output;
x=2;y=19566;z=280;output;
x=3;y=19569;z=120;output;
format y yymmdds10.;
run;
これに、その変数の説明と、参照元、型の補足(日付なのか、IDとかみたいに質的なものなのかとか)を
それぞれ
detail source cate という適当な属性を作って付与してみようと思います。
以下を実行します
proc datasets nolist;
modify Q1;
xattr add var x (detail="ユーザーID" source='登録情報' cate="ID")
y (detail="XX日" source='20XX年情報' cate='日付')
z (detail="売り上げ(千円)" source='売り上げ情報' cate="量的変数")
;
run;
quit;
拡張属性はいまのとこ、proc datesetsでしか作れないみたいです。attribステートメントとかでも
作れればいいのにね。
文法は上記のとおりで、xattr add varで変数に対する拡張属性を付与できます。
ちなみにvarのところをdsにすると、データセットにも属性を定義できます。
脱線しますが、ラベルにもあまり使われていないですが、データセットラベルというのが
昔からありました。
「データセットラベルの話」
http://sas-tumesas.blogspot.jp/2014/08/blog-post_31.html
で、話を戻して、拡張属性を付与したら、そのデータセットをcontentsプロシジャで確認してみましょう。
(ods ooutputは後で使うのでおまけです)
ods output ExtendedAttributesVar=xattr_ds;
proc contents data=Q1;
run;
このように、付与した拡張属性を参照することができました。
例えばこれを使って、拡張属性で「cate」の値が「量的変数」の変数のみを
meansにかけたいな~と思ったら、さっきods outputでデータセット化しておいたものを
使って
data _null_;
length target $1000.;
set xattr_ds end=eof;
retain target;
where ExtendedAttribute='cate' & AttributeCharValue='量的変数';
target=catx(' ',target,attributevariable);
if eof then call symputx('target',target);
run;
proc means data=Q1;
var ⌖
run;
上記のようにかけるわけですね。
拡張属性、バリバリ利用してますって方がいれば、どんなことしてるか是非教えてほしいです
gsubmitでSASメニューをカスタマイズしたおす話
ちょっと身の回りで色々とあったりしてすっかり更新が滞ってしまってました。
僕自身は相変わらず元気です。
連絡いただいているのに返せていない方、ごめんなさい。少し落ち着いたので徐々に
返信させていただきます。不義理な真似してすみませんでした。
あと、掲示板の方を、amatsuさんに丸投げしているようで、本当にすみません。
いつも見てはいるんですが、ついつい甘えてしまってました。
さて、いつものことですがかなり需要のない話をします。
なんかデータセットがあったとして
data Q1;
x=1;y='A';output;
x=2;y='B';output;
run;
それをSAS上で、選択して右クリックすると以下のようなメニューがでてきます。

これ見て、「もっとマシなメニューにしろよ」って怒りを感じるのは、捻くれた僕だけですか?
しかもEXCELで表示とかいいながら、内部的に実行されているコードは以下のとおりで
filename _temp_ "XXXX.xls";
ods noresults;
ods listing close;
ods html file=_temp_ rs=none style=minimal;
proc print data=Work.'Q1'N label noobs;
run;
ods html close;
ods results;
ods listing;
filename _temp_;
ods htmlで無理やりだしてるだけやん。って感じですね。純粋なEXCELじゃないから
警告出てきてうざいし、そのまま人にだすのがためらわれて嫌なんです。
とはいえ、ここのメニューをどうこうしたりとかは SAS/AF みたいなアプリケーション開発用の
ライセンスないとできないんでしょって思ってました。
ところがなんとできたんです。
手順は「えぇ?」って感じなんですが、覚えれば簡単です。
(9.4は試してないのでわからないのですが9.13-9.3までは以下の手順でできるはずです)
まず、拡張エディタがアクティブな状態で
ツールバーの「表示」で「コンテンツのみ」をクリックします。何もおきませんけど必要なんです

次に「ツール」で「オプション」→「エクスプローラ」を選択します
※この「エクスプローラー」はコンテンツのみを選択した直後しか表示されない
隠し項目になります

ここで「メンバー」タブの「テーブル」を選択して、「編集」ボタンをおします

そこで「追加」をクリックします

アクションの部分に「FREQプロシージャにかける」
アクションコマンドの部分に以下のコードを書いてください。

gsubmit "proc freq data=%8b.'%32b'N;run;"
あとはOK OK OKでウインドウを閉じます。
そしたら、再度さっきのデータセットを選択して、右クリックします。
すると以下のようにメニューが追加されていることがわかります。

そしてそれを実行してみると、

のように選択したデータセットがFREQプロシージャにかけられました。
これを応用すれば色々できます。
たとえば、さっき文句をつけたエクセル表示の場合
アクション「ちゃんとしたEXCELで表示」
アクションコマンド「
gsubmit "proc export data= %8b.'%32b'N outfile= 'c:\tmp.xlsx' dbms=excel label replace;newfile=yes;run;options noxwait noxsync;x 'c:\tmp.xlsx';"
」
とすれば、proc exportで出力したEXCELファイルが開きます
(そもそもproc exportの仕様自体に言いたいことは多いですが...)
で、肝心のアクションコマンドの書き方ですが
%8b.'%32b'Nで、ユーザーが選択したライブラリの選択したデータセットみたいな意味になります。
ここを普通のデータセット名前にしてしまうと、どれ選んで実行しても同じデータセット開いちゃいますからね。
で、どうもアクションコマンドは255までしか記述できないみたいです。
あとマクロを中で使いたい場合は%%testのように%を二度付けするとうまくいくようです。
このあたりに詳しい方がいれば逆に教えてください。情報なさすぎです。
ちなみに、この変の話は、大昔にもちょっと触れていたことがありましたので参考まで
〔記事〕
SASデータセットを開いた時にラベル名ではなく変数名を表示するのをデフォルト設定にする
http://sas-tumesas.blogspot.jp/2013/09/sas_20.html
また、当分、更新ペースはこんな感じか、下手したらさらに遅くなると思いますが
来年もよろしくお願いします
僕自身は相変わらず元気です。
連絡いただいているのに返せていない方、ごめんなさい。少し落ち着いたので徐々に
返信させていただきます。不義理な真似してすみませんでした。
あと、掲示板の方を、amatsuさんに丸投げしているようで、本当にすみません。
いつも見てはいるんですが、ついつい甘えてしまってました。
さて、いつものことですがかなり需要のない話をします。
なんかデータセットがあったとして
data Q1;
x=1;y='A';output;
x=2;y='B';output;
run;
それをSAS上で、選択して右クリックすると以下のようなメニューがでてきます。

これ見て、「もっとマシなメニューにしろよ」って怒りを感じるのは、捻くれた僕だけですか?
しかもEXCELで表示とかいいながら、内部的に実行されているコードは以下のとおりで
filename _temp_ "XXXX.xls";
ods noresults;
ods listing close;
ods html file=_temp_ rs=none style=minimal;
proc print data=Work.'Q1'N label noobs;
run;
ods html close;
ods results;
ods listing;
filename _temp_;
ods htmlで無理やりだしてるだけやん。って感じですね。純粋なEXCELじゃないから
警告出てきてうざいし、そのまま人にだすのがためらわれて嫌なんです。
とはいえ、ここのメニューをどうこうしたりとかは SAS/AF みたいなアプリケーション開発用の
ライセンスないとできないんでしょって思ってました。
ところがなんとできたんです。
手順は「えぇ?」って感じなんですが、覚えれば簡単です。
(9.4は試してないのでわからないのですが9.13-9.3までは以下の手順でできるはずです)
まず、拡張エディタがアクティブな状態で
ツールバーの「表示」で「コンテンツのみ」をクリックします。何もおきませんけど必要なんです

次に「ツール」で「オプション」→「エクスプローラ」を選択します
※この「エクスプローラー」はコンテンツのみを選択した直後しか表示されない
隠し項目になります

ここで「メンバー」タブの「テーブル」を選択して、「編集」ボタンをおします

そこで「追加」をクリックします

アクションの部分に「FREQプロシージャにかける」
アクションコマンドの部分に以下のコードを書いてください。

gsubmit "proc freq data=%8b.'%32b'N;run;"
あとはOK OK OKでウインドウを閉じます。
そしたら、再度さっきのデータセットを選択して、右クリックします。
すると以下のようにメニューが追加されていることがわかります。

そしてそれを実行してみると、

のように選択したデータセットがFREQプロシージャにかけられました。
これを応用すれば色々できます。
たとえば、さっき文句をつけたエクセル表示の場合
アクション「ちゃんとしたEXCELで表示」
アクションコマンド「
gsubmit "proc export data= %8b.'%32b'N outfile= 'c:\tmp.xlsx' dbms=excel label replace;newfile=yes;run;options noxwait noxsync;x 'c:\tmp.xlsx';"
」
とすれば、proc exportで出力したEXCELファイルが開きます
(そもそもproc exportの仕様自体に言いたいことは多いですが...)
で、肝心のアクションコマンドの書き方ですが
%8b.'%32b'Nで、ユーザーが選択したライブラリの選択したデータセットみたいな意味になります。
ここを普通のデータセット名前にしてしまうと、どれ選んで実行しても同じデータセット開いちゃいますからね。
で、どうもアクションコマンドは255までしか記述できないみたいです。
あとマクロを中で使いたい場合は%%testのように%を二度付けするとうまくいくようです。
このあたりに詳しい方がいれば逆に教えてください。情報なさすぎです。
ちなみに、この変の話は、大昔にもちょっと触れていたことがありましたので参考まで
〔記事〕
SASデータセットを開いた時にラベル名ではなく変数名を表示するのをデフォルト設定にする
http://sas-tumesas.blogspot.jp/2013/09/sas_20.html
また、当分、更新ペースはこんな感じか、下手したらさらに遅くなると思いますが
来年もよろしくお願いします
standardプロシジャやstdizeプロシジャで色んな標準化の話
なんかスコアデータみたいのがあって、最低スコアを0にして
最大スコアを100になるように値を変換してということがありました。
z得点だしたり、偏差値だしたり、はしょっちゅうするんで同じノリでproc standardでできるかなと
思ったけど、よく考えてみると、あれ?ってなりました。
proc standardで行ういわゆる標準化は、平均と標準偏差が一定の値になるように変換するわけで
今回みたいに無理やり最低値と最高値を決まった上限下限値にするのと全然違うよな~、平均も標準偏差も
使わないし。
もういいや~、面倒だからsummaryで各変数の最大、最小だして
変換値 =(元の値 - 最小値) / (最大値 - 最小値) * (設定上限値 - 設定下限値 ) + 設定下限値
でデータステップで片づけてしまいました。
で後で、対応するプロシジャってなんになるんだろう、多分stdizeだろうなと思って調べると
やぱりstdizeでした。
stdizeは広い意味での様々な標準化変換を行えるもので、かなり奥が深いです。
さわりだけ見てみましょう
今、以下のようなデータがあります
data q1;
x=1;y=20;z=-5;output;
x=6;y=10;z=5;output;
x=4;y=0;z=-1;output;
x=8;y=0;z=-3;output;
x=2;y=100;z=8;output;
run;
とりあえず基本統計量を見てみましょう
proc means data = q1 n mean std max min median sum maxdec=3;
run;
例えば、データを中心化したいから、平均0に寄せてと言われたら
以下のように各変数の平均を引けばいいわけです。
data q2;
set q1;
x = x - 4.2;
y = y - 26;
z = z - 0.8;
run;
中身と統計量は以下になります。ちゃんと0になってますね
これをstandardプロシジャで行う場合は以下のように書きます
m=0というのが平均0にしている部分ですね
proc standard data=q1 out=a1 m=0;
var x y z;
run;
同じ結果になることが確認できました。
次はいわゆる偏差値です std=は標準偏差の指定です
proc standard data=q1 out=a2 m=50 std=10;
var x y z;
run;
確認
つづいてm=0 std=1 でz得点 標準得点ってやつですね
proc standard data=q1 out=a2 m=0 std=1;
var x y z;
run;
さて一方stdizeプロシジャとはなんなのかという話で、
まずは以下のように何も指定せずにまわしてみます
proc stdize data= q1 out = a3;
var x y z;
run;
さっきstandardプロシジャでzスコアだしたのと同じですね。
つまり何も指定しないとデフォルトで標準得点に変換するということがわかりました
実は、何も指定しなかった場合、内部的には以下のコードを実行しているのと同じになります
proc stdize data= q1 out = a3
method=std
add =0
mult=1
;
var x y z;
run;
method=std というのが標準偏差を使った標準化(変な言い方ですが)であること
add はこの場合、指定する平均
multiは指定する標準偏差になります
ですので偏差値をだすのであれば以下のようにかけます。
methodは省略可です。
proc stdize data= q1 out = a4
method=std
add=50
mult=10
;
var x y z;
run;
さて、ここまでだとstandardプロシジャと何が違うんだという話ですが
stdizeの味噌はmethod=の箇所にいろいろ指定できて、広い意味でのいろんな基準化ができるというところです
例えば冒頭の例のように0から100にデータを変換する場合
変換値 =(元の値 - 最小値) / (最大値 - 最小値) * (設定上限値 - 設定下限値 ) + 設定下限値にしたがって
data q3;
set q1;
x = ( x - 1 ) / ( 8 - 1 ) * (100 - 0) + 0;
y = ( y - 0 ) / ( 100 - 0 ) * (100 - 0) + 0;
z = ( z -(-5) ) / ( 8 - (-5) ) * (100 - 0) + 0;
run;
と書くところを、stdizeであれば以下のようにできます
mehodがrangeになっているところがポイントです
proc stdize data= q1
out = a5
method=range
add = 0
mult = 100
;
var x y z;
run;
結果は同じで
最低値20 最大値100であれば
data q4;
set q1;
x = ( x - 1 ) / ( 8 - 1 ) * (100 - 20) + 20;
y = ( y - 0 ) / ( 100 - 0 ) * (100 - 20) + 20;
z = ( z -(-5) ) / ( 8 - (-5) ) * (100 - 20) + 20;
run;
を以下のように指定できます。
proc stdize data= q1
out = a6
method=range
add = 20
mult = 80
;
var x y z;
run;
stdizeプロシジャの挙動は、わかると明快で
一貫した以下のルールに従って変換を行うんですね。
変換値 = add指定値 + mult指定値 ×[(元の値 - location(method毎に決まってる)/scale(method毎に決まってる]
method=stdの場合locationは平均値 scaleは標準偏差になります
method=rangeの場合locationは最小値 scaleはレンジになります
メソッドごとのlocationとscaleの設定は以下にまとまっております
http://support.sas.com/documentation/cdl/en/statug/63033/HTML/default/viewer.htm#statug_stdize_sect012.htm
methodに関わらず addのデフォルトは0 multのデフォルトは1になります。
なので、例えば中央値を使って標準化をする場合
proc stdize data= q1
out = a7
method=median
add = 50
;
var x y z;
run;
最後に平均と標準偏差を使った標準化に対して
ノンパラ版として平均の代わりに中央値、標準偏差の代わりに正規化四分位範囲を使った方法が工業分野とかでよくあるらしいです。
すみません、まったくアホなんで詳しくないですが
四分位範囲はmethod=IQR で指定できるので、それに正規化のnormオプションを付けて
proc stdize data= q1
out = a8
method=iqr
norm
;
var x y z;
run;
な感じですかね
最大スコアを100になるように値を変換してということがありました。
z得点だしたり、偏差値だしたり、はしょっちゅうするんで同じノリでproc standardでできるかなと
思ったけど、よく考えてみると、あれ?ってなりました。
proc standardで行ういわゆる標準化は、平均と標準偏差が一定の値になるように変換するわけで
今回みたいに無理やり最低値と最高値を決まった上限下限値にするのと全然違うよな~、平均も標準偏差も
使わないし。
もういいや~、面倒だからsummaryで各変数の最大、最小だして
変換値 =(元の値 - 最小値) / (最大値 - 最小値) * (設定上限値 - 設定下限値 ) + 設定下限値
でデータステップで片づけてしまいました。
で後で、対応するプロシジャってなんになるんだろう、多分stdizeだろうなと思って調べると
やぱりstdizeでした。
stdizeは広い意味での様々な標準化変換を行えるもので、かなり奥が深いです。
さわりだけ見てみましょう
今、以下のようなデータがあります
data q1;
x=1;y=20;z=-5;output;
x=6;y=10;z=5;output;
x=4;y=0;z=-1;output;
x=8;y=0;z=-3;output;
x=2;y=100;z=8;output;
run;
とりあえず基本統計量を見てみましょう
proc means data = q1 n mean std max min median sum maxdec=3;
run;
例えば、データを中心化したいから、平均0に寄せてと言われたら
以下のように各変数の平均を引けばいいわけです。
data q2;
set q1;
x = x - 4.2;
y = y - 26;
z = z - 0.8;
run;
中身と統計量は以下になります。ちゃんと0になってますね
m=0というのが平均0にしている部分ですね
proc standard data=q1 out=a1 m=0;
var x y z;
run;
同じ結果になることが確認できました。
次はいわゆる偏差値です std=は標準偏差の指定です
proc standard data=q1 out=a2 m=50 std=10;
var x y z;
run;
確認
つづいてm=0 std=1 でz得点 標準得点ってやつですね
proc standard data=q1 out=a2 m=0 std=1;
var x y z;
run;
さて一方stdizeプロシジャとはなんなのかという話で、
まずは以下のように何も指定せずにまわしてみます
proc stdize data= q1 out = a3;
var x y z;
run;
さっきstandardプロシジャでzスコアだしたのと同じですね。
つまり何も指定しないとデフォルトで標準得点に変換するということがわかりました
実は、何も指定しなかった場合、内部的には以下のコードを実行しているのと同じになります
proc stdize data= q1 out = a3
method=std
add =0
mult=1
;
var x y z;
run;
method=std というのが標準偏差を使った標準化(変な言い方ですが)であること
add はこの場合、指定する平均
multiは指定する標準偏差になります
ですので偏差値をだすのであれば以下のようにかけます。
methodは省略可です。
proc stdize data= q1 out = a4
method=std
add=50
mult=10
;
var x y z;
run;
さて、ここまでだとstandardプロシジャと何が違うんだという話ですが
stdizeの味噌はmethod=の箇所にいろいろ指定できて、広い意味でのいろんな基準化ができるというところです
例えば冒頭の例のように0から100にデータを変換する場合
変換値 =(元の値 - 最小値) / (最大値 - 最小値) * (設定上限値 - 設定下限値 ) + 設定下限値にしたがって
data q3;
set q1;
x = ( x - 1 ) / ( 8 - 1 ) * (100 - 0) + 0;
y = ( y - 0 ) / ( 100 - 0 ) * (100 - 0) + 0;
z = ( z -(-5) ) / ( 8 - (-5) ) * (100 - 0) + 0;
run;
と書くところを、stdizeであれば以下のようにできます
mehodがrangeになっているところがポイントです
proc stdize data= q1
out = a5
method=range
add = 0
mult = 100
;
var x y z;
run;
結果は同じで
最低値20 最大値100であれば
data q4;
set q1;
x = ( x - 1 ) / ( 8 - 1 ) * (100 - 20) + 20;
y = ( y - 0 ) / ( 100 - 0 ) * (100 - 20) + 20;
z = ( z -(-5) ) / ( 8 - (-5) ) * (100 - 20) + 20;
run;
を以下のように指定できます。
proc stdize data= q1
out = a6
method=range
add = 20
mult = 80
;
var x y z;
run;
stdizeプロシジャの挙動は、わかると明快で
一貫した以下のルールに従って変換を行うんですね。
変換値 = add指定値 + mult指定値 ×[(元の値 - location(method毎に決まってる)/scale(method毎に決まってる]
method=stdの場合locationは平均値 scaleは標準偏差になります
method=rangeの場合locationは最小値 scaleはレンジになります
メソッドごとのlocationとscaleの設定は以下にまとまっております
http://support.sas.com/documentation/cdl/en/statug/63033/HTML/default/viewer.htm#statug_stdize_sect012.htm
methodに関わらず addのデフォルトは0 multのデフォルトは1になります。
なので、例えば中央値を使って標準化をする場合
proc stdize data= q1
out = a7
method=median
add = 50
;
var x y z;
run;
最後に平均と標準偏差を使った標準化に対して
ノンパラ版として平均の代わりに中央値、標準偏差の代わりに正規化四分位範囲を使った方法が工業分野とかでよくあるらしいです。
すみません、まったくアホなんで詳しくないですが
四分位範囲はmethod=IQR で指定できるので、それに正規化のnormオプションを付けて
proc stdize data= q1
out = a8
method=iqr
norm
;
var x y z;
run;
な感じですかね
ユニークなIDとか作れって言われたらの話
多分需要ないし、方法に工夫もないけど、与えらえた文字列から指定の長さで
ユニークなIDを作成するマクロです。
すでに発行済のIDが入ったデータセットを指定することで
過去に発行したものとも重複しない作成が可能です。
使用可能文字の数と、作成IDの長さの設定によって、生成できるIDの限界数が
決まります。
例えば100万の生成限界なのに90万のIDを発行したりすると実行効率遅いです。
生成限界はできるだけ余裕を持たせてください。
アルゴリズムは単純ですが
生成限界がぶっとんだ値であれば、何十万発行しようが結構速いと思います。
すでに存在するデータセットに付与して作りたい場合は
マクロをちょっと書き換えてsetをいれて、do untilのループをとっちゃえばいいです。
%macro unique_make(outds=,obs=,idlength=,seed=,moji=,ban=);
/*--------------------------------------------------------
outds=作成されるデータセット
obs =作成するオブザベーション数
idlength=作成される文字列の長さ
seed =作成のための乱数シード
moji =使用する文字列(半角英数字)
ban =データセット指定(ここにあるidは作成されない)
----------------------------------------------------------*/
data &outds(keep = id);
length id $&idlength..;
if _N_ = 1 then do;
declare hash h1
%if %length(&ban) =0 %then %do;();%end;
%else %do;(dataset:"&ban");%end;
h1.definekey('id');
h1.definedone();
end;
x="&moji";
n=length(x);
kumi=n**&idlength;
put 'NOTE:与えられた文字の数は ' n;
put 'NOTE:生成限界は ' kumi;
call streaminit(&seed);
do until(obs=&obs);
do until(okfl=1);
do i = 1 to &idlength;
r=int(rand('uniform') * n +1);
id = cats(id , char(x,r));
if i = 5 then do;
if h1.check() ne 0 then do;
okfl=1;
h1.add();
output;
obs+1;
end;
else id='';
end;
end;
end;
end;
run;
%mend;
以下実行例です。
/*英数字で5桁のIDを10000発行*/
%unique_make(outds=A1
,obs=10000
,idlength=5
,seed=2345
,moji=abcdefghijklmnopqrstuvwxyz0123456789
);
/*先に発行したA1とかぶらないようにさらに10000発行*/
%unique_make(outds=A2
,obs=10000
,idlength=5
,seed=6789/*シード変えた方がよい*/
,moji=abcdefghijklmnopqrstuvwxyz0123456789
,ban=A1
);
ユニークなIDを作成するマクロです。
すでに発行済のIDが入ったデータセットを指定することで
過去に発行したものとも重複しない作成が可能です。
使用可能文字の数と、作成IDの長さの設定によって、生成できるIDの限界数が
決まります。
例えば100万の生成限界なのに90万のIDを発行したりすると実行効率遅いです。
生成限界はできるだけ余裕を持たせてください。
アルゴリズムは単純ですが
生成限界がぶっとんだ値であれば、何十万発行しようが結構速いと思います。
すでに存在するデータセットに付与して作りたい場合は
マクロをちょっと書き換えてsetをいれて、do untilのループをとっちゃえばいいです。
%macro unique_make(outds=,obs=,idlength=,seed=,moji=,ban=);
/*--------------------------------------------------------
outds=作成されるデータセット
obs =作成するオブザベーション数
idlength=作成される文字列の長さ
seed =作成のための乱数シード
moji =使用する文字列(半角英数字)
ban =データセット指定(ここにあるidは作成されない)
----------------------------------------------------------*/
data &outds(keep = id);
length id $&idlength..;
if _N_ = 1 then do;
declare hash h1
%if %length(&ban) =0 %then %do;();%end;
%else %do;(dataset:"&ban");%end;
h1.definekey('id');
h1.definedone();
end;
x="&moji";
n=length(x);
kumi=n**&idlength;
put 'NOTE:与えられた文字の数は ' n;
put 'NOTE:生成限界は ' kumi;
call streaminit(&seed);
do until(obs=&obs);
do until(okfl=1);
do i = 1 to &idlength;
r=int(rand('uniform') * n +1);
id = cats(id , char(x,r));
if i = 5 then do;
if h1.check() ne 0 then do;
okfl=1;
h1.add();
output;
obs+1;
end;
else id='';
end;
end;
end;
end;
run;
%mend;
以下実行例です。
/*英数字で5桁のIDを10000発行*/
%unique_make(outds=A1
,obs=10000
,idlength=5
,seed=2345
,moji=abcdefghijklmnopqrstuvwxyz0123456789
);
/*先に発行したA1とかぶらないようにさらに10000発行*/
%unique_make(outds=A2
,obs=10000
,idlength=5
,seed=6789/*シード変えた方がよい*/
,moji=abcdefghijklmnopqrstuvwxyz0123456789
,ban=A1
);
引数指定の際のofとカンマ区切りの細かい話
最近ちょっと更新が途絶えてましたが元気です。
さて、昔、人にプログラムをあげた時に
s = sum(of a -numeric- e) ;
みたいな書き方を入れていたんですが、なんですかこれ??って
質問が来たことがあります。
特定の関数で、複数の引数を指定するときの方法として
カンマで区切って列記する方法と、ofで指定する方法があります
(併用もできます)。
が、うっかりしていると慣れた人でもハマりやすい罠があるので
おさらいしてみようと思います。
テストデータ
data Q1;
x1=1;x2=2;x3=3;x4=4;x5=5;
run;
まずは基本。
x1からx5まで全部の合計をだすには例えば以下のように書きます。
data A1;
set Q1;
y1 = sum(x1,x2,x3,x4,x5);
y2 = sum(of x1-x5);
put (y1-y2) (=/);
run;
ログに
y1=15
y2=15
とでます。あってます。
y1は基本で、対象の変数を全部カンマ区切りで指定します。
y2はofを使用しています。
これから詳しく見ていきますが、ofを使うと
①of x1-x10 (変数の末尾の連番指定)
②of id--y (データセットの格納順 idからyまでの間にある変数全部)
③of _all_ や of _numeric_; や of _character_;(全変数や全数値変数や全文字変数)
④of ax{*} (配列を指定)
⑤of x: (コロンモディファイア指定→xから始まる変数名のもの全て)
⑥of x1 x3 x5 (変数列記 変数だけでなく①から⑤までのものを全て空白区切りで列記できる)
といった指定が可能になります(他にもあったら教えてください)。
で、とても便利なのですが、よくやってしまうのが
data A3;
set Q1;
y3 = sum(x1-x5);
put y3=;
run;
結果は
y3=-4
なんで値がマイナスになっているかというと
of をつけないと x1からx5を引いた単一の値がsum関数の対象になってしまってるんですね。
続いて、複数の変数リストを指定する場合ですが
data A4;
set Q1;
y4 = sum(of x1-x2 x4-x5);
y5 = sum(x1-x2 , x4-x5);
y6 = sum(of x1-x2 , of x4-x5);
y7 = sum(of x1-x2 , x4 , x5);
put (y4-y7) (=/);
run;
結果は
y4=12
y5=-2
y6=12
y7=12
y4のようにof の後、空白でリストを区切ればOKです。
先ほどと同様にy5のようにしてしまうと x1引くx2とx4引くx5の値の合計値がでてしまいます。
意外と盲点なのが、y6のようにカンマで区切っても、その中ごとにofをつけてやれば
コードとして正しいということなんですね。
同様にy7のようにof指定とカンマ区切りを併用することもできます。
y4のようにカンマつけなきゃ一つのofで済む話なのに、なんで敢えてこういうことを書くかというと
例えば、プログラム仕様書の条件式や指定変数からコードを自動生成したりする場合、
仕様書の記載法と生成ロジックを考えなきゃいけないわけですが、そういう時に
こういう書き方でも式が正しく成立している(していない)といったケースを多く知ってると意外と役立つと思います。
で、次は、対象の変数に個々にマイナスをかけて合計したい場合です。
質問方向が反転の項目とかでそういうことありますよね。
以下を実行すると
data A5;
set Q1;
y8 = sum(-x1,-x2,-x3);
y9 = sum(of -x1 -x2 -x3);
put (y8-y9) (=/);
run;
結果は
y8=-6
y9=.
となってy8は正しいですがy9は正しく計算されません。
ofの場合、カンマ区切りと違ってマイナスつけはできないのです。
マイナスつけができないというより、もっと広く言えば
以下のように
data A6;
set Q1;
y10 = sum(x1+x2,x1*x2);
y11 = sum(of x1+x2 x1*x2);
put (y10-y11) (=/);
run;
結果は(y11のせいでエラーになりますが個別にだすと)
y10=5
y11=.
つまり、ofは変数の一括指定のためのキーワードで計算式を引数には
とれないうことです
さて次は、of使うといろいろできるよという例ですね
data A7;
set Q1;
array x{*} x: ;
y11 = sum(of x{*});
y12 = sum(of x{*} , x2 , of x3-x5 , of x:);
put (y11-y12) (=/);
run;
結果
y11=15
y12=44
y11についてはカンマで区切らなければofは一個で、後は空白区切りでOKですからね。
最後は冒頭で述べた例に戻ります。
以下のテストデータがあり
data Q2;
a=1;b=2;c=3;d='AAA';e=4;
run;
--を使うことによって変数の格納順を利用できます。
data A8;
set Q2;
y13= sum(of a--c);
put y13 = ;
run;
結果
y13=6
で、以下の例のy13のように_numeric_としてデータセット内の
全数値型変数を対象にできるのですが、
さらにそれを--と組み合わせてy14のようにかくと
data A9;
set Q2;
y14 = sum(of _numeric_);
y15 = sum(of a -numeric- e);
put (y14-y15) (=/);
run;
y14=10
y15=10
aからeの間に格納されている変数のうち、数値型のものに
限るという指定ができます。
ながながと細かい話でした
さて、昔、人にプログラムをあげた時に
s = sum(of a -numeric- e) ;
みたいな書き方を入れていたんですが、なんですかこれ??って
質問が来たことがあります。
特定の関数で、複数の引数を指定するときの方法として
カンマで区切って列記する方法と、ofで指定する方法があります
(併用もできます)。
が、うっかりしていると慣れた人でもハマりやすい罠があるので
おさらいしてみようと思います。
テストデータ
data Q1;
x1=1;x2=2;x3=3;x4=4;x5=5;
run;
まずは基本。
x1からx5まで全部の合計をだすには例えば以下のように書きます。
data A1;
set Q1;
y1 = sum(x1,x2,x3,x4,x5);
y2 = sum(of x1-x5);
put (y1-y2) (=/);
run;
ログに
y1=15
y2=15
とでます。あってます。
y1は基本で、対象の変数を全部カンマ区切りで指定します。
y2はofを使用しています。
これから詳しく見ていきますが、ofを使うと
①of x1-x10 (変数の末尾の連番指定)
②of id--y (データセットの格納順 idからyまでの間にある変数全部)
③of _all_ や of _numeric_; や of _character_;(全変数や全数値変数や全文字変数)
④of ax{*} (配列を指定)
⑤of x: (コロンモディファイア指定→xから始まる変数名のもの全て)
⑥of x1 x3 x5 (変数列記 変数だけでなく①から⑤までのものを全て空白区切りで列記できる)
といった指定が可能になります(他にもあったら教えてください)。
で、とても便利なのですが、よくやってしまうのが
data A3;
set Q1;
y3 = sum(x1-x5);
put y3=;
run;
結果は
y3=-4
なんで値がマイナスになっているかというと
of をつけないと x1からx5を引いた単一の値がsum関数の対象になってしまってるんですね。
続いて、複数の変数リストを指定する場合ですが
data A4;
set Q1;
y4 = sum(of x1-x2 x4-x5);
y5 = sum(x1-x2 , x4-x5);
y6 = sum(of x1-x2 , of x4-x5);
y7 = sum(of x1-x2 , x4 , x5);
put (y4-y7) (=/);
run;
結果は
y4=12
y5=-2
y6=12
y7=12
y4のようにof の後、空白でリストを区切ればOKです。
先ほどと同様にy5のようにしてしまうと x1引くx2とx4引くx5の値の合計値がでてしまいます。
意外と盲点なのが、y6のようにカンマで区切っても、その中ごとにofをつけてやれば
コードとして正しいということなんですね。
同様にy7のようにof指定とカンマ区切りを併用することもできます。
y4のようにカンマつけなきゃ一つのofで済む話なのに、なんで敢えてこういうことを書くかというと
例えば、プログラム仕様書の条件式や指定変数からコードを自動生成したりする場合、
仕様書の記載法と生成ロジックを考えなきゃいけないわけですが、そういう時に
こういう書き方でも式が正しく成立している(していない)といったケースを多く知ってると意外と役立つと思います。
で、次は、対象の変数に個々にマイナスをかけて合計したい場合です。
質問方向が反転の項目とかでそういうことありますよね。
以下を実行すると
data A5;
set Q1;
y8 = sum(-x1,-x2,-x3);
y9 = sum(of -x1 -x2 -x3);
put (y8-y9) (=/);
run;
結果は
y8=-6
y9=.
となってy8は正しいですがy9は正しく計算されません。
ofの場合、カンマ区切りと違ってマイナスつけはできないのです。
マイナスつけができないというより、もっと広く言えば
以下のように
data A6;
set Q1;
y10 = sum(x1+x2,x1*x2);
y11 = sum(of x1+x2 x1*x2);
put (y10-y11) (=/);
run;
結果は(y11のせいでエラーになりますが個別にだすと)
y10=5
y11=.
つまり、ofは変数の一括指定のためのキーワードで計算式を引数には
とれないうことです
さて次は、of使うといろいろできるよという例ですね
data A7;
set Q1;
array x{*} x: ;
y11 = sum(of x{*});
y12 = sum(of x{*} , x2 , of x3-x5 , of x:);
put (y11-y12) (=/);
run;
結果
y11=15
y12=44
y11についてはカンマで区切らなければofは一個で、後は空白区切りでOKですからね。
最後は冒頭で述べた例に戻ります。
以下のテストデータがあり
data Q2;
a=1;b=2;c=3;d='AAA';e=4;
run;
--を使うことによって変数の格納順を利用できます。
data A8;
set Q2;
y13= sum(of a--c);
put y13 = ;
run;
結果
y13=6
で、以下の例のy13のように_numeric_としてデータセット内の
全数値型変数を対象にできるのですが、
さらにそれを--と組み合わせてy14のようにかくと
data A9;
set Q2;
y14 = sum(of _numeric_);
y15 = sum(of a -numeric- e);
put (y14-y15) (=/);
run;
y14=10
y15=10
aからeの間に格納されている変数のうち、数値型のものに
限るという指定ができます。
ながながと細かい話でした
登録:
投稿 (Atom)


















