2015年5月28日木曜日

複数のテキストファイルを個別に読み込む時に便利なINFILEステートメントの書き方。


「C:\SAMPLE」 というパスに、以下のようなCSVファイルがいくつかあったとします。

A1.csv
11,22,33
44,55,66

A2.csv
aa,bb,cc
dd,ee,ff

これらを個別に読み込んでデータセット化したい場合、
filenameステートメントを以下青文字のように書きがちですが、、

filename TEST1 "C:\SAMPLE\A1.csv";
data DT1;
  infile TEST1 dsd truncover;
  input a b c;
run;

filename TEST2 "C:\SAMPLE\A2.csv";
data DT2;
  length d e f $20.;
  infile TEST2 dsd truncover;
  input d e f;
run;

↓↓
filenameステートメントでは、フォルダのパスまでを指定しておき、
infileステートメントで 「ファイル参照名 (ファイル名)」 という書き方でいけます。

filename TEST "C:\SAMPLE";

data DT1;
  infile TEST ("A1.csv") dsd truncover;
  input a b c;
run;

data DT2;
  length d e f $20.;
  infile TEST ("A2.csv") dsd truncover;
  input d e f;
run;

こちらの方が、すっきりとして見やすくいい感じです。

2015年5月27日水曜日

NLDATM関数で、日時値を好きな書式で文字変換する。


以下の2つとセットの記事です。
NLDATE関数で、日付値を好きな書式で文字変換する。
NLTIME関数で、時間値を好きな書式で文字変換する。


NLDATM関数を使うと、
日時値に対して、好みの区切り文字を設定して、文字に変換できます。

構文
 NLDATM( 対象変数, 'ディスクリプタによる変換方法を指定' )

ディスクリプタ
 %Y  …  4桁の年
 %y  …  2桁の年
 %m  …  月
 %d  …  日

 %H …  時間(24時間表記)
 %I   … 時間(12時間表記)
 %M …  分
 %S …  秒

・ディスクリプタは大文字・小文字の違いに注意して下さい。
(例えば「%Y」と「%y」)
・第2引数はシングルクォーテーションで囲む(ダブルクォーテーションはNG)



data DT1;

  length VAR2 $50.;
  VAR = input('2014/01/04 15:20:00', ymddttm.);

  * 「2014年01月04日 15時20分00秒」 という文字に変換;
  VAR2 = nldatm( VAR , '%Y年%m月%d日 %H時%M分%S秒' ) ;
run;



上の例では月や日、秒などの頭に0がついてます。
0を取りたい場合、以下のように「%」の後に「#」を入れてやります。

data DT2;
  length VAR3 $50.;
  set DT1;

  * 「2014年1月4日 15時20分0秒」 という文字に変換;
  VAR3 = nldatm( VAR , '%Y年%#m月%#d日 %#H時%#M分%#S秒' ) ;
run;


NLTIME関数で、時間値を好きな書式で文字変換する。


以下の2つとセットの記事です。
NLDATE関数で、日付値を好きな書式で文字変換する。
NLDATM関数で、日時値を好きな書式で文字変換する。


NLTIME関数を使うと、
時間値に対して、好みの区切り文字を設定して、文字に変換できます。

構文
 NLTIME( 対象変数, 'ディスクリプタによる変換方法を指定' )

ディスクリプタ
 %H …  時間(24時間表記)
 %I   … 時間(12時間表記)
 %M …  分
 %S …  秒

・ディスクリプタは大文字・小文字の違いに注意して下さい。
(例えば「%M」を「%m」と記述すると無効な引数として処理が失敗してしまいます)
・第2引数はシングルクォーテーションで囲む(ダブルクォーテーションはNG)


data DT1;
  length A1 - A3 $50.;

  A = '23:05't;
  A1 = nltime(A, '%H時%M分');          /* 23時05分 */
  A2 = nltime(A, '%H時%M分%S秒');  /* 23時05分00秒 */
  A3 = nltime(A, '%I時%M分');           /* 11時05分 */
