2016年3月31日木曜日

配列の特定の要素番号に割り当てられている変数名を取得する



タイトル通りの質問を頂きました。これはVNAME関数を使えば一発です。

data TEST;
    array AR(3) A B C;

    length VAR1 $40.;
    VAR1 = vname( AR(2) );
    put VAR1;
run;


ログ
B

配列ARの要素番号2に割り当てられている変数名を取得しています。

VNAME関数は変数だけでなく配列も指定できるってのが便利です。

2016年3月29日火曜日

オブザベーション数を取得してマクロ変数に格納する方法 【まとめ】


ぱっと思いつく限りあげてみました。他に面白い方法があれば教えてほしいです。


*** サンプルデータ ;
data DT1;
  do i=1 to 100;
    output;
  end;
run;


① SQL INTO
proc sql noprint;
  select count(*) into :OBS1 trimmed
  from DT1;
quit;

%put &OBS1;


② NOBS
data _NULL_;
  if 0 then set DT1(drop=_ALL_)  nobs=NOBS;
  call symputx("OBS2", NOBS);
  stop;
run;

%put &OBS2;

参照…http://sas-boubi.blogspot.jp/2015/06/blog-post_26.html(過去記事)
※ 注意事項があるので過去記事参照


③ SQL DICTIONARY
proc sql noprint;
  select NLOBS into :OBS3
  from DICTIONARY.TABLES
  /* ライブラリ名とデータセット名を大文字で指定する */
  where LIBNAME="WORK" and MEMNAME="DT1";
quit;

%put &OBS3;

参照…http://sas-boubi.blogspot.jp/2015/08/dictionary.html(過去記事)


④ OPEN, ATTRN関数
data _NULL_;
  DSID = open("DT1");
  OBS  = attrn(DSID, "NLOBSF");
  call symputx("OBS4",OBS);
run;

%put &OBS4;

まず、OPEN関数でデータセットを開く(戻り値としてデータセット識別子というIDが返される)
次に、ATTRN( データセット識別子, "NLOBSF" ) でオブザベーション数を取得しています。


⑤ HASH
data _NULL_;
  if 0 then set DT1;
  dcl hash hs(dataset :"DT1", multidata:"y");
  hs.definekey("i");
  hs.definedone();
  call symputx("OBS5", hs.num_items);
  stop;
run;

%put &OBS5;

参照…http://sas-tumesas.blogspot.jp/2014/07/clearnumitemsdeleteremove.html(データステップ100万回)

ポイント
multidata:"y" で重複キーも保持するようにしています(ハッシュオブジェクトはデフォルトだと、キーが重複するレコードに対して、最初のレコードしか保持しないので)


⑥ SYSNOBS
参照…http://sas-boubi.blogspot.jp/2014/01/blog-post_8902.html(過去記事)


⑦ SQLOBS
参照…http://sas-boubi.blogspot.jp/2014/09/blog-post_8.html(過去記事)

2016年3月24日木曜日

SASのバージョンを取得する方法



SASのバージョン情報が格納されている3つのマクロ変数を紹介。
自宅で遊んでるSAS OnDemandのバージョン情報を出してみます。

   %put  &SYSVER;         *** ⇒ 9.4 ;
   %put  &SYSVLONG;    *** ⇒ 9.04.01M3P062415 ;
   %put  &SYSVLONG4;  *** ⇒ 9.04.01M3P06242015 ;


「SYSVLONG」「SYSVLONG4」 の方がメンテナンスレベル等、詳細な情報が格納されてます。


ちょっと前、「顧客にSASプログラムを納品したらERRORが出て動かないってクレームが来たよ。。」という話しを聞きました。

「顧客とその会社では同じSAS9.4を使ってて、なんでだろうねー」ってとこで話しは終わっちゃいましたが、メンテナンスレベルとかが異なってたのかもですね。
(上の例では 「9.04.01M3P062415」 なのでメンテナンスレベル3)

実は同じSAS9.4でもマイナーバージョンアップが行われて新機能が追加されたりするので、上のような事が起こり得るわけです。
他にもHotfix(不具合に対する修正プログラム)を当ててるかどうかっても挙動に影響します。


また、SAS/STATなどプロダクト毎にカスタムバージョンがあるのでその辺も確認しておく必要があるかと思います。
以下プロシジャでプロダクト毎のバージョンをログに表示できます。

  proc product_status;
  run;

