2014年12月26日金曜日

今年も1年有難うございました&締めのPROC EXPLODE


今年もあとちょっとですね。
ブログを見てくださった方や、コメントくださった方、皆さん本当に有難うございました!

まだ今年も更新するかもしれませんが、来年もよろしくお願いします!

とりあえず、一度使ってみたかったEXPLODEプロシジャで締めたいと思います。

filename ft15f001 temp;

proc explode;
parmcards;
1 HAVE A GREAT
2 NEW YEAR!!
;
run;


。。。SAS Studioだと、結果がでないので、
ログ・アウトプットを外部ファイルに出力する。で紹介したPRINTTOプロシジャで、出力結果をテキストファイルとして保存し、SAS Studio上でそのテキストファイルを開くと、見れると思います。

2014年12月24日水曜日

「SAS Studio」でインデントなどを自動設定する便利機能



今回紹介する機能や画面構成などは、環境やバージョンによって変更される可能性があります。


   まず、適当にプログラムを書いて、、、



「コードのフォーマット」を押すと、




インデントが入りました!


インデントに限らず、プログラムの可読性を高めるようなコードの自動フォーマットを行ってくれるようです。


2014年12月19日金曜日

EXCELを読み込む方法と落とし穴 【DDE編】




Windows環境でDDEを使ってEXCELファイルを読み込む方法。
Unicode版だと日本語が文字化けして、うまく読み込めないようなのでご注意下さい。

ちなみに、色々な落とし穴があるので最後までご覧ください。




以下のEXCELファイルを読み込みたいとします。



C:\test.xlsx





EXCELファイルをDDEで読み込む例

* EXCELを起動 ;
options  noxwait noxsync;
%sysexec  "C:\TEST.xlsx";


* 5秒待機 ;
data _NULL_;
  rc = sleep(5,1);
run;


* EXCELデータを読み込む ;
filename EXC dde "Excel|[test.xlsx]Sheet1!C1:C4";

data OUT1;
  attrib
      A length=8.
      B length=$20.
      C length=8.   informat=yymmdd10.  format=yymmdds10.
      D length=8.   informat=time5.         format=time5.
  ;
  /* filenameで読み込む行のみを設定した場合、firstobsオプションは不要 */
  infile EXC notab dlm="09"x dsd missover lrecl=50000 firstobs=2;
  input A B C D;
run;

* ファイル参照を解放 ;
filename EXC clear;


OUT1
 
B 
 C 
 D 
   1
 1
                 .
           .
   2
 1
 2012/01/10 
 10:20 
   3
 1
                 .
           .
   4
 1
                 .
           .
   5
  
                 .
           .
   6
  
 2012/10/20
           .
   7
 1 
                 .
           .
   8
 1 
                 .
           .
   9
 aaa 
                 .
           .




解説

options noxwait noxsync;
%sysexec "C:\TEST.xlsx";

・OSのコマンドの力を借りて、EXCELファイルを開きます。
%sysexec "開きたいファイルのフルパス";

・コマンド発行時にSASが一時停止しないよう、noxwait noxsyncオプションを設定しておく。


data _NULL_;
  rc = sleep(5,1);
run;

・EXCELが開くまで後続のSASプログラムが実行されないよう「 sleep(秒数, 1) 」でSASを5秒スリープさせる。重いファイルで起動が遅い場合は、秒数を長く設定しましょう。

(この場合、sleep入れなくてもEXCELが開くまでSASプログラムの実行止めてくれるんでしたっけ?書かなくていい場合と、書かなきゃいけない場合があって、よく分からないのでひとまずsleep入れておきます)


filename EXC dde "Excel|[test.xlsx]Sheet1!C1:C4";

・読み込むEXCELの範囲をファイル参照名で定義しておく。
filename ファイル参照名  dde "Excel|[ファイル名]シート名!読み込むセル範囲"; 

今回は1~4列目を読み込む範囲に設定してます。


