ラベル ステートメント: OUTPUT の投稿を表示しています。 すべての投稿を表示
ラベル ステートメント: OUTPUT の投稿を表示しています。 すべての投稿を表示

2016年6月7日火曜日

「OUTPUT」と「CALL MISSING」のコンボ技


一対多のマージで、一側データ由来の変数値を各BYグループの最初のみに出力したい事ってありませんか?


*** サンプルデータ ;
data DT1;
input A$ B$ C$ @@;
cards;
001 aa cc 002 bb dd
;

A
 B  
 C  
  001    aa     cc   
  002    bb  dd   

data DT2;
input A$ D @@;
cards;
001 11 001 22 001 33 002 44 002 55
;

A
  D  
  001     11   
  001     22
  001     33
  002     44
  002     55


やりたい事

上のデータをMERGEすると、
data OUT1;
  merge DT1 DT2;
  by A;
run;


上記のように一側データ(DT1)由来の黄色部分が繰り返されますが、これを空白にしたい。
A
 B  
 C  
  D  
  001    aa     cc      11   
  001                 22   
  001        33   
  002    bb     dd      44   
  002        55  


前提: 
一対多のマージを前提とします。多対多を含むマージ(一対多対多とか)だと挙動が変わってくるので今回の記事では対象外とします。



方法1 最初に思いつく方法
data OUT1;
  merge DT1 DT2;
  by A;
  if first.A = 0 then call missing( B, C );
run;

以下記事で紹介した「FIRST.BY変数」を利用しています。

1つ注意として、ここで使用している「FIRST.BY変数」は「サブセット化IF」と一緒に使用すると正しく動かなくなりやすいです。



方法2 OUTPUT と CALL MISSING のコンボ技
data OUT1;
  merge DT1 DT2;
  by A;
  output;
  call missing( B, C );
run;

紹介しといてなんですが、方法1の方が、分かり易くていいと思います。
ですが方法2のようなやり方を知ってると、応用がきいて便利な時があります。


方法2の解説

以前の記事 「MERGEステートメントの落とし穴」 で紹介した内容を理解することで、今回の内容も理解がしやすいです。

重要なポイントは、
「一対多のマージにおいて、一側データセットの変数は、BYグループ毎に1回しかPDVに読み込まれない。
(BYグループが変わるまで、PDVに残った値が保持される。。。RETAINのようなイメージ)」


そこで、1オブザベーション毎に
① 「output;」でデータセットOUT1に出力
② 「call missing( B, C );」 でPDVの中の変数BとCを欠損値にする

PDVに残った一側データ由来の変数BとCの値が保持されるのを②によって抑制しているので、BYグループの最初にしかBとCが入らなくなっているわけです。


一側データの変数の数が多い場合は、「call missing( of _all_ );」と書いてしまえば、いっぺんに欠損値に出来るので楽です。

data OUT1;
  merge DT1 DT2;
  by A;
  output;
  call missing( of _all_ );
run;

ただし、PDV内の全変数を欠損値にしているので、
RETAIN等、値を保持するような処理は全て無効になります。


。。。つづく

2016年3月1日火曜日

1つのデータステップで、複数のデータセットを作成する方法。




以下を実行すると、、

data  DT1 DT2 DT3;
    set  SASHELP.CLASS;
run;

データセットSASHELP.CLASSをセットしたデータセットDT1, DT2, DT3をいっぺんに作ることが出来ます。



また、以下のように書くことも出来ます。

data  DT1 DT2 DT3;
     set  SASHELP.CLASS;
     if  AGE=13 then  output DT1;
     if  AGE=15 then  output DT2;
     output DT3;
run;

・ AGE=13のオブザベーションをDT1に出力
・ AGE=15のオブザベーションをDT2に出力
・ 条件なしでオブザベーションをDT3に出力


注意:データステップ中にOUTPUTステートメントとMODIFYステートメントを併用していない前提となります無限ループになったり挙動が変わることがあるので・・)



