タイトルのような質問をよく頂きます。
製薬業界だと、CDISC対応で文字変数のLENGTHを実データの最大長に合わせたい時に必要になりますね。
求め方は、すでにデータステップ100万回のSASYAMAさんが記事を載せています。
データセット内のすべての文字変数に対して、それぞれに入っているテキストの長さの最大値を取得するハッシュオブジェクト
やりたい事とHASHオブジェクトがマッチしていて素晴らしいプログラムです。私も他の解法を考えてみたくなりました。
data DT1;
input ONE (TWO THREE FOUR)(:$10.);
cards;
1 aaa cc .
2 bbbb ddd .
3 . ee .
;
|
今回はこのDT1が、実データの最大長を取得したい対象のデータセットとします。
※ 本題に入る前に
今回紹介するプログラムでは文字の長さを「LENGTH関数」を使って調べてますが注意が必要です(以下参照)
何が言いたいかというと、変数が欠損値の場合、LENGTH関数は「1」を返します。
もし「0」と返してほしいなら、プログラム中の「LENGTH関数」を「LENGTHN関数」に変える必要があります。
まずは平凡なプログラムから。
※諸注意
以下プログラム中で「_VARNAM」「_VARLEN」「_i」という変数を作っていますが、対象のデータセットにもこの3つと同じ名前の変数がある場合、処理がうまくいきません。
その場合はプログラム中の「_VARNAM」「_VARLEN」「_i」を別の変数名(対象のデータセットにはない変数名)に変えてください。
data OUT1;
set DT1;
* 文字変数を配列に格納 ;
array ar(*) _character_;
* 変数名とlengthを出力 ;
length _VARNAM $32. _VARLEN 8.;
do _i = 1 to dim(ar);
_VARNAM = vname(ar(_i));
_VARLEN = length(ar(_i));
output;
end;
keep _VARNAM _VARLEN;
run;
* 変数毎の最大lengthを求める ;
proc summary data=OUT1 nway;
class _VARNAM;
output max(_VARLEN)=_VARLEN out=OUT2(keep=_VARNAM _VARLEN);
run;
|
内部処理的には無駄が多いものの、プログラムとしては単純かつ楽に書けるため私はよくこの方法使ってます。
※諸注意
以下プログラムでは「_TRANUM」という変数を作っていますが、対象のデータセットにもこれと同じ名前の変数がある場合、処理がうまくいきません。
その場合はプログラム中の「_TRANUM」を別の変数名(対象のデータセットにはない変数名)に変えてください。
* 後のtranspose用にobs毎の連番を持たせる ;
data OUT1;
set DT1;
* 全文字変数を縦に転置 ;
proc transpose data=OUT1 out=OUT2 name=_VAR prefix=COL;
format _character_;
* 変数毎の最大lengthを求める ;
proc sql;
create table OUT3 as
select _VAR, max(length(COL1)) as _LEN
from OUT2
group by 1;
quit;
|
方法③ LUAプロシジャ(SAS9.4メンテナンスリリース3から追加になったプロシジャ)
個人的に書いてて一番楽しかったです。
LUAの知識は初心者レベルなので、これが最適解なのか不明、、もっといい書き方やこういう書き方も出来るってのがあったら教えてほしいです。
proc lua;
submit;
-- データセットをopen
local dsid = sas.open("work.dt1")
local tb = {}
-- 文字変数名をテーブルに格納
for var in sas.vars(dsid) do
if var.type == "C" then
tb[#tb+1] = {col=var.name, len=0}
end
end
-- OBSを読み込んでいって最大lengthを求める
while sas.next(dsid) do
for i in pairs(tb) do
tb[i].len = sas.max( sas.length(sas.get_value(dsid,tb[i].col)), tb[i].len )
end
end
-- データセットを閉じる&テーブルの中身をデータセットに出力
sas.close(dsid)
sas.write_ds(tb,"work.out1")
endsubmit;
run;
|