data OUT1;
  attrib
      A length=8.
      B length=$20.
      C length=8.   informat=yymmdd10.  format=yymmdds10.
      D length=8.   informat=time5.         format=time5.
  ;
  /* filenameで読み込む行のみを設定した場合、firstobsオプションは不要 */
  infile EXC notab dlm="09"x dsd missover lrecl=50000 firstobs=2;
  input A B C D;
run;

・attribで、読み込む変数の属性を定義しておき、infileとinputで、EXCELデータを読み込んでいきます。

「 lrecl=50000 」で、1行に読み込む長さを50000バイトに増やしてます。
この長さが足りないと、途中で文字が切れてしまう事があるので。。
ここは読み込むデータの長さに応じて、設定して下さい。

・また、今回はEXCELの1~4列目を読み込む範囲に設定しましたが、読み込みを開始したい行は、2行目からなので、「 firstobs=2 」としてます。

「 filename EXC dde "Excel|[test.xlsx]Sheet1!R2C1:R10C4"; 」というように、filenameで読み込む行も設定してる場合、firstobs は不要です。




落とし穴


① EXCELのセル内改行には注意。

たとえば1セルに
「aaa
  aaa」
と入力して、SASデータセット化して開いてみると、「aaaaaa」と入ってるように見えます。
改行きえた?と思うかもしれないけど、実は目に見えない改行が入ってます。
この見た目と中身が異なるデータを持つのは、後々面倒を起こしそうです。

対策として、EXCEL上でセル内改行を全て削除しておくか、
SASYAMAさんが紹介している 「目に見えない改行コードが邪魔をしてくる話」を参考に、SAS上で改行コードを削除する方法があります。
(データに日本語等のマルチバイト文字を含む場合、マルチバイト非対応のCOMPRESS関数ではなく、KCOMPRESS関数を利用して下さい)



② EXCELに表示されてる”見た目”をそのまま読み込んでしまうという罠。

どういう事かというと、

EXCELで長い文字を入力して、セルの書式を「文字列」に設定すると、、


以下の通り、「########…」となってしまいます。(EXCELのバージョンとかによっては、この問題は起こらない)












EXCELの仕様っぽいですが、この状態で今回のプログラムを実行すると、SASデータセットにも「#######…」という文字が入ってしまいます。
対策としては、該当セルの書式を「標準」にしてあげれば、直ります。

また、EXCEL上でフィルタや非表示の設定をしている状態でプログラムを実行すると、非表示になってる部分は読み込んでくれません。


③ 想定した通りの行を読み込めているか確認した方が良さそう。

例えば、今回の例では読み込む範囲を「Sheet1!C1:C4」として、EXCELの1~4列の範囲にあるデータを読み込むよう指定していますが、このような指定方法では、どこの行まで読み込むかを明示的に指定していないため、データが入っている範囲外の空白行を読み込んでしまうことがあった。

その他、EXCELの仕様など、不確定要素があるので、最低限、最初と最後の行が想定通りに読み込めているか確認した方が良さそうです。


2014年12月17日水曜日

レポート作成インターフェイス(RWI)入門7


RWIで画像ファイルを配置するメソッドを紹介。

  IMAGE( file: "画像ファイルのフルパス)



簡単な例ですが、スマホで撮った写真を配置してみます。
(以降、HTMLへの出力が有効になっている前提)

data _NULL_;

  dcl odsout ob();
  ob.image( file: "画像ファイルのパス\flower.jpg" );

run;


結果



実務では、SASで作ったグラフを、画像ファイルとして保存しておき、それを配置するってことも出来ます。


ちなみに、写真の花は堀切菖蒲園というところで撮ったやつです。
花びらがクシャッと垂れた感じがかわいいですね。



2014年12月15日月曜日

日付値と時間値から、日時値を作る小技。





DHMS関数で日時値を簡単に作ることが出来ます。



まずは 日付値、時、分、秒 から日時値を作る方法。


構文
DHMS日付値,  ,  ,   )



