ラベル id= の投稿を表示しています。 すべての投稿を表示
ラベル id= の投稿を表示しています。 すべての投稿を表示

水準がデータで揃わずスカスカの集計表に立ち向かう_meansやsummaryのclassdata=とtransposeのid 複数変数を利用して

以前、meansまたはsummaryプロシジャのclassdata=オプションは集計表を作成するうえで役立つと言い詳細はまた紹介すると書きました。
(ちなみにmeansとsummaryの違いは、デフォルトでアウトプット出力するかどうかです。つまりmeansにnoprintをつければsummaryと同じで、逆にsummaryにprintをつければmeansと同じです。)

またtransposeプロシジャのIDステートメント複数づけのおかげで、集計表が作りやすくなったとも言い、実例をいつか紹介すると書きました。

遅くなりましたが、ざっくりとした流れを紹介したいと思います。

今、以下のようなデータセットがあるとします

data Q_1;
GROUP='A';SUBGROUP='X';LEVEL1=2;LEVEL2=1;LEVEL3=5;output;
GROUP='A';SUBGROUP='Z';LEVEL1=1;LEVEL2=0;LEVEL3=4;output;
GROUP='B';SUBGROUP='Z';LEVEL1=1;LEVEL2=0;LEVEL3=3;output;
run;






で、GROUPはA群とB群がいて
それぞれのグループにSUBGROUP X、Y、 Zがあるとします。
そして、3つの変数があり
LEVEL1は1-2の値をとります。
LEVEL2は0-1の値をとります。
LEVEL3は3-5の値をとります。

ところが、今まだデータが集まっていない、またはデータの収集が打ち切られた等の
理由で、たった3オブザベーションしかありません。

このわずかのデータで
















のようなEXCELの集計表テンプレートに出力する必要があるとします。
つまり、大半のセルは0になります。

どんな言語でも、あるデータをだすのは簡単ですが、ないデータをだすプログラムは難しいものです。とりあえず先にコード全部のせます。

data CLDS;
 do GROUP='A','B';
  do SUBGROUP='X','Y','Z';
   do DUMMY=0 to 20;
    output;
   end;
  end;
 end;
run;

/*================================================
マクロ名:syogi
引数-①dsname 対象データセット
   ②varname 対象変数
   ③minval 対象変数のとりうる最小のカテゴリ数値
   ④maxval 対象変数のとりうる最大のカテゴリ数値
=================================================*/
%macro syougi(dsname,varname,minval,maxval);
 proc means data=&dsname.
     classdata=CLDS(rename=(DUMMY=&varname.) where=(&minval.<=&varname.<=&maxval.))
     noprint nway exclusive;
     class GROUP SUBGROUP &varname.;
     var &varname.;
     output out=&varname._(drop=_TYPE_ _FREQ_) N=COUNT;
 run;

 proc sort;
  by &varname.;
 run;

 proc transpose data=&varname._ out=_&varname.(drop=_NAME_ rename=(&varname.=VAL))  delimiter=_;
  var COUNT;
  id GROUP SUBGROUP;
  by &varname.;
 run;
 %mend;

/*実行*/
 %syougi(Q_1,LEVEL1,1,3)
 %syougi(Q_1,LEVEL2,0,1)
 %syougi(Q_1,LEVEL3,3,5)

 data A_1;
  set _:;
 run;

/*EXCEL出力*/
 libname OUTEX "D:\集計.xlsx" header=no scan_text=no;

data OUTEX."Sheet1$D11:I18"n ;
 set A_1;
 modify OUTEX."Sheet1$D11:I18"n ;
  F1=compress(put(A_X,best.));
  F2=compress(put(A_Y,best.));
  F3=compress(put(A_Z,best.));
  F4=compress(put(B_X,best.));
  F5=compress(put(B_Y,best.));
  F6=compress(put(B_Z,best.));
run;

libname OUTEX clear;











まず、CLDSというのを作っています。
これはクラスデータセットといって、meansのクラスで指定する変数の、フルパターンをクラスデータセットに作成しておくことで、カウントであれば実際のデータの水準が足りなくても0で補完して計算してくれます。

