2013年8月15日木曜日

データセットのI/Oを補足するコード(私家版#2)

これ自分用のコードです。

function libname(dsn)
{
        i = index(dsn, ".");
        if (i > 0) {
                tmp = substr(dsn, 1, i - 1);
        }
        else {
                tmp = "WORK";
        }
        return toupper(tmp);
}

function memname(dsn)
{
        i = index(dsn, ".");
        if (i > 0) {
                tmp = substr(dsn, i + 1, length(dsn));
        }
        else {
                tmp = dsn;
        }
        return toupper(tmp);
}

function ilog(path, dsn)
{
        printf("R,%s,%s,%d,%s,%d\n", path, dsn, FNR, FILENAME, NR);
        return 1;
}

function olog(path, dsn)
{
        printf("W,%s,%s,%d,%s,%d\n", path, dsn, FNR, FILENAME, NR);
        return 1;
}

/NOTE: ライブラリ参照名/ && /を次のように割り当てました。/ {
        libref = $3;
        getline;
        while ($1 != "物理名") {
                getline;
        }
        lib[libref] = $3;

}

$1 ~ /NOTE:/ && $2 ~ /データセット/ && $NF ~ /オブザベーションを読み込みました。/ {
        dsn = $3;
        libref = libname(dsn);
        mem = memname(dsn);

        if (libref != "WORK" && libref != "SASHELP") {
                dir = lib[libref];
                if (dir != "") {
                        tmp = sprintf("%s/%s.sas7bdat", dir, tolower(mem));
                        status = ilog(tmp, dsn);
                }
                else {
                        printf("ERROR %s\n", dsn);
                        exit;
                }
        }
}

$1 ~ /NOTE:/ && $2 ~ /データセット/ && $NF ~ /変数です。/ {
        dsn = $3;
        libref = libname(dsn);
        mem = memname(dsn);

        if (libref != "WORK" && libref != "SASHELP") {
                dir = lib[libref];
                if (dir != "") {
                        tmp = sprintf("%s/%s.sas7bdat", dir, tolower(mem));
                        status = olog(tmp, dsn);
                }
                else {
                        printf("ERROR %s\n", dsn);
                        exit;
                }
        }
}

$1 ~ /MPRINT\([A-Z0-9_]+\):/ && $2 ~ /^set$/ {
        match($3, /([a-zA-Z0-9_]+\.)?[a-zA-Z0-9_]+/);
        if (RLENGTH > 0) {
                dsn = substr($3, RSTART, RLENGTH);
                libref = libname(dsn);
                mem = memname(dsn);
                if (libref != "WORK" && libref != "SASHELP") {
                        dir = lib[libref];
                        if (dir != "") {
                                tmp = sprintf("%s/%s.sas7bdat", dir, tolower(mem));
                                status = ilog(tmp, dsn);
                        }
                        else {
                                printf("ERROR %s\n", dsn);
                                exit;
                        }
                }
        }
}

END {
        print "_EOF_";
}

SASデータセットのI/Oを捕捉するコード(私家版)

SASログからI/Oを追いかける私家版のSASプログラム。入力のCSVファイルはAWKで作成しています。お客様先で大量のSASログから、入力と出力を特定するためのものです。入力ファイルで1件もデータを読まないで、僅かにMPRINTのログにしか手掛かりがないものが厄介です。
その他に、SASHELP.VTABLEを通してファイルの存在チェックをしているものなど、SASログには現れない入力があります。

/*
*++
* import io log-file
*--
*/

data iods;
 infile "c:\temp\iods.csv" dsd firstobs=2;
 attrib RW length=$1;
 attrib FILENAME length=$64;
 attrib DSN length=$32;
 attrib FNR length=8;
 attrib LOG length=$61;
 attrib NR length=8;
 input rw filename dsn fnr log nr;
run;

/*
*++
* input data-set
*--
*/

proc sort data=iods out=ids;
 by filename nr rw;
run;

data ids;
 set ids;
 by filename nr rw;
 if first.filename and rw eq 'R' then output;
run;

proc sort data=ids;
 by nr;
run;


/*
*++
* output data-set
*--
*/

proc sort data=iods out=ods;
 where rw eq 'W';
 by filename nr;
run;

data ods;
 set ods;
 by filename nr;
 if last.filename then output;
run;

proc sort data=ods;
 by nr;
run;

/*
*++
* make compare procedure statement
*--
*/

data _null_;
 set ods;
 attrib comment length=$256;
 attrib libstat length=$256;
 attrib titstat length=$256;
 attrib comproc length=$256;
 attrib mem length=$32;

 basedir = "/sumdata/bk02";

 comment = "/* dsn=" || compress(upcase(dsn)) || " logfile=" || compress(log) || " line=" || compress(put(fnr, best.)) || " */";
 put comment;

 i = 1;
 imax = length(filename);
 pos = 0;

 do i=1 to imax;
  if substr(filename, i, 1) eq '/' then pos = i;
 end;

 put "options nocenter;";
 libstat = "libname tmp1 '" || compress(basedir) || substr(filename, 1, pos-1) || "' access=readonly;";
 put libstat;

 libstat = "libname tmp2 '" || substr(filename, 1, pos-1) || "' access=readonly;";
 put libstat;

 titstat = "title 'dsn=" || compress(upcase(dsn)) || " log=" || compress(log) || "';";
 put titstat; 

 mem = substr(dsn, index(dsn, ".")+1);
 comproc = "proc compare base=tmp1." || compress(mem) || " compare=tmp2." || compress(mem) || " maxprint=5;";
 put comproc;
 put "run;";
 put "title;";
 put ;

run;

2013年5月3日金曜日

TExcelApplicationでExcelの書式設定を行う

Officeを2007から2013にアップグレードしたため、いくつかのアプリケーションを点検しています。そんな中で、TExcelApplicationを使ったサンプルを見つけ、Office2013で動くか試してみました。問題なく動いています。

元ネタは、Delphi-Fanの「Excelの書式設定を行う」を利用させていただきました。Excel絡みのアプリケーションを組むことがあるので、大変勉強になるコードです。ブログにコメントが残せないので、ここでお礼申し上げます。


2013年4月28日日曜日

自宅PCをSSD化

連休は、自宅PCのハードディスクをSSDに換装しました。今のところ特段問題もなく、快適に動いています。入れ替えたのは、インテル Boxed SSD 335 Series 240GB MLC 2.5inch 9.5mm Jay Crest Reseller BOX SSDSC2CT240A4K5です。換装の手順は


  1. 筐体を開けて、SSDをセカンダリのディスクとして接続
  2. 起動してクローンのディスク作成のプログラムをインスト&実行
  3. 再起動してHDDからSSDへコピーされるのを待つ
  4. シャットダウンされたらセカンダリをプライマリのディスクとして入れ替え


換装前後で、ディスクのI/O性能を測って比較してみました。使ったのはCrystalDiskMarkです。なんだか、萌え絵がついたスペシャルバージョンをインストールしてしまいました。値をExcel2013でまとめて、グラフにしています。


HDDとSSDの比較

換装前

SSD換装後




2013年2月16日土曜日

年齢の計算

YRDIFを年齢の計算に使っていましたが、不具合があることに気がつきました。結局、古典的なコードを引っ張り出して計算しています。

data _null_;
        format start end yymmdds10.;

        start = '01mar1992'd;
        end = '01mar1999'd;

        /* BUG */
        dif = yrdif(start,end,'ACT/ACT');
        put _all_;

        dif = intck('YEAR', start, end);

        /* 今年の誕生日がまだ来ていない場合は、数え年から1を引きます */
        if (month(end) < month(start)) then
                dif = dif - 1;
        else if (month(end) = month(start)) and day(end) < day(start) then
                dif = dif - 1;

        put _all_;
run;


2013年2月9日土曜日

Peek at your data using VBScript, OLE DB, and the SAS local data provider

SAS Local Data Providerを使って、SASデータセットの情報を読み取るサンプルコードです。元ネタはココですが、貼り付けるときにバックスラッシュが化けたのかそのままでは動きませんでした。少し手直ししています。SAS.EXEを起動しなくてもOKというのが軽くて良いのですが、果たして何に使うかは思案中です。Delphiで作るツールに役立つかも。

path = "C:\Program Files\SASHome\x86\SASEnterpriseGuide\4.3\Sample\Data"
filename = "Candy_Sales_Summary"

WScript.Echo "Path specified: " & path
WScript.Echo "File name : " & filename

' Check registry for SAS Local Provider
Set WSHShell = CreateObject("WScript.Shell")
clsID = WSHShell.RegRead("HKEY_CLASSES_ROOT\sas.LocalProvider\CLSID\")
'clsID = WSHShell.RegRead("HKCRSAS.LocalProviderCLSID")
WScript.Echo "DIAGNOSTICS: SAS.LocalProvider CLSID is " & clsID
inProcServer = WSHShell.RegRead("HKCR\CLSID\" & clsID & "\InprocServer32\")
WScript.Echo "DIAGNOSTICS: Registered InprocServer32 DLL is " & inProcServer

' Constants for ADO calls
Const adOpenDynamic = 2
Const adLockOptimistic = 3
Const adCmdTableDirect = 512

' Instantiate the provider object
Set obConnection = CreateObject("ADODB.Connection")
Set obRecordset = CreateObject("ADODB.Recordset")

obConnection.Provider = "SAS.LocalProvider"
obConnection.Properties("Data Source") = path
obConnection.Open
obRecordset.Open filename, obConnection, adOpenDynamic, adLockOptimistic, adCmdTableDirect

'Report on Fields in this data set
WScript.Echo ""
WScript.Echo "Opened data " & filename & ", Record count: " & obRecordset.RecordCount
For Each Field In obRecordset.Fields
If Field.Type = 5 Then pType = "Numeric"
If Field.Type = 200 Then pType = "Character"
WScript.Echo Field.Name & " " & pType
Next

obRecordset.Close
obConnection.Close

2013年1月15日火曜日

SAS Log Inspector Version 1.2.0.0 リリース

3連休の間に機能追加しました。マッチングのパターンを画面上から追加、修正して、INIファイルに上書き保存することができるようにしました。INIファイルの書式の問合せが来ても面倒と考えての機能追加です。


この1.2.0.0は、IDEの環境を英語版にスイッチして、ビルドしています。Exportしたときのダイアログは何故か、日本語のままです。それと、ビルドの番号が自動的に上がらない現象がでています。

英語版のサイトに載せて公開することを検討しています。

SAS Log Inspectorの機能
  • 複数のログファイルからエラー、バグに繋がるNOTEを集計
  • 日本語と英語版のメッセージに対応
  • Excelファイルにログをチェックした結果をエクスポート
  • 任意のチェック項目を検査対象から外すことができる
  • Notepadを起動して、該当のログファイルを参照できる
  • マッチングパターンを画面から追加、修正できるように機能を追加