data DT1;
   format DT yymmdd10. H M S 8. DTM e8601dt19.;

   * 日付値 ;
   DT  = input("2014/01/10", yymmdd10.);

   * 時 ;
   H   = 20;

   * 分 ;
   M   = 10;

   * 秒 ;
   S   = 00;

   * 日時値を作成 ;
   DTM = dhms(DT, H, M, S);
run;


 DT 
S
DTM 
  2014-01-10  
  20  
  10  
  0  
  2014-01-10T20:10:00  



マニュアルにも載ってる小技ですが
日付値と時間値から、日時値を作る事も出来ます。


構文
DHMS日付値,  0,  0,  時間値 )




data DT1;
   format DT yymmdd10.  TM time5.  DTM e8601dt19.;

   * 日付値 ;
   DT  = input("2014/01/10", yymmdd10.);

   * 時間値 ;
   TM  = input("20:10", time5.);

   * 日時値を作成 ;
   DTM = dhms(DT, 0, 0, TM);
run;


 DT 
TM 
DTM 
  2014-01-10  
  20:10  
  2014-01-10T20:10:00  


2014年12月12日金曜日

レポート作成インターフェイス(RWI)入門6


RWIで改ページを設定するメソッド。

  PAGE()



プログラムと結果を見てみましょう。
(以降、HTMLへの出力が有効になっている前提)

data _NULL_;
  dcl odsout ob();

     *** 1ページ目 ;
     ob.table_start();
         ob.row_start();
            ob.format_cell( data:"aa" );
         ob.row_end();
     ob.table_end();

     ob.page();

     *** 2ページ目 ;
     ob.table_start();
         ob.row_start();
            ob.format_cell( data:"bb" );
         ob.row_end();
     ob.table_end();
run;

結果



レポートを作るうえで大事なメソッドです。

2014年12月10日水曜日

ATTRIBとKEEPを使った基本コンボ


大した内容じゃないですが、基本コンボを紹介。
まずATTRIBステートメントの性質から。

LABEL, LENGTH などの変数属性を、まとめて定義できるステートメント。
データステップの一番先頭に書くと、ATTRIBに指定した順番で変数が並んでくれる。



よくデータセット作成のシメとして、きちんと変数属性を定義する目的で使ってます。
例を見てみましょう。

*** 適当なサンプルデータ作成 ;
data DT1;
  retain A B C D E F 1 G "aa" H '20:00't  ;
run;

 C 
 D  
 E  
  1  
  1  
  1  
  1  
  1  
  1  
  aa  
 72000  


*** 要る変数だけ属性定義してKEEPする例 ;
data OUT1;
    attrib
       A label="あ" length=8.
       B label="い" length=8.
       C label="う" length=8.
       G label="え" length=$10.
       H label="お" length=8.   format=time5.
    ;
    set DT1;

    keep A B C G H;
run;

 C 
  1  
  1  
  1  
  aa  
 20:00 



解説
ATTRIBで変数属性を定義し、これら変数をKEEPで残しています。

ここで、ひとつ無駄がある。
KEEPする前の状態として、変数はATTRIBで指定した順番に並び、ATTRIBで指定されてないものはそのうしろに並びます。
(順番・・・A B C G H  D E F)

なので、「変数名をいっきに指定する方法 [まとめ]」のテクニックを利用してKEEPの部分を

keep A -- H ;


と省略して書いた方が楽です。

ちなみにKEEPあるあるで今回の話しに限ったことではないですが、後から保持する変数が追加になったとき、KEEPにその変数を書き忘れがちっていう、SASプログラマーあるあるにご留意を。



今回の例では変数の数が少ないので、必要性を感じづらいかもしれないですが、
実務では沢山の変数を扱うことになるので、その便利さがわかると思います。

2014年12月8日月曜日

レポート作成インターフェイス(RWI)入門5



RWIで、複数の表を好きな位置に配置する方法を紹介。

ods html close;
ods pdf file="出力するファイルのパス\test.pdf";

