今度は、【SAS】サクッと複数の条件を組み合わせたクロス集計(件数)をする、の割合バージョンになります。
割合に関しては、行方向(表側)、または、列方向(表頭)のどちらのN数を分母に取るかで、出力レイアウトが変わります。
サクッとクロス集計の割合を算出したい人向けのマクロです。
目次
1.サンプルデータ
サンプルとして、以下の外部ファイル読み込みマクロで読み込んだ「国勢調査令和2年人口集計」を使用してみます。 目次 1.タブ区切りの読み込み・サンプルデータ・展開例2. ヘッダー無しカンマ区切りの読み込み・サンプルデータ・展開例・引数inputを指定するパターン・引数vnameとcharを指定するパターン3. ... 続きを見る
【SAS】テキストファイル(カンマ区切り、タブ区切り)の読み込み(INFILE、CSV、TAB)
読み込んだデータ「list_population」は以下になり、性、年代、都道府県の人口分布になっています。
2.展開例
・行方向(表側)を分母にして算出
行方向(表側)のN数を分母にする場合は、引数bunbo=rを指定します。
%cross_rate(
indata =list_population,
sibori =8000 <= area <= 14000,
weight =population,
bunbo =r,
round =,
percent =,
row =area=8000 # area=9000 # area=10000 # area=11000 # area=12000 # area=13000 # area=14000,
row_label =茨城県 # 栃木県 # 群馬県 # 埼玉県 # 千葉県 # 東京都 # 神奈川県,
col =sex=1 # sex=2,
col_label =男 # 女,
prefix =out,
outdata =cross_rate_weight_row
);
下図のように、行方向(表側)のN数が分母になり、男と女の合計が100パーセントになっています。
・行方向(表側)を分母にして算出し、四捨五入する
四捨五入する場合は、引数roundに、小数点以下何桁に丸めるかを指定します。
以下はround=0.01を指定しているので、小数点以下2桁に丸めています。
%cross_rate(
indata =list_population,
sibori =8000 <= area <= 14000,
weight =population,
bunbo =r,
round =0.01,
percent =,
row =area=8000 # area=9000 # area=10000 # area=11000 # area=12000 # area=13000 # area=14000,
row_label =茨城県 # 栃木県 # 群馬県 # 埼玉県 # 千葉県 # 東京都 # 神奈川県,
col =sex=1 # sex=2,
col_label =男 # 女,
prefix =out,
outdata =cross_rate_weight_row_round
);
以下のように、先ほどと違って、割合が四捨五入されています。
偶数丸め(銀行丸め)などではなく、普通の四捨五入なので、合計が約100パーセントになります。
・列方向(表頭)を分母にして算出し、四捨五入して、パーセント表示にする
今度は列方向のN数を分母にするため、引数bunbo=cを指定しています。
四捨五入後にパーセント表示にするため、引数percent=Yを指定しています。
%cross_rate(
indata =list_population,
sibori =8000 <= area <= 14000,
weight =population,
bunbo =c,
round =0.01,
percent =Y,
row =area=8000 # area=9000 # area=10000 # area=11000 # area=12000 # area=13000 and age>=sum(10, 20) # area=14000 ,
row_label =茨城県 # 栃木県 # 群馬県 # 埼玉県 # 千葉県 # 東京都30歳以上 # 神奈川県,
col =sex in (1 2) # . < age < 20,
col_label =男女合計 # 未成年,
prefix =out,
outdata =cross_rate_weight_col_round
);
以下のように、列方向(表頭)のN数が分母になり、100パーセント表示に変わりました。
3.参考プログラム
以下は、参考プログラムになります。
右上のコピーボタンを押せば、プログラム全体をコピーできます。
%macro cross_rate(indata=, sibori=, weight=, bunbo=, round=, percent=, row=, row_label=, col=, col_label=, prefix=out, outdata=);
%put --------------------------------------------------;
%put cross_rate; /*クロス集計(割合)*/
%put &=indata; /*入力データセットを指定(データセットオプションの指定可)*/
%put &=sibori; /*読込みデータの絞り条件(省略可)*/
%put &=weight; /*ウェイト用変数を指定(省略した場合はレコード数の割合を算出する)*/
%put &=bunbo; /*行方向(表側)を分母にする場合はr、列方向(表頭)を分母にする場合はcを指定し、省略した場合は読込んだレコード数が分母となる*/
%put &=round; /*例えば、小数点以下3桁目を四捨五入して2桁にしたい場合は0.01を指定し、省略した場合は四捨五入なしとなる*/
%put &=percent; /*パーセント表示にする場合はYを指定*/
%put &=row; /*表側条件を#で区切って指定(省略可)*/
%put &=row_label; /*表側ラベルを#で区切って指定(省略可)*/
%put &=col; /*表頭条件を#で区切って指定し(省略可)*/
%put &=col_label; /*表頭ラベルを#で区切って指定(省略可)*/
%put &=prefix; /*出力変数の接頭辞を指定(既定値=out)*/
%put &=outdata; /*出力データセットを指定(データセットオプションの指定可)*/
%put --------------------------------------------------;
/*ローカルマクロ変数の定義*/
%local
_row_len
_row_label_len
_row_num
_row_label_num
_r
_c
;
/*表側数の算出*/
%let _row_len =%length(%superq(row));
%let _row_label_len =%length(%superq(row_label));
%if &_row_len. > 0 %then %do;
%let _row_num=%eval(%sysfunc(count(%superq(row), #)) + 1);
%end;
%else %let _row_num=0;
%if &_row_label_len. > 0 %then %do;
%let _row_label_num=%eval(%sysfunc(count(%superq(row_label), #)) + 1);
%end;
%else %let _row_label_num=0;
%if &_row_len. > 0 and &_row_label_len. > 0 and &_row_num. ne &_row_label_num. %then %do;
%put WARNING:指定した表側条件と表側ラベルの数が異なっています;
%end;
%put ----- 指定した表側数チェック -----;
%put &=_row_num;
%put &=_row_label_num;
%put;
/*表頭数の算出*/
%let _col_len =%length(%superq(col));
%let _col_label_len =%length(%superq(col_label));
%if &_col_len. > 0 %then %do;
%let _col_num=%eval(%sysfunc(count(%superq(col), #)) + 1);
%end;
%else %let _col_num=0;
%if &_col_label_len. > 0 %then %do;
%let _col_label_num=%eval(%sysfunc(count(%superq(col_label), #)) + 1);
%end;
%else %let _col_label_num=0;
%if &_col_len. > 0 and &_col_label_len. > 0 and &_col_num. ne &_col_label_num. %then %do;
%put WARNING:指定した表頭条件と表頭ラベルの数が異なっています;
%end;
%put ----- 指定した表頭数チェック -----;
%put &=_col_num;
%put &=_col_label_num;
%put;
/*N数(全体)の追加*/
%let _row_num =%eval(&_row_num. + 1);
%let _row_label_num =%eval(&_row_label_num. + 1);
%let _col_num =%eval(&_col_num. + 1);
%let _col_label_num =%eval(&_col_label_num. + 1);
%let row =%str() # %superq(row);
%if %index(&bunbo., r) > 0 or %length(&bunbo.)=0 %then %let row_label=全体 # %superq(row_label);
%else %let row_label=N数 # %superq(row_label);
%let col=%str() # %superq(col);
%if %index(&bunbo., c) > 0 or %length(&bunbo.)=0 %then %let col_label=全体 # %superq(col_label);
%else %let col_label=N数 # %superq(col_label);
/*データの読込み*/
data _cross_rate_cemp;
set &indata.;
/*絞り条件*/
%if %length(%superq(sibori)) > 0 %then %do;
if &sibori.;
%end;
/*カウント用変数の作成*/
%if %length(&weight.) > 0 %then %do;
if not missing(&weight.) then _count=&weight.;
else _count=0;
%end;
%else %do;
_count=1;
%end;
run;
/*読込んだレコード数*/
proc sql noprint;
select sum(_count) into:_n_all from _cross_rate_cemp;
quit;
%put ----- 読込んだレコード数チェック -----;
%put &=_n_all;
%put;
/*集計*/
%do _r=1 %to &_row_num.;
/*表側条件ごとの絞り込み*/
data _cross_rate_cemp2;
set _cross_rate_cemp;
%if %qscan(%superq(row), &_r., #) ne %str() %then %do;
if %scan(%superq(row), &_r., #);
%end;
run;
%if &sysnobs. > 0 %then %do;
data _cross_rate_sum&_r.;
keep
joken
label
_num1 - _num&_col_num.
;
set _cross_rate_cemp2 end=eof;
length joken label $1000.;
%if %qscan(%superq(row), &_r., #) ne %str() %then %do;
joken=strip("%qscan(%superq(row), &_r., #)");
%end;
%else %do;
joken="";
%end;
%if %qscan(%superq(row_label), &_r., #) ne %str() %then %do;
label=strip("%qscan(%superq(row_label), &_r., #)");
%end;
%else %do;
label="";
%end;
%do _c=1 %to &_col_num.;
%if %qscan(%superq(col), &_c., #) ne %str() %then %do;
if %scan(%superq(col), &_c., #) then _num&_c. + _count;
%end;
%else %do;
_num&_c. + _count;
%end;
%end;
if eof;
run;
%end;
%else %do;
/*全レコード数が0の場合はダミーデータを作成する*/
data _cross_rate_sum&_r.;
length joken label $1000.;
%if %qscan(%superq(row), &_r., #) ne %str() %then %do;
joken=strip("%qscan(%superq(row), &_r., #)");
%end;
%else %do;
joken="";
%end;
%if %qscan(%superq(row_label), &_r., #) ne %str() %then %do;
label=strip("%qscan(%superq(row_label), &_r., #)");
%end;
%else %do;
label="";
%end;
%do _c=1 %to &_col_num.;
_num&_c. = 0;
%end;
run;
%end;
%end;
/*統合して出力*/
data &outdata.;
keep
no
joken
label
%do _c=1 %to &_col_num.;
&prefix.&_c.
%end;
;
label
no="No"
joken="表側条件"
label="表側ラベル"
%if %index(&bunbo., c) > 0 %then %do;
%if &_col_label_len. > 0 %then %do;
%do _c=1 %to &_col_label_num.;
%if %qscan(%superq(col_label), &_c., #) ne %str() %then %do;
&prefix.&_c.="%qscan(%superq(col_label), &_c., #)"
%end;
%else %do;
&prefix.&_c.=""
%end;
%end;
%end;
%else %if &_col_len. > 0 %then %do;
%do _c=1 %to &_col_num.;
%if %qscan(%superq(col), &_c., #) ne %str() %then %do;
&prefix.&_c.="%qscan(%superq(col), &_c., #)"
%end;
%else %do;
&prefix.&_c.=""
%end;
%end;
%end;
%end;
%else %do;
&prefix.1="%qscan(%superq(col_label), 1, #)"
%if &_col_label_len. > 0 %then %do;
%do _c=2 %to &_col_label_num.;
%if %qscan(%superq(col_label), &_c., #) ne %str() %then %do;
&prefix.&_c.="%qscan(%superq(col_label), &_c., #)"
%end;
%else %do;
&prefix.&_c.=""
%end;
%end;
%end;
%else %if &_col_len. > 0 %then %do;
%do _c=2 %to &_col_num.;
%if %qscan(%superq(col), &_c., #) ne %str() %then %do;
&prefix.&_c.="%qscan(%superq(col), &_c., #)"
%end;
%else %do;
&prefix.&_c.=""
%end;
%end;
%end;
%end;
;
/*N数列を追加した分をリネームしてずらす*/
rename
&prefix.1=all
%do _c=2 %to &_col_num.;
&prefix.&_c.=&prefix.%eval(&_c. - 1)
%end;
;
set _cross_rate_sum1 - _cross_rate_sum&_row_num.;
no=_n_-1;
%if %index(&bunbo., c) > 0 %then %do;
/*表頭を分母にする場合は分母用変数を作成する*/
retain _bunbo1-_bunbo&_col_num.;
if no=0 then do;
*表頭のN数;
%do _c=1 %to &_col_num.;
&prefix.&_c.=_num&_c.;
%end;
%do _c=1 %to &_col_num.;
_bunbo&_c.=_num&_c.;
%end;
end;
else do;
%do _c=1 %to &_col_num.;
if _bunbo&_c. > 0 then &prefix.&_c.=_num&_c. / _bunbo&_c.;
else &prefix.&_c.=.;
%end;
end;
%end;
%else %if %index(&bunbo., r) > 0 %then %do;
/*表側を分母にする場合*/
&prefix.1=_num1;
%do _c=2 %to &_col_num.;
if _num1 > 0 then &prefix.&_c.=_num&_c. / _num1;
else &prefix.&_c.=.;
%end;
%end;
%else %do;
/*読込んだレコード数を分母にする場合(デフォルト)*/
&prefix.1=_num1;
%do _c=1 %to &_col_num.;
if &_n_all. > 0 then &prefix.&_c.=_num&_c. / &_n_all.;
else &prefix.&_c.=.;
%end;
%end;
/*四捨五入*/
%if %length(&round.) > 0 %then %do;
%if %index(&bunbo., c) > 0 %then %do;
if no ne 0 then do;
%do _c=1 %to &_col_num.;
if &prefix.&_c. ne . then &prefix.&_c.=round(&prefix.&_c., &round.);
%end;
end;
%end;
%else %do;
%do _c=2 %to &_col_num.;
if &prefix.&_c. ne . then &prefix.&_c.=round(&prefix.&_c., &round.);
%end;
%end;
%end;
/*パーセント表示*/
%if %upcase(&percent.) = Y %then %do;
%if %index(&bunbo., c) > 0 %then %do;
if no ne 0 then do;
%do _c=1 %to &_col_num.;
if &prefix.&_c. ne . then &prefix.&_c.=&prefix.&_c. * 100;
%end;
end;
%end;
%else %do;
%do _c=2 %to &_col_num.;
if &prefix.&_c. ne . then &prefix.&_c.=&prefix.&_c. * 100;
%end;
%end;
%end;
run;
/*不要データの削除*/
proc datasets lib=work noprint;
delete _cross_rate_:;
quit;
%mend cross_rate;