run;



「23時05分00秒」というように分や秒の頭に0が入ってるので、
これを取りたい場合、「%」の後に「#」を入れてやります。

data DT2;
  length A4 $50.;
  set DT1;

  * 「23時5分0秒」 という文字に変換;
  A4 = nldatm( A , '%#H時%#M%#S秒' ) ;
run;

2015年5月26日火曜日

「PROC CATALOG」でフォーマット名を取得する方法と、「PROC FORMAT」でコードの中身を取得する方法


「フォーマット名を取得したい」 とか 「ある特定のフォーマットにどんなコードがあるのか見てみたいんだけど」 という質問を度々頂くので、解説したいと思います。

* サンプルフォーマット ;
proc format;
  value A_
    1="aaa"
    2="bbb"
  ;
  value $B_
    "xx"="aaa"
    "yy"="bbb"
  ;
  invalue C_
    "ccc"=1
    "ddd"=2
  ;
run;



フォーマット名を取得したい

サンプルで作成したフォーマットカタログから「フォーマット名」を出力する例。
proc catalog cat=work.formats;
   contents;
run;
quit;



青字部分は「cat=ライブラリ名.フォーマットカタログ名」という感じで適宜変えてください。

データセット化したい場合は、「OUT=」を使います。
proc catalog cat=work.formats;
   contents out=OUT1;
run;
quit;



中身のコードを取得する

サンプルで作成したフォーマットカタログから「フォーマットの中身」を出力する例。
「フォーマットの中身」の文字数が多くて枠のなかに収まらない文字は切られて表示されるので注意

proc format lib=work.formats  fmtlib;
run;



これも青字部分を 「lib=ライブラリ名.フォーマットカタログ名  fmtlib」 という感じで適宜変えてください。


出力するフォーマットを絞りたい場合は、SELECTを使います。
INFORMATの場合は、以下のように、頭にを付けるのを忘れずに!
proc format lib=work.formats  fmtlib;
   select  A_  $B_  @C_;
run;

データセットに出力したい場合は、「CNTLOUT=」を使います。
proc format lib=work.formats cntlout=OUT2;
run;

2015年5月21日木曜日

論理演算子「^」「^^」によるフラグ変数の作成


データステップ100万回
データステップで0,1のフラグ変数を簡単に作成する

あと、私のブログ記事

の応用なので、まずはリンク先記事をご覧ください。



サンプルデータ作成

data DT1;
  V1="abc"; output;
  V1="def";  output;
  V1="";      output;
run;



論理演算子「^」を利用する。

たとえば、
変数V1に"b"という文字が含まれてたら「0」、含まれてなかったら「1」
という変数を作りたい場合、通常以下のように書くと思います。

if index(V1, "b") > 0 then FLG=0;
else FLG = 1;


これは、以下の2つの書き方でもいけます。


FLG = index(V1,"b")=0;

または

FLG = ^( index(V1,"b") );



2個目の「^( 変数値、式、関数 )」という書き方について。「^」はNOTを表しており、
  • 中の変数値、式、関数の結果を真偽値として評価
  • 「^」で否定しているので、真偽値の結果は逆になります。


つまり、変数値,式の結果,関数の結果が、
  • 0、NULLの場合 ・・・ 「1.true
  • 上記以外 ・・・ 「0.false
となる。



論理演算子「^^」を利用する。

上記の条件を逆にしたい場合、つまり、、
 変数に"b"という文字が含まれてたら「1」、含まれてなかったら「0」としたい場合、以下の2つの書き方でいけます。


FLG2 = index(V1,"b")>0;

または

FLG2 = ^^( index(V1,"b") );



2個目の「^^( 変数値、式、関数 )」という書き方について。「^^」はNOT NOTを表し、
変数値,式の結果,関数の結果などに対して、「否定の否定」をしています。