data _NULL_;
  dcl odsout ob();

  * レイアウト設定開始 ;
  ob.layout_absolute();

     * 1個目の表を配置 ;
     ob.region( x:'1cm', y:'1cm' );
     ob.table_start();
         ob.row_start();
            ob.format_cell(data:"aa");
            ob.format_cell(data:"bb");
         ob.row_end();
     ob.table_end();

     * 2個目の表を配置 ;
     ob.region( x:'5cm', y:'2cm' );
     ob.table_start();
         ob.row_start();
            ob.format_cell(data:"cc");
           ob.format_cell(data:"dd");
         ob.row_end();
     ob.table_end();

  ob.layout_end();
run;

ods pdf close;
ods html;


結果




解説


今回使うlayout_absoluteメソッドは、マニュアルにPRINTER出力のみ対応してるよと書いてあります。
なので、HTML出力を閉じておき、PDFファイルに出力します。

  ods html close;
  ods pdf file="出力するPDFファイルのフルパス";


好きな位置に配置するためのレイアウトを開始。

  layout_absolute()


表を配置する前にregionメソッドで領域の出力位置を設定。
ただ、いまいち分からないのが、例えば位置を5cmと設定して、出力されたpdfを倍率100%にして見ても5cmのところに配置されてるわけでもなさそう?。。どういう基準で配置されてるのかよく分からないです。

  region( x : 'xの位置' ,  y : 'yの位置' )


レイアウト設定を終了させます。

  layout_end()


PDF出力を閉じて出力完了。あと最後にデフォルトの出力先をHTMLにしてます。

  ods pdf close;
  ods html;


イメージとしては、スクラップブックのように、いろんな表やグラフを好きな位置にペタペタ貼る感じです。


また別の記事で、応用例(遊び)を紹介したいと思います。



RWI入門記事一覧
1.基本構文
2.データセットの中身を出力する
4.複数の表を並べる
5.複数の表を好きな位置に配置する

2014年12月5日金曜日

FREQプロシジャのLISTオプションで、変数値同士の組み合わせを見易くする。




データの組み合わせなどを確認する際に役立つ、ちょこっとしたオプションを紹介。

*** Sample Data ;
data DT1;
input CODE$ NAME$;
cards;
001 AAA
004 DDD
003 BBB
001 AAA
004 DDD
002 CCC
002 CCC
;

 CODE 
NAME
  001  
  AAA  
  004
  DDD  
  003
  BBB  
  001
  AAA  
  004
  DDD  
  002
  CCC  
  002
  CCC  


たとえば、上のサンプルで「CODEとNAMEの値の組み合わせを調べたい」ってなったとします。


そこでまずはFREQを使ってみます。

proc freq data=DT1;
   tables CODE * NAME / missing;
   format _all_;
run;




これでもいいけど、もうちょっと変数値同士の組み合わせを見やすくしたいので、、

proc freq data=DT1;
   tables CODE * NAME / missing list;
   format _all_;
run;


「LISTオプション」を追加するとリスト形式になって、確認しやすくなりました。


ちなみに今回はFREQプロシジャに以下を設定してますが、この辺は状況に合わせて設定してください。
  • 変数に欠損値があった場合、欠損値も水準に含めるため「missingオプション」を設定(組み合わせを見たい時って、欠損値も含めたいことが多いので)
  • formatをあてた値の組み合わせではなく、変数値の組み合わせを見たい場合「format _all_」で全変数にあてられたformatを解除。


📝 1つ気をつけたいのが、、

例えば、以下の例

data DT2;
   X="   AAA"; output;
   X="BBB"; output;
run;

proc freq data=DT2;
   tables X / missing list;
   format _all_;
run;



データセットDT2の1行目の変数Xの値は「   AAA」って感じで先頭に半角スペースが含まれていますが、
FREQの出力結果を見ると、この半角スペースが削除されて表示されていますよね。

プロシジャの結果がHTML形式等で表示される際、先頭の半角スペースを除く仕様みたいです。