2016年3月23日水曜日

SASで基本統計量を求める 【MEANSプロシジャ】




基本構文

PROC  MEANS  DATA=対象データセット;
      VAR  分析変数 ;
RUN;



基本統計量を出力

proc means data=SASHELP.CLASS ;
   var AGE;
run;




グループ分けして基本統計量を出力

proc means data=SASHELP.CLASS ;
   var AGE;
   class SEX;
run;


CLASSステートメントで、グループ分けして基本統計量を出力する事が出来ます。
上の例ではSEXの値「F」「M」毎に基本統計量を出しています。


ただし、CLASS変数が欠損値のオブザベーションは集計から除かれます。
欠損値も含めるには「CLASS 分類変数 / MISSING;」と記述します(詳細は以下記事を参照)

http://sas-boubi.blogspot.com/2017/03/missing_21.html




表示する統計量を指定する

proc means data=SASHELP.CLASS n min median max q1 q3 range;
   var AGE;
run;




表示したい統計量を指定できます。
  統計量   内容
    非欠損値の数
  NMISS  欠損値の数 
  SUM  合計
  MEAN  平均値
  MIN  最小値
  MEDIAN   中央値
  MAX   最大値   
  RANGE  範囲
  MODE  最頻値   
  STDDEV  標準偏差   
  STDERR  標準誤差   
  Q1  下側四分位点 
  Q3  上側四分位点  
  QRANGE  四分位範囲  
  KURT  尖度
  SKEW  歪度
  …etc


2016年3月22日火曜日

数値を四捨五入して文字変数に格納したい場合、PUT関数を使うと想定外の結果になる。



数値を四捨五入して文字変数に格納したい場合、PUT関数を使ってませんか?
以下の例を見てみましょう。

想定通りの結果
data DT1;
    length B $10.;
    A = 1.25;
    B = left(put(A, 12.1));
    put B=;
run;

ログ
B=1.3

小数第2位を四捨五入して文字化した変数が出来ました。
次はどうでしょう。


想定外の結果
data DT1;
    length B $10.;
    A = 4.1-4.05;
    B = left(put(A, 12.1));
    put B=;
run;

ログ
B=0.0

4.1-4.05 = 「0.05」 なので、小数第2位を四捨五入すると「0.1」となるはずが、「0.0」になりました。

これは浮動小数点誤差ってやつのせいですね。
http://sas-tumesas.blogspot.jp/2014/03/blog-post_14.html (データステップ)100万回)
http://blog.n-insight.co.jp/2015/05/blog-post_27.html (NIBLOG)


つまり「4.1-4.05」は「0.05」ではなく「0.049999999・・・」みたいな値で格納されてます。
だからPUT関数で「0.0」という値が帰ってきたわけですね。



で、これをどうするかは各自(各社)いろいろ考え・やり方があると思います。
よくROUND関数をかませたりしますが、、、
data DT1;
    length B $10.;
    A = 4.1-4.05;
    B = left(put( round(A,0.1) ,8.1));
    put B=;
run;

ログ
B=0.1



📝ROUND関数には注意すべき動きがある

ROUND関数は「第1引数を第2引数の最も近い倍数に丸める関数」ですが、
その独特の動きや制限も重要です。以下にSAS社のドキュメントの記述を抜粋します
(※日本語ドキュメントは誤記がある?のでちょっと分かりにくいですが英語の方を示します。。)


The Effects of Rounding
Rounding by definition finds an exact multiple of the rounding unit that is closest to the value to be rounded. For example, 0.33 rounded to the nearest tenth equals 3*0.1 or 0.3 in decimal arithmetic. In binary arithmetic, 0.33 rounded to the nearest tenth equals 3*0.1, and not 0.3, because 0.3 is not an exact multiple of one tenth in binary arithmetic.

The ROUND function returns the value that is based on decimal arithmetic, even though this value is sometimes not the exact, mathematically correct result. In the example ROUND(0.33,0.1), ROUND returns 0.3 and not 3*0.1.

「第1引数を第2引数の最も近い倍数に丸める」処理において、10進数の算術と2進数の算術では「正確な倍数」の扱いの違いがあって、そこをROUND関数では調整してるような事が書かれているのでしょうか(間違っていたらご指摘をお願いします)