注意点


たとえば、以下の赤文字の処理、変数FLAGがDT1とDT2に作成されますが、、

data DT1 DT2;
    set SASHELP.CLASS;
    if AGE=13 then output DT1;
    if AGE=15 then output DT2;
    if AGE=13 then FLAG=1;     ***  × この書き方はダメ ******;
run;

DT1には「AGE=13」のオブザベーションがありますが、「FLAG=1」が反映されません。

暗黙のOUTPUTステートメント」 を見ていただくと原因が分かります。



というわけで、OUTPUTの前に移動してやります。

data DT1 DT2 DT3;
   set SASHELP.CLASS;
   if AGE=13 then FLAG=1;
   if AGE=13 then output DT1;
   if AGE=15 then output DT2;
run;


2016年2月18日木曜日

グループ毎に空白行を追加したい



やりたい事

* サンプルデータ ;
data DT1;
length NO SEX $10. AGE HEIGHT 8.;
input NO$ SEX$ AGE HEIGHT @@;
cards;
001 F 25 159.2  002 F 43 161.8  003 F 21 158.1  004 M 19 178.0  005 M 28 160.2
;



変数SEXの値ごとに1行だけ空白行を追加したいとします。




方法① データステップ

proc sort data=DT1;
   by SEX ;
run ;

data OUT1;
   set DT1 ;
   by SEX ;
   output ;
   if last.SEX then do ;
       call missing( NO, AGE, HEIGHT );
       output ;
   end ;
run ;


ざっくり解説すると、
  • output;」でいったん現在のオブザベーションをデータセットに出力
  • if last.SEX then do;」で、SEXの値「F」「M」ごとに最後のオブザベーション(3obsと5obs)を読み込んだ時に、
  • call missing( NO, AGE, HEIGHT )」で変数NO, AGE, HEIGHTを欠損値にして、
  • output;」でデータセットに再出力しています。


📝注意点

ここで使用している「LAST.BY変数」は「サブセット化IF」と一緒に使用すると正しく動かない事があります(解説記事:「サブセット化IFでありがちな落とし穴」)





方法② REPORTプロシジャ

proc report data=DT1 out=OUT2 nowd ;
   define _numeric_  / display ;
   define SEX  / order order=internal missing ;
   break after SEX  / ;
run ;


REPORTプロシジャってデータセットにも出力できるんです!

  • define SEX / order order=internal missing;」と「break after SEX /;」で、SEX毎に要約行を追加
    • この書き方、HTML等への出力時には要約行が入らないんですが、データセット出力時には要約行が入ります
  • このままだと要約行に数値変数AGE, HEIGHTの要約値(合計)が入ってしまうので、「define _numeric_ / display;」で全数値変数を集計せずただdisplayするだけ(表示するだけ)にしています(データセット中に数値変数が存在しないなら「define _numeric_ / display;」は不要)
  • out=OUT2」でデータセットOUT2に出力


他に何かいいアイディアがあれば教えてください!


2015年10月1日木曜日

暗黙のOUTPUTステートメント



今回の内容は、PDV(プログラムデータベクトル)を理解していないと、正確な動きを理解しづらいかも。

あと、データステップ中にMODIFYステートメントを使用していない前提での説明となります無限ループになったり挙動が変わることがあるので・・)



サンプルデータ

data DT1;
   length A 8.;
   A=1; output;
   A=2; output;
run;

  A  
  1
  2



暗黙のOUTPUT


以下は単純なデータステップですが、、

data OUT1;
   set DT1;
run;

内部では、SETに指定した「DT1」から1オブザベーション毎にPDV(読み込んだデータを入れておく一時的な箱)で処理してから、出力データセット「OUT1」に出力していて、
この処理の流れが「DT1」から1オブザベーションずつ、全オブザベーションに対して反復されます。