こんな感じで、格納値の通りに表示されない場合があるので、
データクリーニングとか、ちゃんと格納値を確認したい場合は、今回の機能だけじゃなくて、FREQをかける前の元データを目視確認したり、改行・文字化け・余計なスペース等のおかしなデータを検出するチェックプログラムを作ったり、別途確認が必要です。



てことで、あんまり使わなそうな機能でも、覚えておくと今回のようにちょっとした事で役立つという話でした。
SASに無駄な機能なしです。



2014年12月4日木曜日

日時値から、日付値または時間値を抽出する



わりとこの質問を沢山もらうので、紹介したいと思います。


日時値に対して、DATEPART関数で日付値を抽出し、TIMEPART関数で時間値を抽出します。

data DT1;
   format DT yymmdd10. TM time5.;

   * 適当な日時値を作成 ;
   DTM  = input("2014/07/01 10:20:00", ymddttm.);

   * 日付値を抽出 ;
   DT   = datepart( DTM ) ;

   * 時間値を抽出 ;
   TM   = timepart( DTM ) ;

   keep DT TM;
run;


 DT 
TM 
  2014-07-01  
  10:20  


2014年12月2日火曜日

SQLプロシジャ入門13:データセットを横結合する【FULL JOIN】




横結合時に両方のデータセットの全てのレコードを残す方法を紹介。




サンプルデータ
data DT1;
  A=1; B="AA"; output;
  A=2; B="BB"; output;
run;

data DT2;
  A=2; C=10; output;
  A=3; C=20; output;
run;

DT1
 A 
B
  1  
  AA   
  2
  BB  

DT2
 A 
C
  2  
  10   
  3
  20  





FULL JOIN
proc sql;
   create table  DT3 as
   select    coalesce( DT1.A, DT2.A ) as A ,
                B ,
                C
   from      DT1  full join  DT2  on  DT1.A = DT2.A ;
quit;

  A  
 B 
  C  
  1
  AA 
 .
  2
  BB 
 10
  3
   
 20


基本構文
  from  データセット1  full join  データセット2  on  結合条件


解説
結合時、2つのデータセットの全てのレコードを残します。
 (結合条件に合うレコードだけでなく、片方のデータセットにしかないレコードも残す)


② 結合する2つのデータセットで同じ変数名を持っている場合、「データセット名.変数名」と書きます。
(今回の例では、DT1とDT2で同じ変数名Aを持っているので、どっちのAを使うのか明確にするため、「DT1.A」とか「DT2.A」と書いています)


③ SELECTでcoalesceという関数を使ってます。
この関数は、指定した引数の値を順番に見ていって、最初の非欠損値の値を返します。

つまりSELECTの「coalesce( DT1.A, DT2.A )」は、
・DT1にしか存在しないレコードだったら、DT1.Aの値を持ってくる
・DT2にしか存在しないレコードだったら、DT2.Aの値を持ってくる

という事をやっています。






13.データセットを横結合する【FULL JOIN】

2014年12月1日月曜日

変数名などの定義情報を比較する方法



以下に、似てるようで似てない2つのデータセットがあります。

data DT1;
  length A $20. B C 8.;
  A="xxx";
  B=1;
  C=2;
run;

 A  
  B   
  C  
 xxx 
  1
  2

data DT2;
  length A C $10. D 8.;
  A="yyy";
  C="zzz";
  D=3;
run;

  A  
  C   
  D   
 yyy
 zzz
  3



この2つの変数定義を比較したい場合、以下の例のようにCOMPAREプロシジャを使うと楽です。

注意:
COMPAREプロシジャには変数の順番を比較する機能はないようです (例えば片方のデータセットでは変数A, B, C の順、もう片方では変数A, C, B の順で定義されてても、定義不一致として検出してくれません)


proc compare  base=DT1(obs=0)  compare=DT2(obs=0) listvar;
run ;


結果の一部




解説


  • listvar を指定すると、一方にしかない変数名をリストアップしてくれます。
  • それと、今回比較するのは変数定義だけで、変数値の比較結果は必要ありません。なので、(obs=0) をつけてオブザベーションを読み込まないようにします。
  • ログに「同等なオブザベーションが見つかりません。」みたいなWARNINGが出てしまうけど、オブザベーションを読み込んでないから出るメッセージです。