ただ、1つの変数に対しての集計ならいいのですが、複数で、かつ採りうる値が変数ごとに違う場合、変数分それに対応するクラスデータセットをつくってたら煩雑すぎます。

なので、通常、群やサブグループなど固定のクラス変数は固定して、それ以外のカテゴリ値の部分は一端ダミーの変数名にして、大きめにデータセットを作っておきます。

以下がCLDSの中身(一部です)です。






















(一部です)


それで、マクロの中で、実際に集計する対象の変数を指定して、ダミー変数をその変数名にrenameして、かつそのカテゴリ値の採りうる値でwhereでクラスデータセットを絞っています。
以下が、マクロにLEVEL1を指定した時の、meansからoutされたデータセットです。
欠損水準を0で補って、フルセットで集計されています。





















しかしこのままだと縦持ちなのでこれをグループ、サブグループで転置します。

以下は上記のデータセットがtransposeされた後のデータセットです







delimiter=でアンダーバーを追加しています。
たとえば、A_XはグループAのサブグループXの集計結果というわけです。

それでLEVEL1-3までを集計してその結果をコロンモディファイア指定でSetしてつなぎます。
最後にLIBNAME EXCELでだしていますが、DDEでもなんでもいいです。

ざっくりとした例なので、実際に使用される場合は、適宜書き換えてください(とりあえずマクロ名変えましょう)。そのままべたっと貼っても動きません多分。

で注目点はクラスデータと、実際のデータのフォーマットやラベルなどのメタデータが同期していることが必要なのでLENGTHを合わせたり、

proc datasets nolist;
 modify Q_1;
  attrib _all_ informat= format= label='';
quit;

などで、余計なメタデータを消しておいた方がいいです。
classdata=使うといつもエラーになって、あきらめますという話を聞くのですが
大半はここが原因です。

あと最後に、若干ネタ切れ感がでてきたので、取り上げてほしい部分や、詰めSASの問題など
アイデアがある方はご連絡ください



正直あまりいいオプションとは思えませんが、むしろ反面教師としてTRANSPOSEプロシジャのletの説明

transpsoeプロシジャのletオプションは、ちょっと危ないオプションというか、
絶対にWarningのでるオプションで、はっきり言って僕は絶対使わない方がいいと思うオプションです。
よくわかってない人が全部のtransposeプロシジャの使用箇所にletオプションをつけたせいで、えらいことになって、代わりに書きなおすはめになったことがあるので、個人的に恨みがあります。

どういうことかといいますと、
通常transposeでidステートメントを使用する場合、指定された変数の値に重複があると、転置ができないためエラーになります。これは当然の処置で、本来idに指定する変数は一意に値を定める手がかりにするものなので重複は論外、エラーになるのが正しいのです。

ところがletオプションは、これを強引にWARNINGに格下げして、データセットを最後まで作成します。重複をどのようにして解決するかというと、重複する変数の最後の値のみ採用するという方法で回避します。

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

data Q1;
X='A';Y=1;Z=1;output;
X='A';Y=2;Z=2;output;
X='A';Y=3;Z=4;output;
X='A';Y=3;Z=3;output;
X='A';Y=3;Z=3;output;
X='B';Y=1;Z=1;output;
X='B';Y=2;Z=3;output;
X='B';Y=2;Z=2;output;
X='B';Y=3;Z=4;output;
run;













proc transpose data=Q1 out=A1 prefix=Y_;
 var Z;
 id Y;
 by X;
run;


を実行すると









となってデータセットは作成されません。
Yの値が重複してるからです。
例えばX='A'のグループにおいてY_3変数が3つ作られてしまい、SASは同じ変数名をデータセット内
に保持できないのでエラーなのです。

ところが

proc transpose data=Q1 out=A1 prefix=Y_ let;
 var Z;
 id Y;
 by X;
run;












となってERRORからWARNINGにメッセージレベルが変わり、データセットは作成されます。

中身は






です。

いや、まあ便利といってしまえばそうなのかもしれませんが、どうせ重複した値を最後を除いて
消すのなら
transposeに流す前に、nodupkeyとかfirst.last.とかで加工して、一意になる形にしてから
通常の転置をした方がWARNINGもでないし、正道だと思うのです。

どうしても一意にできないデータセットなら、そもそも転置をかけること自体が間違っているか
データ構造に不備があるのではないかと思います。