Producing Expected Results
In general, ROUND(argument, rounding-unit) produces the result that you expect from decimal arithmetic if the result has no more than nine significant digits and any of the following conditions are true:
  • The rounding unit is an integer.
  • The rounding unit is a power of 10 greater than or equal to 1e-15. (If the rounding unit is less than one, ROUND treats it as a power of 10 if the reciprocal of the rounding unit differs from a power of 10 in at most the three or four least significant bits.)
  • The result that you expect from decimal arithmetic has no more than four decimal places.

期待される結果を生成する条件が示されています。思ったより扱える範囲が狭い?
ちなみに文中の、significant digits と decimal places の意味を混同してるケースをよく見かけるので注意。



その他の挙動についてもドキュメントに書かれているので、要確認!
ROUND関数を単純に四捨五入するやつと思っていると、想定外の結果になるかもしれません。
例えば以下のように最後の桁が「8」か「9」かで結果が異なっています。
data DT1;
 A = round(1.049999999998,0.1);
 B = round(1.049999999999,0.1);
 put A= B=;
run;

ログ
A=1 B=1.1



というわけで、一概にこうすればいいってのはない気がします。
「私はこうしてます」 とか何かアイディアがあったら教えてほしいです。

2016年3月18日金曜日

SASで頻度集計とクロス集計 【FREQプロシジャ】




構文 : 頻度集計

PROC  FREQ  DATA=対象データセット;
       TABLES  対象変数  ;
RUN;

対象変数が欠損値のオブザベーションは集計から除かれます。
欠損値も含めるには「TABLES  対象変数  /  MISSING;」と指定します。



proc freq data=SASHELP.CLASS;
     tables AGE ;
run;







構文 : クロス集計

PROC  FREQ  DATA=対象データセット;
    TABLES  表側変数 *  表頭変数 ;
RUN;

表側または表頭変数が欠損値のオブザベーションは集計から除かれます。
欠損値も含めるには「TABLES  表側変数 * 表頭変数  /  MISSING;」と指定します。




proc freq data=SASHELP.CLASS;
     tables SEX * AGE;
run;


度数, パーセント, 行のパーセント, 列のパーセントが出力されます


対象変数が欠損値のオブザベーションが集計から除かれる件の詳細は以下参照。
http://sas-boubi.blogspot.com/2017/03/missing_21.html


2016年3月17日木曜日

RTF出力時に透かし文字を設定する 【WATERMARKオプション】


SAS9.3からTAGSETS.RTF の 「WATERMARKオプション」 で、透かし文字を設定できます。


”Sample” という透かし文字を設定してみます。
ods tagsets.rtf file="出力するRTFファイルのフルパスを指定" options( watermark = 'Sample' ) ;

   proc print data=SASHELP.CLASS;
   run;

ods tagsets.rtf close;




IF-THEN-ELSE入門 【条件分岐処理】


IF-THEN-ELSEステートメントは条件によって処理を分岐する場合に使います。




構文1

IF  条件式  THEN  処理 ;

  • 「条件式」を満たす場合に、指定した「処理」を実行する。




data DT1;
   set SASHELP.CLASS;
   if AGE=11 then X=1;
run;
  • 「AGE=11」 だったら、「変数X=1」 にする。





構文2

IF  条件式1  THEN  処理1 ;
ELSE  IF  条件式2  THEN  処理2;
ELSE  処理3;

  • 「条件式1」を満たす場合に、「処理1」を実行。
  • 「条件式1」以外で「条件式2」を満たす場合に、「処理2」を実行。
  • 「条件式1」と「条件式2」以外だったら、「処理3」を実行。




data DT2;
   set SASHELP.CLASS;
   if AGE=11 then X=1;
   else if AGE=12 then X=2;
   else if AGE=13 then X=3;
   else X=99;
run;




構文3

IF  条件式  THEN  DO;
      処理1 ;
      処理2 ;
      ~
END;
ELSE  IF  条件式  THEN  DO;
      処理1 ;
      処理2 ;
      ~
END;
ELSE  DO;
      処理1;
      処理2;
END;

  • DO~ENDで囲って処理を複数指定できます。




data DT2;
   set SASHELP.CLASS;
   if AGE=11 then do;
          X=1;
          Y=1;
   end;
   else do;
          X=99;
          Y=99;
   end;