もし厳密にWARNINGを出したくない場合は、以下のようにかけばok

proc compare  base=DT1  compare=DT2  listvar  novalues ;
run ;



novaluesで変数値の比較結果を出力しないようにします。
(といっても裏では変数値の比較をしてるので、重たいデータセットだと実行がモタつくかも)


2014年11月28日金曜日

LIBNAMEを使わずに永久データセットを取り扱う小技



LIBMAMEを使わずに、永久データセットを扱う方法があります。


以下は パス「C:\TEST」 の中にDT1というデータセットがあったとして、それをWORKにSETする例です。

libname MYLIB "C:\TEST";

data DT1;
   set MYLIB.DT1;
run;

これが普通の書き方ですが、
実は以下のように、フルパスを指定することも出来ちゃいます。

data DT1;
   set "C:\TEST\DT1";
run;


一時的な作業であれば素早く書けて便利です。
がっつりプログラムを書く場合は、libnameでパスを通す基本的な書き方のほうが、プログラムとしてはキレイなのでそっちを使うほうがいいかと思います。



2014年11月25日火曜日

WHEREステートメントのLIKE演算子でエスケープ文字を設定する。



まずWHEREステートメントのLIKE演算子について、

データステップ100万回「whereステートメントでのみ使用できる演算子_contains,between,like
で分かり易い説明があるのでご覧ください。



そして、今回紹介するエスケープ文字の設定について。

Sample data
data DT1;
   VAR = '100%'; output;
   VAR = '100'; output;
run;

VAR
  100% 
  100


例えば上のデータから、末尾に「%」という文字が含まれるやつを抽出したい場合、

data DT2;
   set DT1;
   where VAR like '%#%' escape '#';
run;

VAR
  100% 

というように書けます。

escape '#'」で、「#」という文字をエスケープ文字として設定し、
この「#」の次に書かれた「%」をワイルドカードではなく、単なる文字としての「%」に変えています。


エスケープ文字は、「$」とか「\」とか自分で適当に設定できます。

ちなみに、
本題とそれるけど例えば「where VAR like "%abc"」みたいに、検索文字列をダブルクォーテーションで囲むと、検索文字列中の「%abc」がマクロとして解釈されてしまう。
マクロとして解釈されたくない場合は、検索文字列をシングルクォーテーションで囲むこと。



2014年11月21日金曜日

ディレクトリ内のファイル名とフォルダ名を取得する(DOPEN関数編)



たとえば、「C:\TEST」からファイル名とフォルダ名を取得したいとします。



filename DIR1 "C:\TEST";

data DT1 ;
   length  VAR  $400 ;

   *** ディレクトリを開く ;
   did = dopen("DIR1") ;

   *** ファイル・フォルダ名を変数に格納 ;
   do i = 1 to dnum( did ) ;
       VAR = dread( did , i ) ;
       output ;
   end;

   rc = dclose( did ) ;
run;

 VAR  
  aaa 
  bbb.txt 
  ccc.xlsx  



解説

① DOPEN関数でディレクトリを開きます。

構文 … DOPEN( "開きたいディレクトリが割り当てられたファイル参照名" )

戻り値として開いたディレクトリの識別番号が返されるので、その番号を変数didに入れてます。



② DNUM関数でディレクトリ内のメンバ数を取得できます。

構文 … DNUM( ディレクトリの識別番号 )



③ DREAD関数でディレクトリ内のメンバ名を取得できます。

構文 … DREAD( ディレクトリの識別番号 , メンバの通し番号 )

つまりファイル・フォルダの数だけDOループをまわして、ディレクトリ内の全ファイル・フォルダ名を取得しています。



④ DCLOSE関数でディレクトリを閉じます。

構文 … DCLOSE( ディレクトリの識別番号 )




2014年11月19日水曜日