つまり、変数値,式の結果,関数の結果が
  • 0、NULLの場合 ・・・ 「0.false
  • 上記以外 ・・・ 「1.true
となる。



「^^」を使った応用として、
変数「V1」に"b"という文字が含まれてるかどうかで「1:無」「2:有」という有無変数を作りたい場合、

FLG3 = ^^(index(V1,"b"))+1;


とすれば良い。


注意点

以下のプログラムで出来る「FLG1」と「FLG2」の値は異なる事を理解しておく必要があります。

data DT2;
   A=2;  B=1;

   FLG1 = ^^(A=B);
   FLG2 = ^^A=B;
run;


「FLG = ^^(A=B)」 と書くとAとBが同じ値であれば「1」、それ以外「0」が返されるのに対し、
「FLG = ^^A=B」 と書くと、 「^^A」 の部分が先に評価されて 「FLG = 1=B」 と展開してしまいます。

必要な場合はカッコで括るのを忘れずに!


以上、「色んな書き方があるんだねー」って思ってもらえればいいかなって記事でした。

2015年5月18日月曜日

「SAS Studio」 でデータセット名や変数名をドラッグ&ドロップで自動入力する機能



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

以下のように、ライブラリからデータセットを選択して、ドラッグ&ドロップでコードまで持っていくと、、















コードにデータセット名が自動で入ります。












その他、Ctrlキー押しながら変数名を複数選択して、同じようにコードにドラッグ&ドロップすることで、、



















コードに変数名がすべて入ってきます。(何気にこれが便利)



















こんな感じでちょっとしたプログラミングの短縮になって便利です。

2015年5月17日日曜日

WHEREステートメントの落とし穴


「WHEREステートメント」 と 「WHERE=データセットオプション」 を組み合わせると、想定外のデータになってしまうかもしれないです。


*** サンプルデータ作成 *************;
data DT1;
   A=1; B="aa"; output;
   A=2; B="bb"; output;
run;

 A 
   1 
 aa  
   2  
 bb  

data DT2;
   A=1; output;
   A=2; output;
run;

 A 
  1 
  2  

*** 注意が必要な例 **********;
data OUT1;
  merge DT1(where=(B="aa"))  DT2;
  by A;
  where A=2;
run;

 A 
   1 
 aa  
   2  
   


結果のデータセットは想定通りだったでしょうか?
↓こんな感じになると思った方もいると思います。

 A 
   2  
       


これは、
  • 「WHERE=データセットオプション」が指定されたデータセットは、そのWHERE条件だけが適用され、
  • それ以外のデータセットは「WHEREステートメント」の条件が適用される。


つまり、今回の例で、、

  merge DT1 (where=(B="aa"))  DT2;
  by A;
  where A=2;

  • DT1は「where=(B="aa")」の条件が適用され、
  • DT2は「where A=2」の条件が適用されて、マージされます。


ちなみに、SAS9.3からログに以下のようなWARNINGが出るようになっていますが、SAS9.2ではWARNINGが出ないので、この問題に気づきにくいです。

WARNING: WHEREステートメントは最後のSET/MERGE/UPDATE/MODIFYステートメントのデータセットに適用できません。
データセットがオープンエラー、または既にWHEREデータセットオプションを指定しています。



2015年5月14日木曜日

DS2プロシジャ入門1:基本構文




SAS9.4から追加されたDS2プロシジャについて、基本的な部分からざっくり解説していきたいと思います。
データステップがパワーアップしたようなプロシジャなので、ある程度データステップの動きを知ってる方が理解しやすいです。

私自身勉強中なので、間違いなどがあればご指摘をお願いします。



基本構文

PROC DS2 ;
     DATA  作成するデータセット ;

         ~ 処理 ~

     ENDDATA ;
     RUN ;