そして実は、データステップの各反復の最後にOUTPUTステートメントが自動で実行されています。
(以下はあくまでもイメージ)

data OUT1;
   set DT1;
   output;
run;


OUTPUTステートメントとは、
読み込んだオブザベーションに対する処理結果を、データセットに出力するステートメントです。

つまりOUTPUTが自動で実行されるおかげで、データセットへの出力ができているわけです。
(これを「暗黙のOUTPUT」と呼びます)



例外


以下のようにプログラマ側が明示的にOUTPUTステートメントを記述した場合、、

data OUT2;
   set DT1;
   if A=2 then output;
run;

  A  
  2

明示的に記述したOUTPUTが優先され、暗黙のOUTPUTは実行されなくなります。



注意


以下の実行結果は想定通りでしょうか?

data OUT3;
   length A B C 8.;
   A=1;
   B=1;
   output;
   C=1;
run;

 A  
 B  
 C  
  1  1  .

① まず「output;」によって、明示的にOUTPUTを行うタイミングを変えています。
② 次に「C=1;」とありますが、①で既にデータセットへの出力が済んでるため、この処理結果はデータセットに反映されません。よってCは欠損値になります(変数自体は作られます。データステップで作成した変数は記述位置に関係なく変数の枠が最初に作られるので)


ちなみに、明示的なOUTPUTを複数記述することもできます。

data OUT4;
   length A B C 8.;
   A=1;
   B=1;
   output;
   C=1;
   output;
run;

 A  
 B  
 C  
  1  1  .
  1  1  1

OUTPUTする度に、データセットにオブベーションが追加され、
2個目のOUTPUTで「C=1;」が反映されたオブザベーションが追加されています。


2015年6月13日土曜日

特定条件の時だけ、RETAIN機能を無効にする。




以下のデータを見てください。

*** サンプルデータ ;
data DT1;
input A B @@;
cards;
1 100 2 . 3 200 4 . 5 400 6 .
;

 A 
   1 
 100  
   2  
  . 
   3  
 200 
   4  
  .
   5  
 400
   6 
  .



このデータで、まずはBの値を単純にRETAINした変数B2を作ってみます。

*** Bの値をRETAIN ;
data OUT1;
   set DT1;

   retain B2;
   if B ^=. then B2 = B;
run;

 A 
B2
   1 
 100  
 100  
   2  
  . 
 100  
   3  
 200 
 200  
   4  
  .
 200  
   5  
 400
 400  
   6 
  .
 400  



次が問題。
たまに特定条件の時だけ、RETAIN機能を無効にしたい事があります。

たとえば、以下のように A=3 の時だけ、RETAINしたくないとします。
(3オブザベーション目の 「200」 が次のオブザベーション以降に保持されないようにする)

 A 
B2 
   1 
 100  
 100  
   2  
  . 
 100  
   3  
 200 
 200  
   4  
  .
   .  
   5  
 400
 400  
   6 
  .
 400  



*** A=3の時だけ、RETAIN機能を無効にする ;
data OUT2;
   set DT1;

   retain B2;
   if B ^=. then B2 = B;
   output;

   if A=3 then B2=. ;
run;



上の赤文字が追加した文。

データセットDT1の3オブザベーション目に対する処理の動きを説明すると、

①「output;」で3オブザベーション目の現状の処理結果をデータセットに出力してから、
②「if A=3 then B2=.;」で A=3 の場合にB2を初期化(欠損値に)しています。


上記①で3オブザベーション目に対する処理結果をデータセットに出力済みなので、②の処理は3オブザベーション目の処理としてはデータセットに反映されず、PDV (処理が行われる一時的な箱) のみに保持されています。

このPDV内のB2は、「retain B2;」により次のオブザベーションを読み込む際にもその値が引き継がれます。


③ 上記②でPDVのB2が初期化された場合、次のオブザベーションには欠損値になったB2が引き継がれる(=RETAIN機能が無効になる)、というわけです。