run;

  • 「AGE=11」 だったら、「変数X=1」「変数Y=1」 にする。
  • それ以外だったら「変数X=99」「変数Y=99」にする。




2016年3月15日火曜日

ODS出力時に文字の書式設定をする【インラインフォーマット】



HTMLやRTFなどのODS出力時に、インラインフォーマットを使って文字の書式設定等が出来ます。
(本機能はSGPLOT・GTLでは、ほとんど動作しません)




インラインフォーマット (一部)

設定内容指定備考
上付き文字にする
{SUPER 文字}  
下付き文字にする
{SUB 文字}  
  
文字のスタイル設定(書式)をかえる 
{STYLE [スタイル設定]文字} 
S={スタイル設定}文字

という書き方もある。
(S=は大文字じゃなきゃダメ)
Unicode記号の表示
{UNICODE unicode値}
  
ノーブレークスペースを挿入
{NBSPACE}

改行 
{NEWLINE 改行数} 
    
ページ番号を表示
(現ページ of 総ページ)
{PAGEOF}
RTF, TAGSETS.RTF で利用可

TITLE・FOOTNOTEステートメント内で使用

ODS RTF で BODYTITLEオプションが設定されていると正しく動作しない
現在のページ番号を表示
{THISPAGE}
RTF, TAGSETS.RTF, PRINTER で利用可

TITLE・FOOTNOTEステートメント内で使用

ODS RTF で BODYTITLEオプションが設定されていると正しく動作しない 
最終ページ番号を表示
{LASTPAGE}
RTF, TAGSETS.RTF で利用可

TITLE・FOOTNOTEステートメント内で使用

ODS RTF で BODYTITLEオプションが設定されていると正しく動作しない


📝
以下の要素によっては、インラインフォーマットの挙動が異なったり、動作しないケースがあるので、リファレンスを確認したり動作確認するなどした方が良いです。
  • 出力先の種類(HTMLやRTFなど)
  • インラインフォーマットの種類
  • エスケープ文字の設定方法



構文1

  (*ESC*){ インラインフォーマット }


前に (*ESC*) を入れると、インラインフォーマットとして解釈させることが出来る。
(「ESC」は大文字で記述する)


*** タイトルを青くする例 ;
title "(*ESC*){style [color=blue]abcde}";

ods rtf file="出力先のパスを指定\test.rtf";
   proc print data=sashelp.class;
   run;
ods rtf close;




構文2

  ODS ESCAPECHAR = 'エスケープ文字 ';
  エスケープ文字{ インラインフォーマット }


(*ESC*) の代わりとなるエスケープ文字を設定することができます。

*** タイトルを青くする例 ;
ods escapechar='~';
title "~{style [color=blue]abcde}";

ods rtf file="出力先のパスを指定\test.rtf";
  proc print data=sashelp.class;
  run;
ods rtf close;


📝注意
  • エスケープ文字を「/」とかありきたりなのにすると、例えば「2016/03/15」のような文字に含まれてる「/」もエスケープ文字として解釈しちゃうので注意(結構ありがち)
  • RTF出力時に「\」(円マーク、バックスラッシュ)をエスケープ文字にしない方が良いようです(RTFで特別な意味を持つ記号のため)




変数値にもインラインフォーマットが利用可能

*** ODS出力時に変数値を途中で改行させる例 ;
data DT1;
   A="SAS(*ESC*){newline 1}BOUBIROKU";
run;

ods rtf file="出力先のパスを指定\test.rtf";
  proc print data=DT1;
  run;
ods rtf close;


2016年3月14日月曜日

SCAN関数で、右からn個目に区切られた文字を取得する。




SCAN関数の使い方含め、落とし穴もあるので、まずは以下リンク記事をご参照下さい。
SCAN関数の落とし穴



そして本題。
SCAN関数の第2引数に「負の値」を指定すると、右からn個目に区切られた文字を取得するように変更できます。

data DT1;
   length A B $20.;
   A = "ABC,DEF,GHI";
   B = scan( A , -1, "," );
run;

  A
  B  
  ABC,DEF,GHI     GHI  


第2引数に 「-1」 と指定しているので、区切った文字の右から1個目の文字 「GHI」 を取得しています。

