2017年10月31日火曜日

現在のオプションの設定値を確認する【PROC OPTIONS】



OPTIONSプロシジャをつかうと、特定のオプションの

・設定方法
・現在の設定値

をログに表示することが出来ます。


構文

   PROC OPTIONS   OPTION=確認したいオプション名   表示内容;
   RUN;


表示内容は以下に示す通り、DEFINE, LONG, SHORT などがあります。



DEFINE … オプションの詳細な説明と現在の設定値を表示
proc options option=msglevel define;
run;




LONG … オプションの簡単な説明と現在の設定値を表示
proc options option=msglevel long;
run;



SHORT … オプションの現在の設定値を表示
proc options option=msglevel short;
run;



カッコで囲んで複数のオプションを指定することもできます。
proc options option=(msglevel center) long;
run;





2017年10月27日金曜日

テキストファイル(CSVなど)への出力【EXPORTプロシジャ編】



注意点を先に、、

  • テキストファイルへの出力に限定した説明になります
他の形式で今回のプログラムを流用すると、重要なオプションが効かなかったり、そもそも動作しない等の可能性あり

 

  • とにかく想定外の結果になりやすいので、出力したテキストファイルは要確認です
例えば、変数値にセル内改行があると、出力したテキストファイルにも改行が入っておかしな出力結果になったり、
変数値に含まれる先頭の半角スペースが出力したテキストでは削除されてしまったり、 
稀ですが、1レコードに書き込み可能な文字の長さの制限を超えると、超えた部分の変数値が出力されなかったりとか、とにかく色々な失敗例を聞きます。

   


構文

PROC EXPORT
   DATA                =     出力データセット
   OUTFILE          =     "作成するテキストファイル"    /* パスが長いと実行失敗します */
   DBMS               =     CSV | TAB | DLM   /* ファイル種類 */
   REPLACE                /* 既存のテキストファイルを上書く */
   LABEL                      /*  ヘッダーに変数名ではなく変数ラベルを適用 */
   ;
   DELIMITER      =     "区切り文字";   /*  DBMS=DLMの場合に指定 */
RUN ;


解説

① DBMS=オプション
指定設定内容
CSV  カンマ区切り 
TAB  タブ区切り
DLM自分で「DELIMITER=オプション」に区切り文字(シングルバイト1文字)を指定 



② その他の注意点
変数にフォーマットが割り当てられている場合、フォーマット変換した値がテキストファイルに出力されます。




例 ・・・ CSVファイルへ出力

proc export
   data=SASHELP.CLASS
   outfile="C:\TEST\TEST.csv"
   dbms=dlm
   replace;
   delimiter=",";
run;





2017年10月26日木曜日

変数属性を定義した空のデータセットを作成する方法【まとめ】



思いつく方法をざっとあげてみました。
(他にも方法ありそうなので、知ってたら教えてください)



例として以下の変数と属性をもつ0オブザベーションのデータセットを作成してみます。




① DataStep
data DT1;
  attrib
     VAR1  length=8.     label="Variable1"  format=yymmdd10.
     VAR2  length=$20. label="Variable2"
  ;
  call missing( of _ALL_ );
  stop;
run;



② PROC SQL
proc sql;
   create table DT2 (
      VAR1  num        "Variable1"  format=yymmdd10. ,
      VAR2  char(20)  "Variable2"
   );
quit;



③ PROC DS2 (SAS9.4~)
proc ds2;
   data DT3 (overwrite=yes);
       dcl  double     VAR1  having  label 'Variable1'  format yymmdd10. ;
       dcl  char(20)  VAR2  having  label 'Variable2';
       method run();
          stop;
       end;
   enddata;
   run;
quit;


LABELの設定はダブルクォーテーションではなく、シングルクォーテーションで囲む必要があります。



④ PROC LUA (SAS9.4~)
proc lua;
submit;
   local dsid = sas.open( "DT4", "o" )
    sas.add_vars( dsid, {{name="VAR1", type="N", label="Variable1", length="8", format="yymmdd10."},
                                    {name="VAR2", type="C",  label="Variable2", length="20"}} )
   sas.close( dsid )
endsubmit;
run;

2017年10月24日火曜日

【小ネタ】%SYSFUNC(SLEEP)




割とどうでもいいような話しを小ネタシリーズとして書いていこうと思います。



以下はSASを3秒間待機させるプログラム

data _null_;
   rc=sleep(3, 1);
   put rc "秒間待機しました";
run;