QUIT ;

  PROC DS2;
      ~
  QUIT ;
  • DS2プロシジャは、「PROC DS2」で始まり、「QUIT」で終わる。
  DATA データセット ;
      ~
  ENDDATA ;
  RUN ;
  • DATAステップと同じで「DATA 」で始まる。
  • 「ENDDATA」で終わる。実行するために「RUN」も必要。



まずは簡単な例。

例①

*** サンプルデータ作成 *************;
data DT1;
input A @@;
cards;
10 20 30
;

  10 
  20  
  30 

*** DS2 Procedure **********;
proc ds2;
  data _NULL_;

      method init();
          put '--- 出力開始';
      end;

      method run();
          set DT1;
          put '---' A;
      end;
      
      method term();
          put '--- 出力終了';
      end;

  enddata;
  run;
quit;

ログ
--- 出力開始
--- 10
--- 20
--- 30
--- 出力終了


「method init()」 とか 「method run()」 とかいうのが出てきて「なんじゃこりゃ、もうやだ!」って感じですが、ここはグッと堪えましょう。

DS2プロシジャでは、基本的にこのメソッドの中に処理を書いていきます。

  method init() ;
     ~
  end;
 最初に1回だけ実行したい処理をこのメソッドの中に書く。


  method run() ;
     ~
  end;
 各オブザベーションを読み込んで実行したい処理をこのメソッドの中に書く。


  method term() ;
     ~
  end;
 最後に1回だけ実行したい処理をこのメソッドの中に書く。



各メソッドの役割を知ったところで、先ほどのプログラムおよびログと照らし合わせて確認すると、理解がしやすいと思います。



では、もう1歩先に進んだ例を紹介。

例②

*** DS2 Procedure **********;
proc ds2;
   data OUT2 (overwrite=yes);

      keep TOTAL ;  

      method init();
         TOTAL = 0;
      end;

      method run();
         set DT1;
         TOTAL + A;
      end;
 
      method term();
         output;
      end;
 
   enddata;
   run;
quit;

 A 
  60  

ログにWARNINGが出ると思いますが、今は放置しといてください。



解説

method init();
   TOTAL = 0;
end;

まず最初、変数TOTALに、初期値「0」を与えます。

method run();
   set DT1;
   TOTAL + A;
end;

データセットDT1をsetして、変数TOTALに各オブザベーションの変数Aの値を足していきます。

method term();
   output;
end;

最後に1回だけ、output。
これで、変数Aの合計値を求めたデータセットOUT2が出来ました。

以上終わり、、と言いたいところですが、ここで2つ重要なポイント。

data OUT2 (overwrite=yes);

「(overwrite=yes)」 というオプションを入れること!
これは、「すでにOUT2というデータセットが存在する場合、上書きしていいよ」、というオプションです。
つまり、これを指定してないと、上書きできずにエラーになってしまいます。

keep TOTAL ;

データステップの場合だと、keepはどこに書いても問題なかったけど、
DS2プロシジャの場合は、メソッドの外に書かなければいけない。
また、どのメソッドよりも上に書く必要があります。




DS2プロシジャ入門記事

1: 基本構文
: 変数の宣言
3: 変数属性と配列の定義

2015年5月12日火曜日

RWIでフォトブックをつくる


SAS9.2ではちゃんとした結果が出てくれないかも。

(また、貼り付ける画像ファイルによっては、出来上がるファイル容量も馬鹿でかくなるので、PCの環境に自信がない場合は、実行しない方がいいかも)

プログラムを実行する場合は、プログラム中の青字にしてるパスを各時環境のパスに変更してください。
あとSAS9.3の場合は、「style_attr」の部分をすべて「overrides」に変えてください。

プログラムで使う写真

   



プログラム
ods html close;
ods pdf file="出力するファイルのパス\photobook.pdf" notoc  dpi=150;