日本語に対応した 「KSCAN関数」 も同様に負の値を指定することが出来ます。

2016年3月11日金曜日

SQLプロシジャの 「NATURAL JOIN」 で3つ以上のデータセットを結合すると意図した結果にならない。


タイトル通りのご指摘をいただきました。

 「NATURAL JOIN」 については以前紹介しています。
SQLプロシジャで結合条件を省略する方法。


というわけで試してみます。以下2つの例は3つのデータセットを「NATURAL JOIN」で結合しているプログラムです。
。。。FEEDBACKオプションでログにプログラムを展開してみると。。


例①
proc sql feedback;
   create table OUT1 as
   select * from
   (select name from SASHELP.CLASS a where AGE=11)
      natural full outer join
   (select name from SASHELP.CLASS b where AGE=12)
      natural full outer join
   (select name from SASHELP.CLASS c where AGE=13)
;
quit;

ログ (環境・バージョンによって挙動が異なるかもしれません)

  select COALESCE(C.Name, A.Name) as Name
    from ( select A.Name
             from SASHELP.CLASS A
            where A.Age = 11
         ) full outer join
         ( select B.Name
             from SASHELP.CLASS B
            where B.Age = 12
         ) on B.Name = A.Name full outer join
         ( select C.Name
             from SASHELP.CLASS C
            where C.Age = 13
         ) on C.Name = A.Name;


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

proc sql feedback;
   create table OUT1 as
   select * from
   (select name from DT1 a where AGE=11)
      natural full outer join
   (select name from DT2 b where AGE=12)
      natural full outer join
   (select name from DT3 c where AGE=13)
;
quit;


ログ(環境・バージョンによって挙動が異なるかもしれません)

  select COALESCE(C.Name, A.Name, B.Name) as Name
    from ( select A.Name
             from WORK.DT1 A
            where A.Age = 11
         ) full outer join
         ( select B.Name
             from WORK.DT2 B
            where B.Age = 12
         ) on B.Name = A.Name full outer join
         ( select C.Name
             from WORK.DT3 C
            where C.Age = 13
         ) on (C.Name = A.Name) and (C.Name = B.Name);



2つの例は同じようなプログラムなのに、展開されるプログラムが異なっています。
(青字と赤字の部分)

マニュアルみても言及してないし、仕様なのかバグなのか不明です。何かご存知の方いたら教えてください。

てことで、「NATURAL JOIN」 で 3つ以上のデータセットを結合しない方がよさそうです。


2016年3月10日木曜日

SELECT-WHEN入門 【条件分岐処理】



SELECT-WHEN は変数の値によって処理を分岐するような場合に使います。



書き方1

data DT1;
  set SASHELP.CLASS;
  select (AGE);
       when (11)       X=1;     * ① ;
       when (12)       X=2;     * ② ;
       when (13)       X=3;     * ③ ;
       when (14,15)  X=4;     * ④ ;
       otherwise       X=99;   * ⑤ ;
  end;
run;


・AGEの値によって変数Xにいれる値を分岐しています。
・①~④の順に見ていって「AGEの値と一致する ”最初のWHENステートメント”」のみ実行される。
・AGEの値がどれにも一致しなかった場合⑤のOTHERWISEが実行される。

※ AGEがどのWHENステートメントにも一致しない場合、OTHERWISEがないとエラーになる。



書き方2

data DT1;
  set SASHELP.CLASS;
  select;
       when (AGE=11 and WEIGHT>100)   X=1;
       when (AGE=12 and WEIGHT>100)   X=2;
       otherwise                                            X=99;
  end;
run;

こちらも「条件に該当する ”最初のWHENステートメント”」のみ実行される。



応用

data DT1;
  set SASHELP.CLASS;
  select;
      when (AGE=11) do;
          X1=1;
          X2=1;
      end;
      otherwise;
  end;
run;

・DO~ENDで囲めば、処理を複数書ける。
・「OTHERWISE ;」のように処理文を省略すると「OTHERWISEの場合、なにも処理しないで」という指定になる。



2016年3月4日金曜日

【%DATATYP】 データステップ外でマクロ変数値のデータタイプを判定する