SQLプロシジャ入門12:データセットを横結合する【LEFT,RIGHT JOIN】




今回は、横結合時に一方のデータセットに存在するレコードだけを残す方法を紹介。
とりあえずサンプルを見てみましょう。




サンプルデータ
data DT1;
  A=1; B="AA"; output;
  A=2; B="BB"; output;
run;

data DT2;
  A=2; C=10; output;
  A=3; C=20; output;
run;

DT1
 A 
B
  1  
  AA   
  2
  BB  

DT2
 A 
C
  2  
  10   
  3
  20  




①LEFT JOIN
proc sql;
   create table  DT3 as
   select    DT1.A , B , C
   from      DT1  left join  DT2  on  DT1.A = DT2.A ;
quit;


データセットDT3
  A  
 B 
  C  
  1
  AA 
 .
  2
  BB 
 10


基本構文
 from データセット1  left join データセット2  on  結合条件


解説
・FROMで結合する時、左側に書いたデータセットDT1のレコードだけ残す。
・DT1とDT2で同じ変数名Aを持ってるので、どっちのAを使うのか明確にするため、「DT1.A」とか「DT2.A」と書いてあげる必要がある。




②RIGHT JOIN
proc sql;
   create table  DT4 as
   select    DT2.A , B , C
   from      DT1  right join  DT2  on  DT1.A = DT2.A ;
quit;


データセットDT4
  A  
 B 
  C  
  2
  BB 
 10
  3
   
 20


基本構文
 from  データセット1  right join データセット2  on  結合条件


解説
・FROMで結合する時、右側に書いたデータセットDT2のレコードだけ残す。




12.データセットを横結合する【LEFT,RIGHT JOIN】


2014年11月17日月曜日

レポート作成インターフェイス(RWI)入門4


RWIで複数の表を並べる方法を紹介します。
まずは以下のプログラムを実行して結果を見てみましょう。

(以降、HTMLへの出力が有効になっている前提)

data _NULL_;
   dcl odsout ob();

   * グリッドを設定する ;
   ob.layout_gridded( columns:2, rows:2 );

       * 1個目の表を配置 ;
       ob.region();
       ob.table_start();
          ob.row_start();
               ob.format_cell(data:"aa");
               ob.format_cell(data:"bb");
          ob.row_end();
       ob.table_end();

       * 2個目の表を配置 ;
       ob.region();
       ob.table_start();
          ob.row_start();
               ob.format_cell(data:"cc");
               ob.format_cell(data:"dd");
          ob.row_end();
       ob.table_end();

       * 3個目の表を配置 ;
       ob.region();
       ob.table_start();
          ob.row_start();
               ob.format_cell(data:"ee");
               ob.format_cell(data:"ff");
          ob.row_end();
       ob.table_end();

   ob.layout_end();
 run;

結果



解説


まずは、表を配置する列数と行数を設定。
  layout_gridded( columns:列数 , rows:行数  )


そしたら、表を配置する前に以下のメソッドを書くだけです。
  region()


最後にレイアウト設定を終了させます。
  layout_end()



例えば列2×行2のレイアウトを作って表を配置するイメージはこんな感じ。














regionメソッドを書く度、左から右、次の行の左から右に、、、と表をどんどん配置していきます。


ちなみに。。

長くなるので紹介は出来ませんでしたが、imageメソッドというのがあり、表だけじゃなくグラフなどのイメージも配置できちゃうので、色々遊べそうです。

2014年11月14日金曜日

引用符で囲んだ文字が長すぎる場合のWARNINGを出さないようにする方法


まずは以下のプログラムを実行してみてください。


data DT1;
A = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab";
run;

ログ
WARNING: 引用符で囲んだ文字列が 262 文字を超えているか、または引用符が足りません。


WARNINGが出てしまいました。
クオーテーションで囲った文字が長すぎる場合、「長すぎるよ、まさかクオーテーションで閉じるの忘れてない?」みたいなSASの親切心でWARNINGをだしてくれます。