よく上のような書き方を見かけますが、これって%SYSFUNC使えば1文ですっきりとした文になっていいんじゃないでしょうか。

%put %sysfunc(sleep(3, 1)) 秒間待機しました;




でも data _null_ を使う書き方も、様式美として好きなので、好みの問題ですね。



2017年10月18日水曜日

外部ファイルをいっぺんに読み込んで連結する【FILEVAR=】



まえに、以下の記事を書きました。
外部ファイルをいっぺんに読み込んで連結する。


今回は別のやり方(INFILEステートメントの FILEVAR=オプション を使った方法)を紹介します。
まず以下のCSVファイルがあったとします。


C:\sample\TEST1.csv
001,a,11
002,b,22

C:\sample\TEST2.csv
003,c,33
004,d,44



手順1
まずは読み込みたいテキストファイルのフルパスをデータセットに格納します。

data DT1 ;
   length VAR $500. ;
   VAR="C:\sample\test1.csv"; output;
   VAR="C:\sample\test2.csv"; output;
run;
VAR 
 C:\sample\test1.csv 
 C:\sample\test2.csv



手順2
上記のテキストファイルのフルパス情報を使って、テキストファイルを読み込んでいきます。

data OUT1;

  *--- テキストファイルのフルパスが格納されたデータセットをSET ---;
  set DT1;

  *--- フルパスが格納された変数VARを filevar= に指定 ---;
  *--- end=で各テキストファイルの終端を検知する一時変数を作成 ---;
  infile dummy filevar=VAR end=EOF dsd truncover;

  *--- テキストファイルの終端を検知するまでループ ---;
  length A B $10. C 8.;

  do while (not EOF);
     input A B C;
     output;
  end;

run;

 B  
 C  
 001 
 a
 11
 002
 b 
 22
 003
 c
 33
 004
 d
 44


📝注意点

上の例で「do while (not 一時変数名)」の部分を「do until (一時変数名)」と書くと正しく読み込めない場合があります
(読み込む外部ファイルのいずれかが空だと、そこでデータステップが終了してしまい、以降の外部ファイルが読み込まれないという落とし穴がある)


📝その他ポイント

ちなみに、この記事の先頭にも貼ったリンク

の方法だと、読み込むファイルのヘッダー部分を「firstobsオプション」でスキップさせたい場合に、うまく動かないのですが、今回の記事の方法だとうまくスキップ出来ます。



2017年10月12日木曜日

「input &」で空白を含むテキストを1変数に読み込む



「INPUTステートメントで、半角スペース区切りで値を読み込む場合」の小技です。
以下の例をご覧ください。


data DT1;

length  JUICE $20.  YEN 8.;
input    JUICE &       YEN;

cards;
APPLE JUICE  120
ORANGE JUICE  130
;
run;



変数 JUICE の値が「APPLE JUICE」と「ORANGE JUICE」という感じで半角スペース(空白)を含んで読み込まれています。


ポイント

・INPUTステートメントで、変数の後に「&」を入れると、テキストに2つ連続する半角スペースが出てくるまでテキストを1つの変数に読込みます。
この機能は「&」を入れた変数のみに効力があります。


上のプログラムでは分かりづらいですが、
CARDSの中の「APPLE JUICE」と「120」の間には2つ半角スペースが入ってます。
同様に「ORANGE JUICE」と「130」の間にも2つ半角スペースが入ってます。



ちょっと前に紹介した以下の記事の方法もあわせて、状況によって使い分けましょう。
INPUTステートメントで、空白を含むテキストを1変数に読み込む方法


2017年10月10日火曜日

【SAS入門】算術演算子



SASで四則演算や累乗を計算するときの、算術演算子について簡単にまとめておきます。




算術演算子

  演算      演算子    
  加算   + 
  減算   - 
  乗算    *
  除算   /
  累乗    **

間違えやすいのが、累乗の場合。
EXCELだと「^」が累乗の意味になりますが、SASだと「**」です。





data DT1;
   A = 4;
   B = 2;

   C = A + B;    /* 加算 */
   D = A - B;     /* 減算 */
   E = A * B;    /* 乗算 */
   F = A / B;     /* 除算 */
   G = A ** B;   /* 累乗 */
run;





2017年10月5日木曜日

【CDISC】変数 [--DTC] から、ADTM, ADT, ATM への変換



CDISC関連の話。


変数「--DTC」を、ADaM変数「ADTM」「ADT」「ATM」に変換するときの、ISO8601のフォーマットと書き方を載せときます。