構文

  %DATATYP( 判定したい値 )


  • 値が半角数値なら 「NUMERIC」、文字なら 「CHAR」 が返される
  • 「+1」「-1」「1e10」「1d10」みたいな表現も「NUMERIC」と判定される
  • 全角等のマルチバイト文字に対応してるかマニュアルに記載がなかったので、マルチバイト文字は入れないほうが無難かも



  %let VAR1 = 10.5;
  %put %datatyp(&VAR1);

ログ
NUMERIC



色々な値で試してみました。

%put %DATATYP(100);    /* → NUMERIC  */
%put %DATATYP(1.0);     /* → NUMERIC  */
%put %DATATYP(+1.0);   /* → NUMERIC  */
%put %DATATYP(-1.0);   /* → NUMERIC  */
%put %DATATYP(1e-1);  /* → NUMERIC  */
%put %DATATYP(1d-1);  /* → NUMERIC  */
%put %DATATYP(1+);     /* → CHAR         */
%put %DATATYP(abc);   /* → CHAR         */
%put %DATATYP();         /* → CHAR         */

2016年3月3日木曜日

箱ひげ図をかく 【GTL編】




GTLの詳細については、以下リンクをご覧ください。



BOXPLOTステートメントの構文


  BOXPLOT X=カテゴリ Y=分析変数 / オプション;


  • デフォルトでカテゴリ(X軸)は離散軸になりますが、ユーザー側で線形軸等に変えると、挙動が変わってしまいます。詳細はリファレンスを要確認。



オプション

  設定内容  指定   詳細設定
  箱ひげ図を横に寝かす 

  ORIENT =  HORIZONTAL  

    
  箱の幅

  BOXWIDTH  =  0~1

  
  箱の書式

  FILLATTRS  =  (詳細設定) 

  COLOR = 色

  ひげの書式

  WHISKERATTRS  =  (詳細設定) 

  COLOR = 色

  中央値の書式

  MEDIANATTRS  =  (詳細設定)

  COLOR = 色

  平均値の書式 

  MEANATTRS  =  (詳細設定)
  
  COLOR   = 色
  SYMBOL = 表示マーク  

  外れ値の書式 

  OUTLIERATTRS  =  (詳細設定)   

  SYMBOL = 表示マーク 
  

…etc ( 色々できるのでリファレンスを確認してみてください )



注意
  • オプションの組み合わせで挙動が変わる可能性あり。
  • デフォルトで出力される箱ひげ図のタイプ(箱、ひげ、外れ値等の定義)はリファレンスを要確認



1. 簡単な例

proc template ;
  define statgraph MYGRAPH;
      begingraph ;
         entrytitle "お魚の体重分布";
         layout overlay  ;
              boxplot x=SPECIES y=WEIGHT;
         endlayout;
       endgraph;
  end;
run;

proc sgrender data=SASHELP.FISH template=MYGRAPH;
run;



2.カスタマイズ

proc template ;
  define statgraph MYGRAPH;
      begingraph ;
         entrytitle "お魚の体重分布";
         layout overlay  ;
              boxplot x=SPECIES y=WEIGHT /
                            orient = horizontal
                            meanattrs = (color=black symbol=plus)
                            medianattrs = (color=black)
                            whiskerattrs = (color=black)
                            fillattrs = (color=white)
                            outlierattrs = (symbol=circlefilled)
                            boxwidth = 0.5
              ;
         endlayout;
       endgraph;
  end;
run;

proc sgrender data=SASHELP.FISH template=MYGRAPH;
run;




2016年3月2日水曜日

テキストファイル(CSVなど)の読み込みと注意点 【IMPORTプロシジャ編】



  • 可変長テキストファイルの読み込みに限定した説明になります。
他の形式で今回のプログラムを流用すると、重要なオプションが効かず、文字切れや変数属性の設定がうまくいかない等の可能性あり。

  • とにかく、想定外の結果になりやすいので、出来たデータセットは要確認です。
例えば、聞いた話ですが、テキストファイルに「クォーテーション」「改行コード」「見えない改行コード」が含まれている際に、正しく読み込めない事があったようです。




構文

 PROC IMPORT
   OUT                         =   出力データセット
   DATAFILE                =   "読込テキストファイル"   /* パスが長いと実行失敗します */
   DBMS                      =    CSV | TAB | DLM             /* ファイル種類 */
   REPLACE                     /* 既存のデータセットを置き換える */
   ;
   DELIMITER             =   "区切り文字";    /* DBMS=DLMの場合に指定 */
   GETNAMES            =   YES | NO ;        /* 1行目を変数名として読み込むか */
   DATAROW              =   行番号 ;             /* 何行目から読み込むか */
   GUESSINGROWS  =   行番号 | MAX;   /* 何行目までのデータで変数属性を判定するか */
 RUN ;


