マクロ絡みの話です。
&とか%とか;とか、そういうSASにとって意味のある記号が、含まれる際に
そいつらをただの文字として扱いたい場合にマクロ引用符関数といったものを使います。
ほっとくと勝手に展開しようとしたり、変なとこで区切って解釈されてエラーになりますからね。
いわゆるクォート処理です。
(SASmemo - マクロ関数、自動マクロ変数一覧)
http://www50.atwiki.jp/sasmemox/pages/56.html
(Welcome to データ分析・マイニングの世界 by SAS)
http://wikiwiki.jp/cattail/?Base%20SAS%A5%DE%A5%AF%A5%ED
ただ、マクロのクォート周りはあんまり得意じゃなくて
正直きっちりと理解できてません。
なので、あんまり僕の説明はあてにしないでください。
さて、以下のようなデータがあるとします。
どうでもいい話ですが、世の中には結構「&」の入った会社名って多いんですよね
SASプログラマーにはつらい話です。
data Q1;
X='A&A';Y=1;Z=3;output;
X='B&B';Y=2;Z=2;output;
X='C&C';Y=3;Z=1;output;
run;
そこで、マクロ変数に値を入れて、それによって抽出を行うコードを考えてみましょう
%let MA = B&B ;
data E1;
set Q1;
where X = "&MA" ;
run;
まあ、できたデータセットE1の中をみると一応「B&B」で抽出できてるんですが
ログに
「WARNING: 記号参照 B を展開していません。」というメッセージがでています。
これは%let MA = B&B ;の時点で「&B」の部分を「B」というマクロ変数と解釈して
それを展開しようとしたけどBというマクロ変数がなかったので展開できずに、B&Bという文字列のまま
マクロ変数MAに入れましたという意味です。
今回はBというマクロ変数がなかったので、WARNINGがうざいなってだけで済みますが、もし
あった場合は展開されて、本来予期しない結果になってしまいます。
それを防ぐにはまず。
%let MA = %nrstr(B&B);
data A1;
set Q1;
where X = "&MA" ;
run;
こういうアプローチがとれます。%nrstrで「&」をただの文字扱いにしています。
では、マクロ変数の生成が%letではなく、call symput(x)の場合はどうでしょうか?
例えば
data _null_;
call symputx('MA','B&B');
run;
data E2;
set Q1;
where X = "&MA" ;
run;
とすると先程と同じようにWARNINGがでます。
これもまあ、
data _null_;
call symputx('MA','%nrstr(B&B)');
run;
とすれば一応解決です。
ただ、これまで見てきたのはマクロ変数に値を格納する際にクォート関数で
包んで一緒に入れてやることで、展開時に文字とみなしてやろうぜという発想です。
次は、そうではなくて、マクロ変数定義は以下のようにクォート関数なしで
そのまま格納し
data _null_;
call symputx('MA','B&B');
run;
展開時になんとかして、文字として解釈させようぜという発想のコードです。
その場合、以下のように書けます。
data A2;
set Q1;
where X = "%superq(MA)" ;
run;
superq関数はとても説明しにくいので、人の言葉を借ります
「引数に指定されたマクロ変数の値に対してマクロプロセッサが展開を行わないようにした上で、その値に含まれる特殊文字をクォート」(SASmemo)
「引数に与える文字列をマクロ変数名とみなし、1回だけ展開した値を返す」(Welcome to データ分析・マイニングの世界 by SAS)
「The %SUPERQ function locates the macro variable named in its argument and quotes the value of that macro variable without permitting any resolution to occur.」(SAS(R) 9.2 Macro Language: Reference)
或いは以前、紹介したsymget関数も、この場合は使えます。こいつはマクロ変数を展開した文字値を返すSAS関数です。
SYMGET関数でマクロ変数の値を取得する
http://sas-tumesas.blogspot.jp/2013/12/symget.html
data A3;
set Q1;
where X = symget('MA') ;
run;
でOKです。
ただし、symget関数はあくまでただのSAS関数として値を返すものですので
例えば以下のようにマクロ変数に抽出式を入れて
data _null_;
call symputx('wh','Y=1&Z=3');
run;
展開する場合
data A4;
set Q1;
where %superq(wh);
run;
だと正しい結果になりますが
data E3;
set Q1;
where symget('wh') ;
run;
だと、3レコードとも抽出されます。
これは結構不思議かもしれませんが
symget('wh')の結果返される値がnullかどうかという真偽値によって
whereが働いてしまうからです。
つまりsymget('wh')が返すのはこの場合「Y=1&Z=3」という文字列なのですが
この文字列はnullじゃないですよね? よって全て真となって
意図した抽出にはならないわけです。
マクロのクォート周りは、ドツボにハマりやすいので、周りにマクロ組むのが上手い人が
いたら直ぐに聞いた方がいいですね!
ハッシュオブジェクトで、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;
おわりです!
久しぶりの更新です。
さて、実は最近、ハッシュオブジェクトに関する質問が結構きます。
(コメントや掲示板も使っていただけると嬉しいですが)
日本でも遅ればせながら少しずつ普及してきているんでしょうか?
誰にも頼まれてないのにハッシュオブジェクトを日本に普及させようと目論む狂信者の僕としては結構嬉しいです。(海外の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;
おわりです!
ビューの生成コードを出力するdescribeについて、SQLビューとデータステップビューだと方法が違うから気を付けてって話
さて、以前、SASのビューについて説明したことがありました。
「SASのテーブルビュー(view)について」
http://sas-tumesas.blogspot.jp/2013/12/sasview.html
おさらいとして、
data Q1;
do X= 1 to 10;
output;
end;
run;
というデータセットが実体としてあった場合、
Xが偶数のもののみに絞るビューは、以下の2種類の方法で書くことができます。
proc sql noprint;
create view V1 as
select *
from Q1
where mod(X,2) = 0;
quit;
data V2/view=V2;
set Q1;
where mod(X,2)=0;
run;
SQLプロシジャのcreate viewか、dataステートメントのviewオプションいずれで
作っても、結果作成される2つのビューの挙動は同じです。
しかし、例えば、contentsプロシジャでビューを見てみると
proc contents data = V1;
run;
proc contents data = V2;
run;
V1の方は
エンジン (ENGINE) SQLVIEW
とでて、
V2の方は
エンジン (ENGINE) SASDSV
となって、ビューはビューでも一応種類が違うことがわかります。
要するに、ビューが参照されるときに内部的に実行されるコードが、データステップなのか
SQLなのかってことですね。
この違いについて、ビューのユーザーは基本的に特に何も意識せずともいいのですが、少しだけ
注意があります。
それは、そのビューがどういった定義で生成されたものなのかを確認するとき、
SQLビューに対しては
proc sql;
describe view V1;
quit;
としなければならず、
SASデータセットビューに対しては
data view=V2;
describe;
run;
としなければならないというルールです。
例えばSQLビューに対して
data view=V1;
describe;
run;
とすると
「ERROR: ビュー WORK.V1 は壊れているか、
または DATA ステップビューではありません。」
と弾かれ、逆にSASデータセットビューに対して
proc sql;
describe view V2;
quit;
とすると
「WARNING: WORK.V2 は SQL ビューではありません」
と弾かれます。
この、スタンドはスタンドでしか攻撃できないみたいなルールについて
知っていればどうってことないのですが、知らない場合、
特にデータセットビューは知ってるけどSQLビューって何?みたいに片方しか把握してない
メンバーが混在して仕事をする時に、問題になったという体験談を聞いたことがあります。
なんか、てっきり壊れてると思って、他の人が作ったビュー、全部消しちゃったとかがマジであったそうです。
(ビューうんぬんというよりかは、自分が作ってないものを無断であっさり消せちゃう度胸が凄いと思いますが…)
できればビューの作成法は統一した方がいいですし、性質上、1ステっプでの表現力が高いSQLの方が
ビューを作るうえでは優位性があると思ってますが…。
さて、最近メールで「最近あまり更新されませんが、なんかありましたか?心配です」という
heart-warmingなメッセージから「将棋見に行ったりするのにかまけてブログ更新頻度落とすんじゃねぇよ!」
というどちらかというとhurtfulなメッセージまでいただきました。
いや、すみません。
ただ、純粋にネタが枯渇してきたというのが実情なのです。まだSAS歴4~5年程度なので底がそんなに深くないんです。
まあしばらく、色々試したり勉強したりしてネタのストックが溜まるまで、低頻度で気ままに更新させていただきます。(まあ、だいたい、データステップをネタにして、月30-40記事も更新してたっていうのが、ちょっと頭おかしいですよね)
「SASのテーブルビュー(view)について」
http://sas-tumesas.blogspot.jp/2013/12/sasview.html
おさらいとして、
data Q1;
do X= 1 to 10;
output;
end;
run;
というデータセットが実体としてあった場合、
Xが偶数のもののみに絞るビューは、以下の2種類の方法で書くことができます。
proc sql noprint;
create view V1 as
select *
from Q1
where mod(X,2) = 0;
quit;
data V2/view=V2;
set Q1;
where mod(X,2)=0;
run;
SQLプロシジャのcreate viewか、dataステートメントのviewオプションいずれで
作っても、結果作成される2つのビューの挙動は同じです。
しかし、例えば、contentsプロシジャでビューを見てみると
proc contents data = V1;
run;
proc contents data = V2;
run;
V1の方は
エンジン (ENGINE) SQLVIEW
とでて、
V2の方は
エンジン (ENGINE) SASDSV
となって、ビューはビューでも一応種類が違うことがわかります。
要するに、ビューが参照されるときに内部的に実行されるコードが、データステップなのか
SQLなのかってことですね。
この違いについて、ビューのユーザーは基本的に特に何も意識せずともいいのですが、少しだけ
注意があります。
それは、そのビューがどういった定義で生成されたものなのかを確認するとき、
SQLビューに対しては
proc sql;
describe view V1;
quit;
としなければならず、
SASデータセットビューに対しては
data view=V2;
describe;
run;
としなければならないというルールです。
例えばSQLビューに対して
data view=V1;
describe;
run;
とすると
「ERROR: ビュー WORK.V1 は壊れているか、
または DATA ステップビューではありません。」
と弾かれ、逆にSASデータセットビューに対して
proc sql;
describe view V2;
quit;
とすると
「WARNING: WORK.V2 は SQL ビューではありません」
と弾かれます。
この、スタンドはスタンドでしか攻撃できないみたいなルールについて
知っていればどうってことないのですが、知らない場合、
特にデータセットビューは知ってるけどSQLビューって何?みたいに片方しか把握してない
メンバーが混在して仕事をする時に、問題になったという体験談を聞いたことがあります。
なんか、てっきり壊れてると思って、他の人が作ったビュー、全部消しちゃったとかがマジであったそうです。
(ビューうんぬんというよりかは、自分が作ってないものを無断であっさり消せちゃう度胸が凄いと思いますが…)
できればビューの作成法は統一した方がいいですし、性質上、1ステっプでの表現力が高いSQLの方が
ビューを作るうえでは優位性があると思ってますが…。
さて、最近メールで「最近あまり更新されませんが、なんかありましたか?心配です」という
heart-warmingなメッセージから「将棋見に行ったりするのにかまけてブログ更新頻度落とすんじゃねぇよ!」
というどちらかというとhurtfulなメッセージまでいただきました。
いや、すみません。
ただ、純粋にネタが枯渇してきたというのが実情なのです。まだSAS歴4~5年程度なので底がそんなに深くないんです。
まあしばらく、色々試したり勉強したりしてネタのストックが溜まるまで、低頻度で気ままに更新させていただきます。(まあ、だいたい、データステップをネタにして、月30-40記事も更新してたっていうのが、ちょっと頭おかしいですよね)
xコマンドで、フォルダ構造のみコピーする話
3/14の電王戦第一局抽選あったたので六本木に見に行ってきます!
このブログ見ていて、もし来られる方がいれば連絡ください!
はい、本題が終わったので、SASの話もちらっと。
Windows環境でコマンドプロンプト使いこなしてると、色々できていいですよね。
僕はさっぱりです。
なんかやりたい時はググってSASのxコマンドにペタッと貼って実行してます。
その中で、ちょっと変わった処理だけど意外と使うな~ってものをご紹介します。
それは、指定したフォルダ内のファイルは無視して、フォルダ構造のみをコピーするというものです。
例えば、ルーチンでやる業務で、前回と同じフォルダが欲しいけど、中に入ってるファイルは別に要らないよというケースです。
一応マクロにしていますが、適当なのでよしなにカスタマイズしてください
/*-----------------------------------------------------------
フォルダ構造のみコピー
※コピー元のフォルダ内のファイルは無視して構造のみをコピー
------------------------------------------------------------*/
%macro folder_emp_copy(basein=,copyout=);
options noxwait noxsync;
%unquote(%bquote(x 'xcopy /t /e "&basein." "©out."'));
%mend folder_emp_copy;
/*実行例:デスクトップのTEST1フォルダ内のフォルダ構造をTEST2フォルダ内にコピー*/
%folder_emp_copy(basein=C:\Users\SASYAMA\Desktop\TEST1,
copyout=C:\Users\SASYAMA\Desktop\TEST2)
ついでに、単純に1ファイルをコピーしてリネームするのも
/*--------------------------------------
単一ファイルのリネームコピー
---------------------------------------*/
%macro filecopy(basein=,copyout=);
options noxwait noxsync;
%unquote(%bquote(x 'copy "&basein" "©out"'));
%mend filecopy;
/*実行例:AA.txtをBB.txtという名前にリネームしてコピー*/
%filecopy(basein=C:\Users\SASYAMA\Desktop\TEST1\AA.txt,
copyout=C:\Users\SASYAMA\Desktop\TEST2\BB.txt)
このブログ見ていて、もし来られる方がいれば連絡ください!
はい、本題が終わったので、SASの話もちらっと。
Windows環境でコマンドプロンプト使いこなしてると、色々できていいですよね。
僕はさっぱりです。
なんかやりたい時はググってSASのxコマンドにペタッと貼って実行してます。
その中で、ちょっと変わった処理だけど意外と使うな~ってものをご紹介します。
それは、指定したフォルダ内のファイルは無視して、フォルダ構造のみをコピーするというものです。
例えば、ルーチンでやる業務で、前回と同じフォルダが欲しいけど、中に入ってるファイルは別に要らないよというケースです。
一応マクロにしていますが、適当なのでよしなにカスタマイズしてください
/*-----------------------------------------------------------
フォルダ構造のみコピー
※コピー元のフォルダ内のファイルは無視して構造のみをコピー
------------------------------------------------------------*/
%macro folder_emp_copy(basein=,copyout=);
options noxwait noxsync;
%unquote(%bquote(x 'xcopy /t /e "&basein." "©out."'));
%mend folder_emp_copy;
/*実行例:デスクトップのTEST1フォルダ内のフォルダ構造をTEST2フォルダ内にコピー*/
%folder_emp_copy(basein=C:\Users\SASYAMA\Desktop\TEST1,
copyout=C:\Users\SASYAMA\Desktop\TEST2)
ついでに、単純に1ファイルをコピーしてリネームするのも
/*--------------------------------------
単一ファイルのリネームコピー
---------------------------------------*/
%macro filecopy(basein=,copyout=);
options noxwait noxsync;
%unquote(%bquote(x 'copy "&basein" "©out"'));
%mend filecopy;
/*実行例:AA.txtをBB.txtという名前にリネームしてコピー*/
%filecopy(basein=C:\Users\SASYAMA\Desktop\TEST1\AA.txt,
copyout=C:\Users\SASYAMA\Desktop\TEST2\BB.txt)
エクセルで開いても前ゼロが消えないcsvをods csvで作る話
ods csvのマニアックな話です。
data Q1;
X=1;Y='001';Z='A';output;
X=2;Y='002';Z='B';output;
run;
のような前ゼロがあるデータを考えます。
ods csv file='/folders/myfolders/A1.csv';
proc print data=Q1 noobs;
run;
ods csv close;
data Q1;
X=1;Y='001';Z='A';output;
X=2;Y='002';Z='B';output;
run;
のような前ゼロがあるデータを考えます。
ods csv file='/folders/myfolders/A1.csv';
proc print data=Q1 noobs;
run;
ods csv close;
として
A1.csvをノートパッド等テキストエディタで開いてみます。
となっています。
同じファイルをEXCEL等のスプレッドシートソフトで開いてみると
のように前ゼロが取れて表示されます。
よく知られた話だと思います。
EXCELで開いても、前ゼロがとれないようにすることできないかな~って考えます。
何のためにか?ただの興味です。
まずはquote_by_typeでクォートしちゃいます。
ods csv file='/folders/myfolders/A2.csv' options(quote_by_type="Yes" );
proc print data=Q1 noobs;
run;
ods csv close;
テキストエディタで開くと
さて、今度はいけるかな~??
はい無理でした~。
最後は、Prepend_Equalsをつけてみます。
ods csv file='/folders/myfolders/A3.csv' options(Prepend_Equals='Yes' quote_by_type="Yes" );
proc print data=Q1 noobs;
run;
ods csv close;
すると、以下のようになんか前に「=」とかつきだしました
今度こそ!!
できた!!わ~い!!
しかし、できたけど、csvって、他のシステムと読み書きでデータの受け渡ししやすいから使うこと
多いわけで、EXCELで確認するときに前ゼロがつく代償に、こんな余計なもんついたcsv作ってもな~。
dkricondオプションで、データセットオプションで存在しない変数を対象としたdrop keep renameが実行された際のエラー制御の話
さて、以下の2ステップを実行するとどうなるでしょうか??
data Q1;
X=1;
run;
data A1;
set Q1(drop=Y);
run;
答えは2ステップ目でエラーになってA1はちゃんと作成されないでした。
ERROR: DROP, KEEP または RENAME リスト内の変数 Y は参照されませんでした。
NOTE: エラーが発生したため、このステップの処理を中止しました。
WARNING: データセット WORK.A1 は未完成です。このステップは、 0 オブザベーション、0 変数で停止しました。
WARNING: このステップを中止したため、データセット WORK.A1 を置き換えていません。
でログが華やぎます。
で、もちろん、存在しない変数を対象にしたデータセットオプションを指定しなければよい話なのですが、例えばマクロなんか組んでると、いちいち変数の存在確認して分岐処理を入れると煩雑になる場合もあります。
エラー制御系のオプションは良し悪しですが、知ってて損はないのでご紹介。
options dkricond=nowarn;
data A2;
set Q1(drop=Y);
run;
options dkricond=error;
とすると、
、ログには何も出ません。
さてdkricondってなんの略だと思います?
dはdrop kはkeep rはrenameです(多分)。
でiはin dataset optionとかinputとかinの略(多分ね)、でcondはcondition(多分ね)の略です。
つまりdkricondはインデータセットオプションにdrop keep renameが指定されて、その変数が
ない場合にどうするかで、デフォルトはERRORです。
ちなみにdkrocondオプションっていうのもあって、つまりは
data A3(drop=Y);
set Q1;
run;
data Q1;
X=1;
run;
data A1;
set Q1(drop=Y);
run;
答えは2ステップ目でエラーになってA1はちゃんと作成されないでした。
ERROR: DROP, KEEP または RENAME リスト内の変数 Y は参照されませんでした。
NOTE: エラーが発生したため、このステップの処理を中止しました。
WARNING: データセット WORK.A1 は未完成です。このステップは、 0 オブザベーション、0 変数で停止しました。
WARNING: このステップを中止したため、データセット WORK.A1 を置き換えていません。
でログが華やぎます。
で、もちろん、存在しない変数を対象にしたデータセットオプションを指定しなければよい話なのですが、例えばマクロなんか組んでると、いちいち変数の存在確認して分岐処理を入れると煩雑になる場合もあります。
エラー制御系のオプションは良し悪しですが、知ってて損はないのでご紹介。
options dkricond=nowarn;
data A2;
set Q1(drop=Y);
run;
options dkricond=error;
とすると、
、ログには何も出ません。
さてdkricondってなんの略だと思います?
dはdrop kはkeep rはrenameです(多分)。
でiはin dataset optionとかinputとかinの略(多分ね)、でcondはcondition(多分ね)の略です。
つまりdkricondはインデータセットオプションにdrop keep renameが指定されて、その変数が
ない場合にどうするかで、デフォルトはERRORです。
ちなみにdkrocondオプションっていうのもあって、つまりは
data A3(drop=Y);
set Q1;
run;
こういうようにアウトするデータセットにつけるデータセットオプションの場合ですが、こっちは
inの方と違ってデフォルトはwarningなのです。だからwarningが出てもデータセットができてれば一向に構わん!って人はいらないかも。
まあ、試してみてください。
まあ、試してみてください。
GTLの中でeval関数を使うことによって、SAS関数を使えて、動的なテンプレート定義ができたりする話
ちょっとした話です。
data Q1;
call streaminit(777);
do Z= 1 to 10;
X=rand('uniform');Y=rand('uniform');
output;
end;
run;
GTL、テンプレート定義は基本的に静的なもので、
dynamicステートメントやマクロで値を渡す以外は、template内で実行時に
変更を伴う動的な処理はできないのですが
evalを使うとSAS関数が使えるので、
proc template;
define statgraph G1;
begingraph;
layout overlay;
scatterplot x=X y=Y
/name='n1'
markerattrs=(size=11 weight=bold symbol=circlefilled)
group=eval(ifc(Z<=5,'low','high'));
discretelegend 'n1'/title='Z'
location=inside halign=right valign=bottom;
endlayout;
endgraph;
end;
run;
proc sgrender data=Q1 template=G1;
run;
のように予めグループ化用の変数を用意してなくても、データから条件を付けて
ダイレクトにグループ化したりできます。
或いは、よく使うのが、グラフに参照線入れる時に、mean関数とかで、平均値とか
要約統計量を使いたい場合とかですかね。
data Q1;
call streaminit(777);
do Z= 1 to 10;
X=rand('uniform');Y=rand('uniform');
output;
end;
run;
GTL、テンプレート定義は基本的に静的なもので、
dynamicステートメントやマクロで値を渡す以外は、template内で実行時に
変更を伴う動的な処理はできないのですが
evalを使うとSAS関数が使えるので、
proc template;
define statgraph G1;
begingraph;
layout overlay;
scatterplot x=X y=Y
/name='n1'
markerattrs=(size=11 weight=bold symbol=circlefilled)
group=eval(ifc(Z<=5,'low','high'));
discretelegend 'n1'/title='Z'
location=inside halign=right valign=bottom;
endlayout;
endgraph;
end;
run;
proc sgrender data=Q1 template=G1;
run;
のように予めグループ化用の変数を用意してなくても、データから条件を付けて
ダイレクトにグループ化したりできます。
或いは、よく使うのが、グラフに参照線入れる時に、mean関数とかで、平均値とか
要約統計量を使いたい場合とかですかね。
登録:
投稿 (Atom)












