データセットをランダムに分割するマクロ

Last-modified: 2008-04-28 (月) 22:58:50
/*
データを指定した個数になるべく同じ大きさで分割するマクロです。
このプログラムのポイントは、DATAステップを1回だけ回す点です。
マクロを一度サブミットしてから、利用できます。
使い方は、こんな感じです。

(使用例)
ユーザー定義のライブラリuserdataに存在するSASデータセットtest1を5分割します。
出力データセットはライブラリuserdataに保存され、splitdata1,splitdata2,…,splitdata5
という名前になります。データ分割の際に使用するランダムシードは123です。

%partition1(data=userdata.test1, outlib=userdata, outdataprefix=splitdata,
n= 5, seed=123)
*/

%macro partition1(data=_last_,outlib=WORK,outdataprefix=OUT,n=2,seed=0);

%* data=で指定したSASデータセットの存在をチェックする;
%if %sysfunc(exist(&data))=0 %then %do;
  %put ERROR: 指定したデータセットが存在しません。分割データセットは作成されませんでした。;
  %goto last;
%end;

%* outlib=で指定したSASライブラリの存在をチェックする;
%if %sysfunc(libref(&outlib)) ne 0 %then %do;
  %put ERROR: 指定したライブラリが存在しません。分割データセットは作成されませんでした。;
  %goto last;
%end;

%* n=で指定する分割数は2以上;
%if %sysevalf(&n-2<0) %then %do;
  %put ERROR: n=で指定する分割数は2以上の整数でなくてはなりません。分割データセットは作成されませんでした。;
  %goto last;
%end;

%* n=で指定する分割数は正の整数;
%if %sysevalf(&n-%sysfunc(int(&n))) ne 0 %then %do;
  %let n=%sysfunc(int(&n));
  %put WARNING: n=で指定した数値が正の整数ではなかったため、小数点以下は切り捨てられました。;
%end;

%* 入力データセットのオブザベーション数を取得;
options nonotes;
%let obs=0;
data _null_;
  set &data nobs=n;
  call symput('obs',left(put(n,best.)));
  stop;
run;
options notes;

%* 入力データセットのオブザベーションが1以下のときの処理;
%if &obs<2 %then %do;
  %put WARNING: 入力データセットのオブザベーション数が1以下です。分割データセットは作成されませんでした。;
  %goto last;
%end;

%* オブザベーション数が分割数よりも小さなときの処理;
%if &n>&obs %then %do;
  %put WARNING: 入力データセットのオブザベーション数は、指定した分割数より小さくなっています。
1オブザベーションからなるデータセットが%trim(&obs)個作成されます。;
  %let n=&obs;
%end;

%* 分割された各データセットのオブザベーション数を定め、マクロ変数initialに格納;
%let __mod=%sysfunc(mod(&obs,&n));
%let __int=%sysfunc(int(&obs/&n));
%let initial=;
%do i=1 %to &__mod;
%let initial=&initial %eval(&i*(&__int+1));
%end;
%do i=&__mod+1 %to &n-1;
%let initial=&initial %eval(&i*&__int+&__mod);
%end;

%*分割データセットの作成;
data
%do i=1 %to &n;
&outlib..&outdataprefix.&i
%end;
;
  call streaminit(&seed);
  array __temp{%eval(&n-1)} _temporary_ (&initial);
  set &data;
  __random=(&obs+1-_n_)*rand('uniform');
  %do i=1 %to &n-1;
  if __random<=__temp{&i} then do;
    __ii=&i;
    output &outlib..&outdataprefix.&i;
  end;
  else
  %end;
  do;
  __ii=&n;
  output &outlib..&outdataprefix.&n;
  end;
    do __i=__ii to &n-1;
      __temp{__i}=__temp{__i}+(-1);
    end;
  drop  __random __i __ii;
run;

%*設定に問題があれば、ここに飛ぶ;
%last :;

%mend;