このメッセージを表示しないようにするにはNOQUOTELENMAXオプションを設定します。
「NOQUOTELENMAX」でメッセージを抑制し、
「QUOTELENMAX」でメッセージを表示するように戻します。



options noquotelenmax;

data DT1;
A = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab";
run;

options quotelenmax;


上記のように、このオプションは使ったらすぐに設定を戻すようにしましょう。
本当にクオーテーションの閉じ忘れがあった時、気づきにくくなるので。

2014年11月13日木曜日

SQLプロシジャ入門11:データセットを横結合する【INNER JOIN】




SQLプロシジャで横結合する方法を紹介していきます。
まずは、2つのデータセットを横結合して、指定した結合条件に合致するレコードのみを残す方法。




サンプルデータ
data DT1;
  A=1; B="AA"; output;
  A=2; B="BB"; output;
run;

data DT2;
  A=2; C=10; output;
  A=3; C=20; output;
run;

DT1
 A 
B
  1  
  AA   
  2
  BB  

DT2
 A 
C
  2  
  10   
  3
  20  



方法1
proc sql;
   create table  DT3 as
   select    DT1.A , B , C
   from      DT1, DT2
   where    DT1.A = DT2.A ;
quit;

データセットDT3
  A  
 B 
  C  
  2
  BB 
 10

基本構文
 from  データセット1 , データセット2
 where  結合条件


解説
・WHEREによって、変数Aの値が共通するレコードだけを残すようにしてます。
・DT1とDT2で同じ変数名Aを持ってるので、どっちのAを使うのか明確にするため、「DT1.A」とか「DT2.A」と書いてあげる必要がある。




方法2
proc sql;
   create table  DT4 as
   select    DT1.A , B ,  C
   from      DT1  inner join  DT2  on  DT1.A = DT2.A;
quit;


基本構文
  from  データセット1  inner join  データセット2   on  結合条件


解説
結果は方法1と同様。




11.データセットを横結合する【INNER JOIN】
13.データセットを横結合する【FULL JOIN】
14.データセットを縦結合する【UNION】
15.NULLの扱いに関する注意点


2014年11月12日水曜日

外部ファイルやフォルダが存在するか確認する方法




ファイルやフォルダの存在確認をするには、FILEEXIST関数を使うと便利です。

ファイルやフォルダに対してアクセス制限など、なんらかの制限をかけている場合、
FILEEXIST関数がどのような挙動をするのかは未確認です。
そのようなケースが想定される場合は、リファレンスの確認や動作確認を行ってください。



構文
リターンコードを格納する変数 = FILEEXIST( "確認したいフォルダまたはファイルのフルパス" );

  • 存在する場合は「1」、存在しない場合は「0」がリターンコードとして返されます。
  • 存在するか、しないかなどの情報はログに表示してくれません。
    • その代わり、存在しなかった場合の警告や処理のエラーをSYSMSG関数で取得できる。


data _null_;
        length msg $2000.;

        rc = fileexist("D:\myfolder\test.txt");
        if rc^=1 then do;
           msg=sysmsg();
           put msg;
        end;   
run;



2014年11月9日日曜日

%SYSFUNC関数の小技


ちょっとした小技を紹介。
(%SYSFUNCって何って方は「データステップ外で関数を使えるようにする「%SYSFUNC」その1」を参照)

たとえば、DATE関数を使って、ログに日付を表示したいとします。
そこで以下のように書いたとします。

%put   %sysfunc(date());

 ログ
 19782


ログには「19782」と日付値が表示されてしまうので、yymmdd10のFORMATをあてる必要があります。

%put   %sysfunc(putn( %sysfunc(date()) , yymmdd10));

 ログ
 2014-02-28


今度は正しく出ました。
けど、実はもっとスマートに書くことができて、、
%SYSFUNC関数の第2引数に、あてたいFORMATを指定することが出来ます

%put %sysfunc(date(), yymmdd10);

 ログ
 2014-02-28


この隠れ第2引数の存在はあまり知られてないと思います。