「このオプションつけるとなんでも転置できて凄い便利ですよ~、でもなんかログが緑になるんですよね~。WARNINGでないようにするオプション知りませんか?」とか言ってたら、ちょっと、その人はヤバイというか、身内なら、全力で叩きなおした方がいいかもしれません。








TRANSPOSE→CATX関数のコンボで縦持ちのデータを1行1変数に指定文字で連結してまとめてみる

CATX関数の使い勝手がよいという話題です。

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

data Q1;
length ID $10.;
ID='AAA';NO=2;ITEM='ろ';output;
ID='AAA';NO=1;ITEM='い';output;
ID='AAA';NO=3;ITEM='';  output;
ID='AAA';NO=4;ITEM='は';output;
ID='BBB';NO=1;ITEM='A'; output;
ID='BBB';NO=5;ITEM='B'; output;
run;



これをIDごとに、出現するITEMの内容を、NO順に「、」で連結して
ALLITEMという変数にいれるという問題があったとします。
求めたい結果は以下です。


なんか面倒そうだと感じますが、楽勝です。

まず、IDとNOでソートした後、IDをbyで固定してtransposeします。
このときIDステートメント(すみません、変数とまぎらわしいですね)でNOを指定します。


proc sort data=Q1;
 by ID;
run;

proc transpose data=Q1 out=Q2(drop=_NAME_) prefix=ITEM_;
 var ITEM;
 by ID;
 id NO;
run;

すると



のデータセットが得られます。
後はこれを順番に「、」で繋ぎたいのですが、nullの場合は飛ばしたいわけです。
空白が間にはいるのも嫌なので、う~んどうしよう、IFいっぱい書かなきゃいけないかと思いきや

data A;
 set Q2;
  ALLITEM=catx('、',of ITEM_:);
 keep ID ALLITEM;

run;

これで、終わりです。
catx関数は指定した区切り文字で、null以外の値を、前後の空白を除去した上で連結してくれるという、凄く気のきいた関数です。
もちろん
  ALLITEM=catx('、',ITEM_1,ITEM_2,ITEM_3,ITEM_4,ITEM_5);
とかいていいのですが、そうすると元データが増えてNO「6」ができたら
コードを修正する必要がでてしまいます。

TRANSPOSEプロシジャのprefix=は接頭語を指定できるので(suffix=は接尾語)
ここをうまく使えば、NOがどんな数字だろうと、転置後の変数は「ITEM_」から始まります。
そこで of とコロンモディファイアで、配列を使うまでもなくターゲットの変数を全て捕まえることが
できます。

CAT やCATSなど、微妙に違うことをしてくれる関数もあるので調べてみるといいと思います





詰めSAS2回目_複数の変数の値で転置する

SAS9.2からtransposeプロシジャのidステートメントに2個以上の変数を指定可能になりました。
あまり取り上げられるところを見たことがないのですが、これはかなり革命的なことだと思っています。いずれ取り上げたいですが、meansプロシジャのclassdataステートメントと合わせれば、医薬系で臨床試験の有害事象を群別Grade集計するのなどが、かなり楽でスマートにできるようになりました。

ほとんど答えを先に言ってしまいましたが、詰めSAS_2問目です。


data Q1;
input X Y Z;
cards;
1 4 3
2 9 8
7 6 5
;
run;







このデータセットについてX、Yの値によってZの値を転置して以下のデータセットを作ります。

目的局面図は以下です。






【解法1】
proc transpose data=Q2 out=A1(drop=_NAME_);
 var Z;
 id X Y;
run;

これだけで詰んでます。idステートメントの複数変数指定はもっと注目されるべきSASのバージョンアップポイントだと思います。

【解法2】
data A2;
 set Q1(where=(X=1 and Y=4) rename=(Z=_14));
 set Q1(where=(X=2 and Y=9) rename=(Z=_29));
 set Q1(where=(X=7 and Y=6) rename=(Z=_76));
 drop X Y;
run;

Setの連続掛けは個人的に、とてもとても好きな方法です。指定したデータセットの中で最小のobs数に統一されることと、同名の変数が上書きされる性質は扱いにくい一方で、mergeステートメントとは違った魅力があると思います。