DBMS=オプション
  指定  設定内容
  CSV    カンマ区切り 
  TAB    タブ区切り
  DLM   自分で 「DELIMITER=オプション」 に区切り文字を指定 



📚 注意点


① GUESSINGROWS=オプション ※重要!

SASはテキストファイルの先頭20行を読み込んで各変数の属性(データ型やlength)を決める。

  • 20行目まで数値で、21行目に文字が入ってきた場合
        ⇒ 数値型になるので、文字は読み込めず欠損値になる。

  • 20行目まで文字の最大長さが100バイトで、21行目に200バイトの文字が入ってきた場合
        ⇒length=$100となるので、101バイト以降の文字は切り捨てられる。


てことで、変数属性を判定する行数をGUESSINGROWSで設定しなおします。
ちなみに、SAS9.3 から 「GUESSINGROWS = MAX」 という指定が可能(MAX = SASが読込める最大行数)



② 制限(環境・バージョンによって異なる可能性あり)

 項目 内容
 SASが読込める最大行数   32767行【SAS9.2】、2147483647行【SAS9.3, 9.4】 
 1行につき読込めるバイト数   区切り文字も含め32767バイト



③ REPLACEオプション

世代管理されたデータセット(GENMAX=オプション)をREPLACEすると、そのデータセットの既存の世代データがすべて削除されるようなので注意。



④ GETNAMES=オプション

「YES」で1行目を変数名として読み込んだ場合、変数名として成立しない空白・特殊文字などが含まれていると、SASが変数名として成立する名前に変換してくれます。ただ、システムオプション「VALIDVARNAME」の設定によって、変換される変数名が変わるので注意。



最後に、気づいた範囲で「うまく読み込めない例」を紹介。



うまく読み込めない例(環境・バージョンで動作が異なるかも)


C:\test\test.txt
X,Y,Z
001, pen,2016/01/01
002,apple,2016/01/02


/* PROC IMPORT */
proc import out=OUT1
    datafile="C:\test\test.txt"
    dbms=dlm  replace
    ;
    delimiter=",";
    getnames=yes;
    datarow=2;
    guessingrows=max;
run;


/* 出力データの変数属性を確認 */
proc contents data=OUT1;
run;


① 変数X: 0落ち問題

数値変数として読み込まれて、しかも結果が読み込み元の値と異なっている(先頭の0が取れた: 001 → 1 )
読み込み元の値をダブルクォーテーションで囲ったら0落ちしませんでしたが( "001" → 001 )、データステップによるINFILE/INPUTでの読み込みに切り替えた方が良いかもしれませんね。



② 変数Y: 先頭の半角スペース落ち問題

読み込み元の値は「 pen」と先頭に半角スペースを含んでいますが、結果は先頭の半角スペースが取れた。
PROC IMPORTに限らず、SASの色々な機能で先頭の半角スペース取れがち。



③ 変数Y: FORMATとINFORMATが付与される問題

変数属性を見ると、FORMAT(出力形式)とINFORMAT(入力形式)が付与されています。
これは以下のような場合に、勘違いを起こしやすいです。

data OUT2;
    length Y $20.;
    set OUT1;
    Y = "aaaaaaab";
run;

SET前のLENGTHステートメントでYの長さを増やしてから、Y = "aaaaaaab" としたのに、結果は「aaaaaaa」となった(末尾の "b" が落ちた?)
実はちゃんと "aaaaaaab" が格納されていますが、FORMAT「$7」が付与されているため、見た目7バイト分しか見えていないだけ(この勘違い、よく聞きます)


FORMAT/INFORMATは以下で外せます(外す処理はSET後に記述しないと動作しないので注意)

data OUT3;
    length Y $20.;
    set OUT1;
    format Y;
    informat Y;
    Y = "aaaaaaab";
run;

全変数いっぺんにFORMATを外したいところですが、変数ZはFORMAT「YYMMDD10」が付与されているので、外したくないものを避ける必要あり。


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;