[--DTC] が日付のみ (時刻を含まない) の場合


※以下の例は「2017」「2017-10」等の、日付が揃っていないデータは想定していません

data DT1;

    LBDTC = "2017-10-03";

    ADT    =  input( LBDTC, e8601da. );

run;





[--DTC] が日時の場合


※以下は想定していません。
・日時が揃っていないデータ(「2017-10-03」「2017-10-03T09」等)
・秒の小数部分が含まれているデータ(「2017-10-03T09:10:12.345」等)

※以下リンクの記事に紹介してるバグにも注意。
【SAS9.3以前のBug】E8601DTインフォーマットで正しく変換できないことがある。


data DT1;

    LBDTC = "2017-10-03T09:10:00";

    ADTM =  input( LBDTC, e8601dt. );
    ADT    =  input( LBDTC, e8601da. );
    ATM    =  timepart( input( LBDTC, e8601dt. ) );

run;

・ATMだけいったん日時変換してから、TIMEPART関数で時刻部分をとりだしています。




以上、自分用のメモ記事でした。

2017年10月3日火曜日

【PROC REPORT】1つの項目に複数のCALL DEFINEを定義したい




「CALL DEFINEの重ね着って出来ないんですか?」って質問をたまにいただきます。


どういう事かと言うと、以下の例をご覧ください。

proc report data=SASHELP.CLASS nowd;
    column NAME AGE ;
    define NAME / display;
    define AGE / display;
    compute AGE;
       if AGE > 13 then call define( "name", "style", "style=[color=blue]" );
       if AGE > 14 then call define( "name", "style", "style=[background=yellow]" );
    endcomp;
run;

CALL DEFINEで、

① AGE>13の時、変数NAMEの値を青色にする
② AGE>14の時、変数NAMEの背景色を黄色にする

という事をしてます。

よくみると、Janetさんは値を青にした上で、背景色を黄色にしたかったんですが、青くなってません。


CALL DEFINEで定義したSTYLEが、1つのセルに対して1つしか適用されていないようです。
STYLEの適用順は状況によって異なりますが、今回は「①値を青色」→「②背景色を黄色」の順で適用されているようですね。



そこで、小技。
以下のように「STYLE/MERGE」とすれば、複数のCALL DEFINEによるSTYLEを、上書きじゃなくてMERGE(結合)してくれます。

proc report data=SASHELP.CLASS nowd;
    column NAME AGE ;
    define NAME / display;
    define AGE / display;
    compute AGE;
       if AGE > 13 then call define( "name", "style", "style=[color=blue]" );
       if AGE > 14 then call define( "name", "style/merge", "style=[background=yellow]" );
    endcomp;
run;

ちゃんとJanetさんが青くなってますね。


ちなみに、
本題からズレますが、CALL DEFINEを書くために利用しているCOMPUTEステートメントは高頻度でうまく設定できないケースを見かけるので、以下記事と以下記事内にリンクしている記事も要確認です。




📝その他、混乱しそうなポイント

以下みたいに一方が「_ROW_」だと、「STYLE/MERGE」としなくても1つのセルに複数のSTYLEが設定できました。

proc report data=SASHELP.CLASS nowd;
    column NAME AGE ;
    define NAME / display;
    define AGE / display;
    compute AGE;
       if AGE = 14 then call define( "name", "style", "style=[color=blue]" );
       if AGE = 14 then call define( _row_, "style", "style=[background=yellow]" );
    endcomp;
run;


ただ、以下の場合ちょっと混乱しそう。

proc report data=SASHELP.CLASS nowd;
    column NAME AGE ;
    define NAME / display;
    define AGE / display;
    compute AGE;
       if AGE = 14 then call define( "name", "style", "style=[color=blue]" );
       if AGE = 14 then call define( _row_, "style", "style=[color=red]" );
    endcomp;
run;


最後「_ROW_」で行の全変数の文字色を赤にしたはずなのに、NAMEの文字色が青になってる?
プログラムの記述順で、STYLEが適用されると思いがちですが、違うっぽい。
2番目の以下「_ROW_」のSTYLEが先に実行されて、、

 if AGE = 14 then call define( _row_, "style", "style=[color=red]" );


次に1番目の以下STYLEが適用されたような挙動です。

 if AGE = 14 then call define( "name", "style", "style=[color=blue]" );



本題から逸れましたが、こんな感じで動きが独特なのでご注意ください。