data _NULL_;
  dcl odsout ob();

  *** 1ページ目 ***************************;
  ob.layout_absolute();

    * 1枚目の写真 ;
    ob.region( x:'2cm',  y:'1cm',  width:'9cm' );
    ob.image( file:'写真を格納したパス\DSC_0614.JPG' );

    ob.region( x:'12cm',  y:'5cm',  width:'7cm' );
    ob.format_text( data:"夏の恒例「SASユーザー総会」",
                         style_attr:"fontsize=12pt" );

    * 2枚目の写真 ;  
    ob.region( x:'10cm',  y:'12cm',  width:'8cm');
    ob.image( file:'写真を格納したパス\DSC_0618.JPG' );

    ob.region( x:'2cm', y:'22cm', width:'8cm' );
    ob.format_text( data:"ユーザー総会限定の粗品",
                         style_attr:"fontsize=12pt" );
    
  ob.layout_end();

  ob.page();

  *** 2ページ目 ***************************;
  ob.layout_absolute();

    * 1枚目の写真 ;
    ob.region( x:'2cm', y:'1cm', width:'10cm' );
    ob.image( file:'写真を格納したパス\DSC_0489.JPG' );

    ob.region( x:'12.5cm', y:'5cm', width:'7cm' );
    ob.format_text( data:"堀切菖蒲園の菖蒲たち",
                         style_attr:"fontsize=12pt" );

    * 2枚目の写真 ;  
    ob.region( x:'2cm', y:'9cm', width:'10cm' );
    ob.image( file:'写真を格納したパス\DSC_0611.JPG' );

    ob.region( x:'12.5cm', y:'16cm', width:'7cm' );
    ob.format_text( data:"熱い将棋バトル、電王戦タッグマッチ2014",
                         style_attr:"fontsize=12pt" );
 
  ob.layout_end();

run;

ods pdf close;
ods html;


結果





写真の解像度が高いと、出来上がるPDFファイルの容量も大きくなってしまいます。

プログラム2行目の「ods pdf  ~ dpi=150;」で、解像度を設定してます。
デフォルトは 「dpi=150」なので、写真枚数が多い場合は、ファイル容量が恐ろしくやばくなるので、まずは「dpi=75」とかにして、様子を見たほうがいいかも。


今回使用してるRWIについては、以下の入門シリーズにまとめています。
レポート作成インターフェイス(RWI)入門1

2015年5月11日月曜日

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


RWIでテキストを配置するメソッドを紹介。

 FORMAT_TEXT( data: "テキスト)



このメソッドと、以前紹介したREGIONメソッドを組み合わせれば、好きな位置に好きなテキストを配置できます。


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

data _NULL_;
  dcl odsout ob();
  ob.layout_absolute();

     ob.region( x:'1cm',  y:'2cm',  width:'8cm');
     ob.format_text( data:"aaaaaaaaaaa" );

     ob.region( x:'5cm',  y:'3cm',  width:'8cm');
     ob.format_text( data:"bbbbbbbbbb" );

  ob.layout_end();

run;

ods pdf close;
ods html;

TEST.pdf


2015年5月8日金曜日

ODS HTML,RTF,PDF等の出力で、別々のファイルに同時出力する方法




まずは、以下のプログラムと実行結果を見てみましょう。

*** サンプルデータ作成 ;
data DT1;
   A=1;
run;

data DT2;
   B=2;
run;

*** 別々のファイルに同時出力する ;
ods html(1)  path="C:\"  file="TEST1.html";
ods html(2)  path="C:\"  file="TEST2.html";

   proc print data=DT1;
   run;

ods html(1) close;

   proc print data=DT2;
   run;

ods html(2) close;

出力ファイル

TEST1.html


TEST2.html


「TEST1.html」 には、最初の PRINTプロシジャの結果だけが出力され、
「TEST2.html」 には、すべての PRINTプロシジャの結果が出力されています。


「ods html(1)」 や 「ods html(2)」 というように書くことで、
同時に別々のファイルに出力することが出来ます。


ODS RTFやPDFでも同様にいけます。