2014年3月9日日曜日

分析の本番環境

意外とアクセス数があったので、記事を加筆、修正します。

分析とかリスク計算に携わるお客様からよく相談されるのがテスト環境、本番環境のことです。システム部門からすると、本番環境だけしかないのは信じがたく、分析者から見るとなんで本番環境を構えるかわからないことがままあります。


そのギャップを理解しないまま、システム統制に従うと色々苦労が絶えません。設計書ありません、テストケースありませんというのは、アドホックな分析を生業としている人には普通のことです。未知なる数値、ロジックを追い求めるときに設計書作ってレビューなんてしません。

大概、システム統制をかけようとしてユーザから情報システム部へ引き渡すときに問題が生じます。プログラムやデータ資産の範囲が不明確、計算のパラメータはテストすることなく都度修正、計算結果は回してみないと業務で使えるかどうかわからない、テストケースが無い等々、情報システム部にとっては腹の立つことばかりです。


システムとして統制効かせたり、定型化して業務とリンクさせるときには、環境を分けて実装しなおすのが良いです。この設計を起こして、実装をやり直すというのが、あまり理解されていないと感じます。分析環境で書かれたドキュメントのないコードを、そのまま本番環境に放り込んでというのは、最初の1年ぐらいは持つかもしれません。

数年たって立ち往生しそうなシステムの手当てを、SAS Log Utilityを使って何回か引受けています。

2014年3月1日土曜日

マクロ %varlist

変数の値をカンマでつないだリストを作りマクロ変数にセットします。よく使う割りに忘れます。変数が数値でも文字型でも動くように修正しました。

/*
*++
* データセット(dsn)の変数(keep)をリストにして、マクロ変数(macvar)にセット
*--
*/
%macro varlist(dsn=, keep=, macvar=);
 %global &macvar;
 %local i dsid n type rc;
 %let i=;
 %let dsid=%sysfunc(open(&dsn,i));
 %let n=%sysfunc(varnum(&dsid,&keep));
 %let type=%sysfunc(vartype(&dsid,&n));
 %let rc=%sysfunc(close(&dsid));
 %let &macvar=;

 %if &type=C %then
  %do;
   /* 変数が文字型 */
   data _null_;
    set &dsn(keep=&keep) end=eod;
    length list $1024;
    retain list;

    if _n_ eq 1 then
     do;
      list = '"' || compress(&keep) || '"';
     end;
    else
     do;
      list = trim(list) || ',"' || compress(&keep) || '"';
     end;

    if eod eq 1 then
     do;
      call symput("&macvar", compress(list));
     end;
   run;

  %end;
 %else
  %do;
   /* 変数が数値型 */
   data _null_;
    set &dsn(keep=&keep) end=eod;
    length list $1024;
    retain list;

    if _n_ eq 1 then
     do;
      list = put(&keep, best.);
     end;
    else
     do;
      list = trim(list) || ',' || put(&keep, best.);
     end;

    if eod eq 1 then
     do;
      call symput("&macvar", compress(list));
     end;
   run;

  %end;
%mend;

%varlist(dsn=sashelp.class, keep=name, macvar=test);
%put test=&test;


マクロ %nobs

データステップを使わないで、OBS数を得るマクロです。よく使う割りに忘れます。

/*
*++
* データセットのOBS数を返す
*--
*/

%macro nobs(ds);
 %local dsid rc;
 %let dsid=%sysfunc(open(&ds));
 %if &dsid %then %do;
  %sysfunc(attrn(&dsid,NOBS));
  %let rc=%sysfunc(close(&dsid));
 %end;
 %else %do;
  -1
 %end;
%mend nobs;

%put nobs=%nobs(sashelp.class);

2013年12月14日土曜日

「このプログラムは正しくインストールされなかった可能性があります」を回避する方法

Windows 7 + Delphi XE2で開発していますが、以下のメッセージを抑制する方法をメモしておきます。アプリケーションにインストールと思しきキーワードが入っていると、OS側でアンインストールの手順をきちんと登録したか確認してくるのが、以下のダイアログです。


で、いろいろ調べてみてうまくいった方法を書きます。 $(BDS)\bin にあるdefault_app.manifestを探しだして、compatibilityセクションを書き加えてビルドします。下がmanifestのファイル、この15-20行目が追加したセクションです。 manifestにWindows7互換という条件を加えることで、UAC互換性アシスタントを回避しています。



  
    
      
    
  
  
    
      
        
        
    
  
  
    
      
      
    
  


2013年11月5日火曜日

NOTE: 要約化中にディスク処理が発生しました。

時間が出来たときに、ツールで以下のメッセージを捕捉するように修正する。

  • Processing on disk occurred during summarization. Peak disk usage was approximately nnn Mbytes. Adjusting SUMSIZE may improve performance. 
  • NOTE: 要約化中にディスク処理が発生しました。 ディスクの最大使用はおよそ 2454 M バイトです。 SUMSIZE の調整によって、パフォーマンスを改善できるかもしれません。

Calls a specific routine or module that resides in an external dynamic link library (DLL).

SASからWindowsのAPIを呼び出す方法を整理しています。
その理由を列挙しますと

  • SASのバージョンアップの作業で、Xステートメントが規制されている
  • SASデータセットをCOPYプロシージャで移行するとタイムスタンプが維持できない
  • タイムスタンプが変わると元ファイルが更新されたときに再転送が必要かわからない
  • DOS窓からの実行だと遅い

VBScriptから制御する手もありますが、古いデータセットから順に移行したり、停止の制御を考えるとSAS1本でやるのが良さそうです。

基本はDLLを呼び出すためのI/Fを"The SASCBTBL Attribute Table"で定義して、MODULE関数で呼び出します。この仕組みが繊細で、定義を間違えるとSAS.EXEが落ちます。WinAPIの型を調べて、変数を割り当てるのは面倒な作業です。

2013年11月4日月曜日

SASからCopyFileAを呼び出して、ファイルをコピー

SASからWindowsのAPIをコールするサンプルコードです。バージョンアップによるデータの引越しで、今回はWinAPIを使ったツールを作る予定です。動作確認したのは、Windows7+SAS9.2です。

ネットにはいくつか見本が転がっていますが、PDFで文字が化けていたり、APIがまったく変わってしまったようなものもあります。素直に動くものが少ないので、いくつかの手本を元に作成しました。APIの定義をテキストで別に用意している例が多いのですが、カタログを使った例がまとまりが良いです。

/*
*++
* WinAPI(CopyFileA)を呼び出して、ファイルをコピー
*--
*/

filename winapi catalog 'WORK.Windows.WINAPI.SOURCE';

data _null_;
 file winapi;
 input;
 put _infile_;
 cards4;
routine CopyFileA
 module=KERNEL32
 minarg=3 maxarg=3 stackpop=called
 returns=ushort
;
arg 1 input char format=$cstr200.; * FROM ;
arg 2 input char format=$cstr200.; * TO ;
arg 3 input num format=pib4. byvalue;
* 1=Do Not Overwrite Existing ;
* 0=Overwrite Existing File ;
;;;;
run;

filename winapi;
filename sascbtbl catalog 'WORK.Windows.WINAPI.SOURCE';

data _null_;
 rc = modulen('*e', 'CopyFileA', 'c:\temp\foo.txt', 'c:\temp\foo_copy.txt', 0);
 put _all_;
run;

filename sascbtbl clear;