The Nameless City

何故か製薬やSAS関連のブログ、の予定。

CentOS on VirtualBox、のスマートな利用方法

という辺りで詰まってますなー。

Proxy Settingはグダグダながら何とか設定した事にした。

GNOMEでのSETTINGもしましたが。
sudoでもENVKEEP設定もしましたが。
結局の所、dconfだったかが、「接続出来ませんねん」みたいなメッセージを出して来る、あの問題をどう解決するかがちょっと目処つかないので、
「proxyの設定を二重にやってます」。


まあでも、ホストOS側でリバースプロキシとか立てないでやってます。
本当はそれがいいのかもですが、ネットワークの領域には踏み込まない。踏み込まないよ!!!!!

今の課題が、CentOS 7での共有フォルダと、CDROMの自動マウントの、そん時の権限設定。

通常の設定だと、CDマウントが、その時デスクトップ開いていたユーザで設定されますね。
また、VBOXの共有フォルダは、vboxsfだったかのグループに所属するユーザだけが権限ありますね。


・・・・・・みたいなのを変えたいんですが、自動マウントはudisks2かで実施されており・・・・・・・みたいな話までしか追いかけられてません。


変態な話ですが、ログインユーザと違うユーザでDVDドライブから共有フォルダを参照しつつインストールを行いたい、という事に向けて、やたら調査中。
いや、分かってんねん、fstabという逃げ手はな。ただ、ワザワザ新しい仕組みがあるのにfstabで設定するというのもちと嫌だし。

会社もグループでもやる気がない感じなのが、もうなんてーかな。

コア数とかSSDとかに大して興味はないんですが、ビッグデータ解析にもマイナス評価なんですが、ベイズ解析とかがガンガン出来る環境上で、SAS構築するのはしたいなと思ってはいるんですよね。


今、単純にマシンスペック入れるだけでは性能が大して上がらないというような状況になりつつあります。
普通の解析なら十分だと思うんですけど、マルチコアマルチスレッドとか考えだすと、システムの仕掛けからまだチューニングとかでざっくりですが数十倍出せると思うんですよね。
・・・・・・プログラムの効率化、じゃなくて、今のOSやシステムの発展は、インタラクティブ型と言いますか、リアルタイムに複数のプログラムを同時に動かす事を前提にしてます。で、要はハードの上に幾つもの階層のVMが想定されているようなもんなんで、普通に動かすには問題ないパラメータで設定はされているんですがね。
やはりパラレルに動こうが巨大なバッチとして捉える方がチューニングとしては向いているんじゃないかなと思うわけです。


・・・・・・しかし、こんな話を上の方とか親会社があんまり楽しく受け取ってくれないんですよね。
頼むからそういうことさせてくれ、とは思うんですが。上の会社にくっついている総合研究所とかそんな所が真面目にそういう事考えるかというと・・・・・・今はあんまりしてくれないですしね。


使えないからほぼほぼ自分で全部インターネットで調べてやってたりするんですが、もうそれで「売上が~」とか言われるのも辛いですし、ホントどうにかして欲しいです。

SASという製品を「1個」買う、という事はまず出来ないので。

SASですが。
「Analytics Pro」であるとか、そういう名前でご指定下さい。マジで。
SAS V8以降、SASのライセンスの呼び方や体系が変わってるんです。


現在、サーバに入れるSASの場合、通常はSAS Enterprise GuideやSAS Studio経由でアクセスするもの、というライセンス体系になってますので、SAS Integration Technologyインストールが普通になってます。要らないと言ってもライセンスには入ってきます。
で。
ライセンスには入って来るんですが、もちろん、そういうサーバ設定を作らなくても使えはするのです。まあ。
ただ、リモートデスクトップ接続で利用する場合にはWindowsサーバ側の設定がいりますし、Linux/VNCだったらVNCの設定いりますし、X Clientでの接続でも似たようなもんです。面倒臭いと言われたらどれもこれも面倒臭いですが。


サーバ構成しちゃうなら、じゃあデータはどこに置くのとかどうファイル持ち込むのとか決めてく必要があります。ただSASをエンジンとして利用したいだけだとかどんどん使い方を決めて行って下さい。


まあでもせっかくサーバ構成しても、WebDAVの部分とかもろくに使われないんですよね・・・・・・

SASでのトランスコーディングあれこれ。

トランスコーディングは、大抵の場合、データセットの別環境への移送も含みます。
ので、そこら辺も絡めてちょっと言及しておきます。

前振りとして。

SASの文字変数は、固定長文字列としてバイト長で定義されています。その為、トランスコーディングに伴う文字列の実際のバイト長に対して問題が起きやすいです。
また、固定長文字列で実際のデータも配置される為、RDBMSなどでは普通に行える事であっても出来ないあるいは難易度の高い事があります。文字列長を簡単には変更出来ません。

SASデータセットは文字変数の長さを直接変更は出来ない。

以下の例をご覧下さい。

/* テストデータはWORK.CLASS */
data WORK.CLASS ;
	set SASHELP.CLASS ;
run ;

/* 列定義変更っぽい事をしてみる */
data WORK.CLASS ;
	set WORK.CLASS ;
	length NAME $100 ;
run ;
328  /* テストデータはWORK.CLASS */
329  data WORK.CLASS ;
330      set SASHELP.CLASS ;
331  run ;

NOTE: データセットSASHELP.CLASSから19オブザベーションを読み込みました。
NOTE: データセットWORK.CLASSは19オブザベーション、5変数です。
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.01 秒


332
333  /* 列定義変更っぽい事をしてみる */
334  data WORK.CLASS ;
335      set WORK.CLASS ;
336      length NAME $100 ;
WARNING: 文字変数Nameの長さはすでに設定されています。 文字変数の長さを宣言するには、
         DATAステップの最初にLENGTHステートメントを使用してください。
337  run ;

NOTE: データセットWORK.CLASSから19オブザベーションを読み込みました。
NOTE: データセットWORK.CLASSは19オブザベーション、5変数です。
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.01 秒
/* テストデータはWORK.CLASS */
data WORK.CLASS ;
	set SASHELP.CLASS ;
run ;

/* 列定義変更前の定義を取得 */
proc contents
	data=WORK.CLASS
	out=WORK.CLASSDEF1
	(keep=LIBNAME MEMNAME VARNUM NAME TYPE LENGTH FORMAT: IN:)
	noprint
	;
quit ;

/* 列定義変更 */
proc sql ;
	alter table WORK.CLASS
		modify NAME length 100
	;
quit ;

/* 列定義変更後の定義を取得 */
proc contents
	data=WORK.CLASS
	out=WORK.CLASSDEF2
	(keep=LIBNAME MEMNAME VARNUM NAME TYPE LENGTH FORMAT: IN:)
	noprint
	;
quit ;

/* 列定義変更の前後の差を比較するも完全一致 */
proc sort data=WORK.CLASSDEF1 ;
	by LIBNAME MEMNAME VARNUM ;
quit ;
proc sort data=WORK.CLASSDEF2 ;
	by LIBNAME MEMNAME VARNUM ;
quit ;
proc compare
	base=WORK.CLASSDEF1
	comp=WORK.CLASSDEF2
	method=absolute
	note
	;
	id LIBNAME MEMNAME VARNUM ;
quit ;
807  /* テストデータはWORK.CLASS */
808  data WORK.CLASS ;
809      set SASHELP.CLASS ;
810  run ;

NOTE: データセットSASHELP.CLASSから19オブザベーションを読み込みました。
NOTE: データセットWORK.CLASSは19オブザベーション、5変数です。
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.01 秒


811
812  /* 列定義変更前の定義を取得 */
813  proc contents
814      data=WORK.CLASS
815      out=WORK.CLASSDEF1
816      (keep=LIBNAME MEMNAME VARNUM NAME TYPE LENGTH FORMAT: IN:)
817      noprint
818      ;
819  quit ;

NOTE: データセットWORK.CLASSDEF1は5オブザベーション、12変数です。
NOTE: PROCEDURE CONTENTS処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.01 秒


820
821  /* 列定義変更 */
822  proc sql ;
823      alter table WORK.CLASS
824          modify NAME length 100
825      ;
NOTE: テーブルWORK.CLASS (5列)は変更されました。
826  quit ;
NOTE: PROCEDURE SQL処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.01 秒


827
828  /* 列定義変更後の定義を取得 */
829  proc contents
830      data=WORK.CLASS
831      out=WORK.CLASSDEF2
832      (keep=LIBNAME MEMNAME VARNUM NAME TYPE LENGTH FORMAT: IN:)
833      noprint
834      ;
835  quit ;

NOTE: データセットWORK.CLASSDEF2は5オブザベーション、12変数です。
NOTE: PROCEDURE CONTENTS処理(合計処理時間):
      処理時間           0.01 秒
      CPU時間            0.01 秒


836
837  /* 列定義変更の前後の差を比較するも完全一致 */
838  proc sort data=WORK.CLASSDEF1 ;
839      by LIBNAME MEMNAME VARNUM ;
840  quit ;

NOTE: データセットWORK.CLASSDEF1から5オブザベーションを読み込みました。
NOTE: データセットWORK.CLASSDEF1は5オブザベーション、12変数です。
NOTE: PROCEDURE SORT処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.01 秒


841  proc sort data=WORK.CLASSDEF2 ;
842      by LIBNAME MEMNAME VARNUM ;
843  quit ;

NOTE: データセットWORK.CLASSDEF2から5オブザベーションを読み込みました。
NOTE: データセットWORK.CLASSDEF2は5オブザベーション、12変数です。
NOTE: PROCEDURE SORT処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.01 秒


844  proc compare
845      base=WORK.CLASSDEF1
846      comp=WORK.CLASSDEF2
847      method=absolute
848      note
849      ;
850      id LIBNAME MEMNAME VARNUM ;
851  quit ;

NOTE: 不等な値はありません。比較した変数はすべて同等でした。
NOTE: データセットWORK.CLASSDEF1とWORK.CLASSDEF2は完全に同等です。
NOTE: データセットWORK.CLASSDEF1から5オブザベーションを読み込みました。
NOTE: データセットWORK.CLASSDEF2から5オブザベーションを読み込みました。
NOTE: PROCEDURE COMPARE処理(合計処理時間):
      処理時間           0.02 秒
      CPU時間            0.01 秒

最初の方法では、エラーになりますし、二番目の方法では、エラーを吐かないですが変更されていません。
ちょっと回りくどいですが、以下のような方法で変更は可能ではあります。(サンプルはあんまり美しくはないのですが、でも面倒臭いのでそのまま)

/* テストデータはWORK.CLASS */
data WORK.CLASS ;
	set SASHELP.CLASS ;
run ;

/* 列定義変更前の定義を取得 */
proc contents
	data=WORK.CLASS
	out=WORK.CLASSDEF1
	(keep=LIBNAME MEMNAME VARNUM NAME TYPE LENGTH FORMAT: IN:)
	noprint
	;
quit ;

/* 列定義変更 */
data WORK.CLASS ;
	length Name $100 ;
	set WORK.CLASS ;
run ;
/*
このままだと変数の並び順がおかしい。
変数を元の並び順にする場合には
以下のような事をする(他にもやりようはある)
*/
%let _WK_VARLIST = ;
proc sql ;
	select cats(NAME) into:_WK_VARLIST separated by ','
    from WORK.CLASSDEF1 order by LIBNAME,MEMNAME,VARNUM ;
quit ;

proc sql ;
	create table WORK.CLASS
	as select &_WK_VARLIST. from WORK.CLASS
	;
quit ;
%symdel _WK_VARLIST ;

/* 列定義変更後の定義を取得 */
proc contents
	data=WORK.CLASS
	out=WORK.CLASSDEF2
	(keep=LIBNAME MEMNAME VARNUM NAME TYPE LENGTH FORMAT: IN:)
	noprint
	;
quit ;

/* 列定義変更の前後の差を比較する
(Variable:Nameについては若干イカサマしているのでこの例では
変更点としては出ないが
Capが定義情報の差として出る事はしばしばある) */
proc sort data=WORK.CLASSDEF1 ;
	by LIBNAME MEMNAME VARNUM ;
quit ;
proc sort data=WORK.CLASSDEF2 ;
	by LIBNAME MEMNAME VARNUM ;
quit ;
proc compare
	base=WORK.CLASSDEF1
	comp=WORK.CLASSDEF2
	method=absolute
	note
	;
	id LIBNAME MEMNAME VARNUM ;
quit ;
1043  /* テストデータはWORK.CLASS */
1044  data WORK.CLASS ;
1045      set SASHELP.CLASS ;
1046  run ;

NOTE: データセットSASHELP.CLASSから19オブザベーションを読み込みました。
NOTE: データセットWORK.CLASSは19オブザベーション、5変数です。
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.00 秒


1047
1048  /* 列定義変更前の定義を取得 */
1049  proc contents
1050      data=WORK.CLASS
1051      out=WORK.CLASSDEF1
1052      (keep=LIBNAME MEMNAME VARNUM NAME TYPE LENGTH FORMAT: IN:)
1053      noprint
1054      ;
1055  quit ;

NOTE: データセットWORK.CLASSDEF1は5オブザベーション、12変数です。
NOTE: PROCEDURE CONTENTS処理(合計処理時間):
      処理時間           0.01 秒
      CPU時間            0.00 秒


1056
1057  /* 列定義変更 */
1058  data WORK.CLASS ;
1059      length Name $100 ;
1060      set WORK.CLASS ;
1061  run ;

NOTE: データセットWORK.CLASSから19オブザベーションを読み込みました。
NOTE: データセットWORK.CLASSは19オブザベーション、5変数です。
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.00 秒


1062  /*
1063  このままだと変数の並び順がおかしい。
1064  変数を元の並び順にする場合には
1065  以下のような事をする(他にもやりようはある)
1066  */
1067  %let _WK_VARLIST = ;
1068  proc sql ;
1069      select cats(NAME) into:_WK_VARLIST separated by ','
1070      from WORK.CLASSDEF1 order by LIBNAME,MEMNAME,VARNUM ;
NOTE: 指定したクエリにはSELECT句にない項目によるソートが含まれます。
1071  quit ;
NOTE: PROCEDURE SQL処理(合計処理時間):
      処理時間           0.01 秒
      CPU時間            0.01 秒


1072
1073  proc sql ;
1074      create table WORK.CLASS
1075      as select &_WK_VARLIST. from WORK.CLASS
1076      ;
WARNING: This CREATE TABLE statement recursively references the target table. A consequence of this is a
         possible data integrity problem.
NOTE: テーブルWORK.CLASS(行数19、列数5)が作成されました。

1077  quit ;
NOTE: PROCEDURE SQL処理(合計処理時間):
      処理時間           0.01 秒
      CPU時間            0.01 秒


1078  %symdel _WK_VARLIST ;
1079
1080  /* 列定義変更後の定義を取得 */
1081  proc contents
1082      data=WORK.CLASS
1083      out=WORK.CLASSDEF2
1084      (keep=LIBNAME MEMNAME VARNUM NAME TYPE LENGTH FORMAT: IN:)
1085      noprint
1086      ;
1087  quit ;

NOTE: データセットWORK.CLASSDEF2は5オブザベーション、12変数です。
NOTE: PROCEDURE CONTENTS処理(合計処理時間):
      処理時間           0.01 秒
      CPU時間            0.01 秒


1088
1089  /* 列定義変更の前後の差を比較する
1090  (Variable:Nameについては若干イカサマしているのでこの例では
1091  変更点としては出ないが
1092  Capが定義情報の差として出る事はしばしばある) */
1093  proc sort data=WORK.CLASSDEF1 ;
1094      by LIBNAME MEMNAME VARNUM ;
1095  quit ;

NOTE: データセットWORK.CLASSDEF1から5オブザベーションを読み込みました。
NOTE: データセットWORK.CLASSDEF1は5オブザベーション、12変数です。
NOTE: PROCEDURE SORT処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.01 秒


1096  proc sort data=WORK.CLASSDEF2 ;
1097      by LIBNAME MEMNAME VARNUM ;
1098  quit ;

NOTE: データセットWORK.CLASSDEF2から5オブザベーションを読み込みました。
NOTE: データセットWORK.CLASSDEF2は5オブザベーション、12変数です。
NOTE: PROCEDURE SORT処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.01 秒


1099  proc compare
1100      base=WORK.CLASSDEF1
1101      comp=WORK.CLASSDEF2
1102      method=absolute
1103      note
1104      ;
1105      id LIBNAME MEMNAME VARNUM ;
1106  quit ;

NOTE: 次の1変数の値は不等です: LENGTH
NOTE: データセットWORK.CLASSDEF1とWORK.CLASSDEF2には不等な値があります。
NOTE: データセットWORK.CLASSDEF1から5オブザベーションを読み込みました。
NOTE: データセットWORK.CLASSDEF2から5オブザベーションを読み込みました。
NOTE: PROCEDURE COMPARE処理(合計処理時間):
      処理時間           0.01 秒
      CPU時間            0.01 秒

Unicodeを扱えるのは、SAS9.1以降

自分はV8.2と9.1.3SP4ぐらいでしか差を認識していないですが。
但し、記憶に従えばSAS9.1.3ではまだDMSの対応が酷くウィンドウが文字化けしまくってた記憶があります。
SAS9.4になってから、Unicodeサポートの状態で日本語でもログが出るようになりました。
しかし、Unicode対応される場合には、ほぼ英語モードが望まれる気がします。

トランスコーディングの前に・・・・・・何に文字コードを寄せるかという時に。

巷では、ほぼUTF-8エンコーディングを寄せるという発想が主流です。
日本語の取り扱いで、SASでは今でもデフォルトはSJISですし、SJISの方が良いことも多々あります。

  • 既存のデータが何の手当もなく使える
  • 既存のプログラム資産も同様に使える

逆に、SASの世界で何故今でもSJISが使われているかというと、

  • UTF-8では参照するにも一手間必要になる
  • プログラム資産の中に地雷がある
  • 文字列変数の変数長の設計がしっくりこない
  • 文字列変数の変数長がSJISよりだいたい1.5倍になる

です。


ただ、国際化の今、SJISでは中国語や韓国語を取り扱えませんし、各種ツールもUTF-8対応が十分に進んでいる為、そろそろユーザ側に近いアプリケーションではあるSASUTF-8を基本として使う方がいいと思います。
まあ、経験上、例えばJavaやWebアプリはUnicodeが基本ですし、各システムとの連携でももうUnicodeでない方が問題が多い所もありますし。

トランスコーディングでの罠。あるいはCVPオプション。

SJISからUTF-8へ変換する、SJISのデータセットをそのまま使う、際に、現状のSASでは、必ず手当が必要です。
SAS 9.4各国語サポート(NLS): リファレンスガイド(PDF)については、一度読むことをオススメします。


SJISのデータセットであっても、V8以降では、本来は特に文字コード変換を描けなくても、使用時に自動的に変換してくれる、CEDA(Cross-Environment Data Access)という機能が使えるはずです。
はずなんですが、SJISからUTF8環境へのCDEAの適用の場合、ほぼCVPオプションによる「対応」が必須です。
CEDAの機能が中途半端だなあと。


CVPオプションは、主に倍率の方を使用されると思いますが、例えば、SJISとUTF8では、「だいたいは1.5倍で大丈夫」です。
ただ、ちょっとヤバイ所がありまして、第三水準、第四水準の実装がShift_JIS-2004ベースで為されている場合の文字は4バイトになるので問題が発生します。
が、元々ちょっと取り扱いしていないだろうとは思います。WindowsだとSJIS実装されてなかったはずですし

/* SJIS環境で実施 */
libname TEST "C:\temp" ;
proc copy in=SASHELP out=TEST ;
	select CLASS ;
quit ;
/* UTF8環境で実施 */
libname TEST "C:\temp" ;
data _null_ ;
	set TEST.CLASS ;
	putlog _all_ ;
run ;
14   /* UTF8環境で実施 */
15   libname TEST "D:\temp" ;
NOTE: ライブラリ参照名TESTを次のように割り当てました。
      エンジン: V9
      物理名: D:\temp
16   data _null_ ;
17       set TEST.CLASS ;
NOTE: データファイルTEST.CLASS.DATAは別なホストにネイティブな形式が使用されているか
      、またはエンコーディングがセッションエンコーディングと一致していません。
      クロス環境データアクセスが使用されるため、追加のCPUリソースが必要となり、
      パフォーマンスが低下します。
18       putlog _all_ ;
19   run ;

WARNING: データセットTEST.CLASSのトランスコード時に文字データが一部損失しました。
         新しいエンコーディングで表せない文字がデータに含まれていたか、またはト
         ランスコード時に切り捨てが発生しました。
Name=アルフレ Sex=男 Age=14 Height=69 Weight=112.5 _ERROR_=0 _N_=1
Name=アリス Sex=女 Age=13 Height=56.5 Weight=84 _ERROR_=0 _N_=2
Name=バーバラ Sex=女 Age=13 Height=65.3 Weight=98 _ERROR_=0 _N_=3
Name=キャロル Sex=女 Age=14 Height=62.8 Weight=102.5 _ERROR_=0 _N_=4
Name=ヘンリー Sex=男 Age=14 Height=63.5 Weight=102.5 _ERROR_=0 _N_=5
Name=ジェーム Sex=男 Age=12 Height=57.3 Weight=83 _ERROR_=0 _N_=6
Name=ジェーン Sex=女 Age=12 Height=59.8 Weight=84.5 _ERROR_=0 _N_=7
Name=ジャネッ Sex=女 Age=15 Height=62.5 Weight=112.5 _ERROR_=0 _N_=8
Name=ジェフリ Sex=男 Age=13 Height=62.5 Weight=84 _ERROR_=0 _N_=9
Name=ジョン Sex=男 Age=12 Height=59 Weight=99.5 _ERROR_=0 _N_=10
Name=ジョイス Sex=女 Age=11 Height=51.3 Weight=50.5 _ERROR_=0 _N_=11
Name=ジュディ Sex=女 Age=14 Height=64.3 Weight=90 _ERROR_=0 _N_=12
Name=ルイーズ Sex=女 Age=12 Height=56.3 Weight=77 _ERROR_=0 _N_=13
Name=メアリー Sex=女 Age=15 Height=66.5 Weight=112 _ERROR_=0 _N_=14
Name=フィリッ Sex=男 Age=16 Height=72 Weight=150 _ERROR_=0 _N_=15
Name=ロバート Sex=男 Age=12 Height=64.8 Weight=128 _ERROR_=0 _N_=16
Name=ロナルド Sex=男 Age=15 Height=67 Weight=133 _ERROR_=0 _N_=17
Name=トーマス Sex=男 Age=11 Height=57.5 Weight=85 _ERROR_=0 _N_=18
Name=ウィリア Sex=男 Age=15 Height=66.5 Weight=112 _ERROR_=0 _N_=19
NOTE: データセットTEST.CLASSから19オブザベーションを読み込みました。
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.03 秒
      CPU時間            0.03 秒
/* UTF8環境で実施 */
libname TEST "D:\temp" cvpmultiplier=1.5 ;
data _null_ ;
	set TEST.CLASS ;
	putlog _all_ ;
run ;
26   /* UTF8環境で実施 */
27   libname TEST "D:\temp" cvpmultiplier=1.5;
NOTE: ライブラリ参照名TESTを次のように割り当てました。
      エンジン: CVP
      物理名: D:\temp
28   data _null_ ;
29       set TEST.CLASS ;
NOTE: データファイルTEST.CLASS.DATAは別なホストにネイティブ
      な形式が使用されているか、またはエンコーディング
      がセッションエンコーディングと一致していません。
      クロス環境データアクセスが使用されるため、追加の
      CPUリソースが必要となり、パフォーマンスが低下しま
      す。
30       putlog _all_ ;
31   run ;

Name=アルフレッド Sex=男子 Age=14 Height=69 Weight=112.5 _ERROR_=0
_N_=1
Name=アリス Sex=女子 Age=13 Height=56.5 Weight=84 _ERROR_=0 _N_=2
Name=バーバラ Sex=女子 Age=13 Height=65.3 Weight=98 _ERROR_=0 _N_=3
Name=キャロル Sex=女子 Age=14 Height=62.8 Weight=102.5 _ERROR_=0 _N_=4
Name=ヘンリー Sex=男子 Age=14 Height=63.5 Weight=102.5 _ERROR_=0 _N_=5
Name=ジェームズ Sex=男子 Age=12 Height=57.3 Weight=83 _ERROR_=0 _N_=6
Name=ジェーン Sex=女子 Age=12 Height=59.8 Weight=84.5 _ERROR_=0 _N_=7
Name=ジャネット Sex=女子 Age=15 Height=62.5 Weight=112.5 _ERROR_=0
_N_=8
Name=ジェフリー Sex=男子 Age=13 Height=62.5 Weight=84 _ERROR_=0 _N_=9
Name=ジョン Sex=男子 Age=12 Height=59 Weight=99.5 _ERROR_=0 _N_=10
Name=ジョイス Sex=女子 Age=11 Height=51.3 Weight=50.5 _ERROR_=0 _N_=11
Name=ジュディー Sex=女子 Age=14 Height=64.3 Weight=90 _ERROR_=0 _N_=12
Name=ルイーズ Sex=女子 Age=12 Height=56.3 Weight=77 _ERROR_=0 _N_=13
Name=メアリー Sex=女子 Age=15 Height=66.5 Weight=112 _ERROR_=0 _N_=14
Name=フィリップ Sex=男子 Age=16 Height=72 Weight=150 _ERROR_=0 _N_=15
Name=ロバート Sex=男子 Age=12 Height=64.8 Weight=128 _ERROR_=0 _N_=16
Name=ロナルド Sex=男子 Age=15 Height=67 Weight=133 _ERROR_=0 _N_=17
Name=トーマス Sex=男子 Age=11 Height=57.5 Weight=85 _ERROR_=0 _N_=18
Name=ウィリアム Sex=男子 Age=15 Height=66.5 Weight=112 _ERROR_=0 _N_=19
NOTE: データセットTEST.CLASSから19オブザベーションを読み込
      みました。
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.11 秒
      CPU時間            0.07 秒


注意点としては、「元データセットを更新する訳ではないですが」、「定義から1.5倍された状態で見えます」。
例えば、SJISで長さ12バイトとして定義されていたデータセットは、cvpmultiplier=1.5で、18バイト元からあったように見えます。

上記指定でもダメな所・・・・・・フォーマットが悪さをする。

CVPオプションは、文字列変数の「変数長に対してのみ効果が出ます」。
さて。
最近、しばしば見かけるデータセットでは、何故かわざわざ文字変数に同じ長さの文字フォーマットを設定している事があります。
Enterprise GuideでExcelファイルを取り込んだりするとそういうのが出来てたりします。
そういったフォーマットが付けられている場合に、自動でフォーマット長も設定されてたりするんですね。その場合には、「フォーマットの長さは変更されません」。
そういう場合には、フォーマットの設定を外して下さい。「format _all_ ;」で消えます。が、まあ、WORKなど現在の環境に複写してUTF8に変換された状態でないとダメです(元データは違うエンコーディングで、更新は出来ません)。
SDTMとか扱っている場合には、そもそも変数にフォーマット付けない方がいいです。XPORTファイル、フォーマットカタログに対応していないですし。

ちゃんと、データセットを使用環境のエンコーディングに変換した方がよい。

上記のようなトラブル防止の為、データセットそのものをUTF8に変換しておいた方がよいんですよね・・・・・・
でも、その場合、例え全ての文字列がASCIIで構成された場合であっても、なんにも判断せず拡張されるんですよね。
つまり、文字変数の変数長は必ず1.5倍になってしまうのです。
間抜けもいいところですよね。


この辺りで、例えば、最初1.5倍しておいて、実データの最大長に縮小する、というようなプログラムは作れない事はないのですが、そんなプログラム作ると時間が無駄に掛かるし大変なんですよね・・・・・・。

XPORTファイルとは何ぞやというお話。

何だか騒がしくなってきた所ですが(超白々しい)、最近話題のSAS transport fileというのを簡単にご説明させて頂きます。

マニュアル(日本語)

SAS 9.4 ファイルの移動とアクセス(PDF)をご参照下さい。
もうそこに書いてる事です。


全ての人に言います。
100回めぐらいですが。
マニュアル嫁。

要諦

プラットフォーム間の移行と、文字エンコーディングの変更は別。

XPORTファイルを送る場合には、送り先と受け先の文字エンコーディングが一致していることが前提なんです。
大事な事なので二回言いますが、XPORTファイルには文字エンコーディングの情報は入ってません。
XPORTファイルは、しかし、それ以外のケース、例えばOSの違いは吸収します。bit数の違いは、数値変数に設定した長さでのIBM形式であると定義されているので差になりません。


で。
そもそもで言うと、SAS XPORT形式は、ASCII以外の文字列を特にサポートはしてないのです。
それどころか、もっと低レベルな範囲でのサポートにとどまります。


蛇足ですが、移送形式なので、プラットフォームが違っても同一の形式になります。
プラットフォームに依存する問題はこの形式では発生しません。

XPORT形式

実はV8のXPORT形式というのも出てきており、V5形式の拡張ながらちょっとはマシになってます。
けど、FDAが言っているのは、SAS V5 transport file formatですので、それの制約がとっても重くのしかかってます。のしかかりすぎです。二十年前に辞めたかったような制約がそのまんま課せられてます。


データセット名・変数名の最大長は8バイトとか、ラベルも40バイトとか、フォーマット定義名も8文字以下とか(文字フォーマットの場合には$も入るので7文字まで)、かなりの制約があります。ちなみにカタログファイルは転送出来ないので、ユーザ定義フォーマットとか禁止。


つまりは、SAS V6ぐらいでも使えるようなデータの形式であるという事です。


ちなみに、文字エンコーディングはサポートされてはないのですが、同じ文字エンコーディングである場合には同じ文字エンコーディングで出力されるし読めるという感じです。

注意

今、SASはこのTransport file formatに類似のV8フォーマットを持ってます。
そちらへの変換SASマクロは提示されてますが、XPORTエンジンはV5のみサポートしていますし、XPORTのファイルというとV5制限がつくことと想定して下さい。
文字化けだーとか言う前に、文字エンコーディングを気にして下さい。
お願いします。

VMWareが面倒臭くなったのでVirtualBoxを試す

VirtualBoxが気にはなってたけど、今までWin7でインストール出来ずに四苦八苦していたけど、とりあえずインストールする方法が見つかったので乗り換えてみる。

Virtual Boxのインストールの要諦。

Windows 7 Professionalではなんでかx32版をインストーラーがインストールしようとしてコケてるようで。
その為、インストーラーからmsiを抽出してから実行してみた。

引数は-extractで。
%temp%\VirtualBoxに展開されるので、そこからAMDx64を選択してインストール。

右側コントロールキーなんて使った事がなかったので手がつる。

左側の方が幸せかもしんない。

SASのHELPが日本語化されると自分の仕事もちょっとは減ると思ったのだけど、未だに減らない件

おかしい。
あと、問題を難しく解こうとする人が多過ぎる。
SASは問題をSQL的に解くことも出来るし、関数使って解くことも出来る。が、得意ではない。
また、使えるのがいいとも限らない。完全外部結合を扱えるSQLだが、そんな結合はプログラム上使うシーンがあるのは設計がおかしい。
特殊欠損値は印刷前以外は使わない方が良い。番兵は仕様書でも伝わりにくいバッドノウハウだ。
結合キーが複雑になり過ぎていたら、結合用のキーを別途考えるべきだし、フラグを計算で作るのも良いだろう。プログラム上でワンラインで書けても実行上複雑になり過ぎていたら結局の所遅いのだし。

EXPORTプロシージャを使わないCSVの出力方法。

EXPORTプロシージャを使ってCSVを出力するのも間違いではないが、EXPORTプロシージャ内でも同様のSASプログラムを生成しているので、それに合わせて簡易なプログラムを作ってみた。
サンプルであり、マクロ化はしていないけど、マクロ化も難しい話ではない。


サンプルは、ヘッダー行にラベルを出力する仕様で、細かいながら、デリミタは「,」、DSDオプションでの出力。

options symbolgen ;
options source source2 ;

%let outputfilepath = %sysfunc(sysget(TEMP))\class.csv ;
%let targetdataset = SASHELP.CLASS ;
%let temporarydataset = WORK._WK_DEF ;


%let _wk_sasfile = %sysfunc(pathname(WORK))\temp.sas ;
%let _wk_dlm = ',' ;


proc contents data=&targetdataset. noprint out=&temporarydataset. ;
quit ;

proc sort data=&temporarydataset. ;
	by MEMNAME VARNUM ;
quit ;

data &temporarydataset. ;
	set &temporarydataset. END=_LAST ;
	file %sysfunc(quote(%superq(outputfilepath))) lrecl=32767 dsd dlm=&_wk_dlm. ;
	attrib _WK_HEADER length=$300. ;
	if not missing(LABEL) then _WK_HEADER = LABEL ;
	else _WK_HEADER = NAME ;
	put _WK_HEADER @@ ;
run ;

data _null_ ;
	set &temporarydataset. ;
	file %sysfunc(quote(%superq(_wk_sasfile))) lrecl=1000 ;
	attrib _TMP length=$1000. ;
	by MEMNAME VARNUM ;
	if first.MEMNAME then do ;
		_TMP = "data _null_ ;" ;
		put _TMP ;
		_TMP = catx(" ","set",symget('targetdataset'),";") ;
		put +4 _TMP ;
		_TMP = catx(" ","file",quote(symget(cats('outputfilepath'))),"mod lrecl=32767 dlm=&_wk_dlm. dsd ;") ;
		put +4 _TMP ;
		_TMP = "put" ;
		put +4_TMP ;
	end ;
	put +8 NAME ;
	if last.MEMNAME then do ;
		_TMP = ";" ;
		put +4_TMP ;
		_TMP = "run ;" ;
		put _TMP ;
	end ;
run ;

%include %sysfunc(quote(%superq(_wk_sasfile))) ;

proc sql ;
	drop table &temporarydataset. ;
quit ;

倍精度浮動小数点型の限界はあるよ、という話。

データステップ100万回      SAS新手一生: マクロ変数に数値をいれて戻すと誤差がでちゃう場合がある問題について考える話を見て。


しばしば、お客さんに説明しても理解してもらえないので超絶面倒臭い話の周りになるんですがね。


確かぼんやりとした記憶ですが、浜田先生のSASの本で、対応ありのt検定をするのにunivariateプロシージャを使っていたのですが(多分SAS6ぐらいの世界の話です)、そこでround関数を使っていたかと思います。
ちなみに、SASV9V6ではround関数の実装にバグがあって、%mround使いましょうねーというお話もありました。

倍精度浮動小数点型の限界。

data _null_ ;
	a = 1.23456789012345678 ;
	put a best32. ;
run ;
                1.23456789012345

SASのマニュアルにも記述はありますが、IEEEで定められた倍精度浮動小数点型の場合、表現に限界があります。
有効数字だいたい15桁ぐらいしか表現出来ません。十分という話もあるんですが。


もうひとつは、この倍精度小数点型、内部表現(計算機内部で保持されている形式)と表示が、1:1の対応にならないんですね。
特に精度が問題になってくる時はそうです。
IEEE 754 - Wikipedia



数値でデフォルトで使われるフォーマットがbest12.なのもその辺りが理由かと思います。

故に。

2進数表現だと、完全に一致させる事は出来ます。
だから何だとか言われても困りますが。


もうひとつ。
基準とするような値、例えば0.005などと大小比較する際には、等号を使わないなど考えて下さい。引き算してfuzz通すとかして下さい。
古いプロシージャでは計算機イプシロンとかが1e-8のまんまだったりするのですが、オプションで変えられるものもあります。だいたい1e-12辺りが他のプロシージャの精度とも合います。
また、情報落ち、桁落ちは、基本的に数字が小さい方に出ます。
で、オマケですが、この辺り、SAS9.4のヘルプをチマチマ確認してたのですが、バグが残ってます。

892  data _null_ ;
893      a = 0.5-0.4 ;
894      b = 0.4-0.3 ;
895      put 'a  :' a ' b  :' b ;
896      put 'a  :' a hex16. ' b  :' b hex16. ;
897      if a = b then put 'OK' ;
898      else put 'ERROR' ;
899      a2 = round(a,1e-12) ;
900      b2 = round(b,1e-12) ;
901      put 'a2 :' a2 ' b2 :' b2 ;
902      put 'a2 :' a2 hex16. ' b2 :' b2 hex16. ;
903      if a2 = b2 then put 'OK' ;
904      else put 'ERROR' ;
905  run ;

a  :0.1  b  :0.1
a  :3FB9999999999998 b  :3FB999999999999C
ERROR
a2 :0.1  b2 :0.1
a2 :3FB999999999999A b2 :3FB999999999999A
OK
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.00 秒

ちなみに。

統計プロシージャでは、内部的に数字をどう扱っているかというと、一部十進計算していたりするようです。私が、実行時の機械語を見た訳ではなく、あくまでも伝聞なのですが。
dataステップで要約統計量を出すと、univariateプロシージャと計算が合わなかったりするのは、それ故に仕方ないです。

SASプログラムを特定ファイルに展開して実行する方法

前のエントリのコメント欄で希望のあった、

call executeについても、わかりにくいですね。putでコード生成して、%includeで実行って方法、聞いたことはありましたが、まだ自分でやったことはありませんでした。ぜひコード例を紹介していただけたら嬉しいです。

SASマクロプログラムの要諦 - The Nameless City

の話。

生成されるプログラムを予めテキストファイルに出力しておき、それを%includeで読み込む。

話としては全く難しくはない。


データセットの中身をファイルに出力する方法は幾つかあるが、
そのウチの一つに、dataステップ内でfileステートメントで出力先を設定し、putで出力する方法がある。

書き出し。

通常、putステートメントは、Log出力に指定した内容あるいは中身を出力する。

data _null_ ;
    put 'Hello! World' ;
run ;
1    data _null_ ;
2        put 'Hello! World' ;
3    run ;

Hello! World
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.14 秒
      CPU時間            0.01 秒


「何も指定していない場合には」。
つまりは、指定する事によって、出力先は変更可能である(この、変更可能である、という所が要諦の一つになる。カンのいい人は、「ストリーム出力だ!」とか、「じゃ、出力デバイス指定出きんじゃね?」とか考えれば、回答が直ぐに思いつくだろう)。

ata _null_ ;
    file "%sysfunc(sysget(temp))\test.txt" lrecl=32767;
    a='Hello! World' ;
    put a a ;
run ;

fileステートメントは、まあ使った事のない人は幾らかいると思うが、infileステートメントの逆だと考えてればいい。通常はテキストファイルを決まった形式で書き出す、ぐらいで考えて使えば良い。
fileステートメントの細かいオプションはヘルプ参照の事、だけど、今回のプログラム書き出すパターンではほぼほぼlrecl=32767の拡張ぐらいしか使わない(逆にこれは設定しておくのが無難)。
細かいながら、putステートメントでの注意点を一つ。このステートメント内には、リテラルか変数ぐらいしか指定できない(変数に付随するフォーマットは設定出来るが)。また、文字列リテラルを2つ並べても、一つの文字列として扱われる(例えば「put 'Hello! World' 'Hello! World' ;」としても、出力では「Hello! WorldHello! World」になる)。

NOTE: 出力ファイル"C:\Users\~~~\AppData\Local\Temp\test.txt" :
      ファイル名=C:\Users\~~~\AppData\Local\Temp\test.txt,
      レコードフォーマット=V,論理レコード長=32767,
      ファイルサイズ (バイト)=0,
      更新日時=2014年12月02日 23時23分18秒,
      作成日時=2014年12月02日 23時04分04秒

NOTE: 1レコードを出力ファイル"C:\Users\~~~\AppData\Local\Temp\test.txt"に書き込みました・
      最小レコード長は25です。
      最大レコード長は25です。
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.09 秒
      CPU時間            0.09 秒

Hello! World Hello! World

(デフォルトの区切り文字は半角空白)


デバック時には、

data _null_ ;
file log ;
a='Hello! World' ;
put a a ;
run ;

(ログ出力)

data _null_ ;
file log ;
a='Hello! World' ;
put a a ;
run ;

(標準出力)

読み込み。

%include、以上、なのだが、まあ注意点としては、
「実行プログラムとして読み込まれる」のと、「オープンコードとして読み込まれる」という事ぐらいかなあ。
例えばこれがマクロ内で読み込みされてもそのマクロ内で生成されたSASマクロ変数なんかは参照出来ない。*1

options source2 ;
ods listing ;


filename _RSAS "%sysfunc(pathname(WORK))/temp.sas" lrecl=32767 ;
data _null_ ;
	attrib TMPSTR length=$1000. ;
	file _RSAS ;
	TMPSTR = 'proc datasets ' ;
	put TMPSTR ;
	TMPSTR = 'nolist' ;
	put +4 TMPSTR ;
	TMPSTR = ';' ;
	put TMPSTR ;
	TMPSTR = 'contents' ;
	put +4 TMPSTR ;
	TMPSTR = 'data=sashelp.iris' ;
	put +8 TMPSTR ;
	TMPSTR = 'varnum' ;
	put +8 TMPSTR ;
	TMPSTR = ';' ;
	put +4 TMPSTR ;
	TMPSTR = 'quit ;' ;
	put TMPSTR ;
run ;

%include _RSAS ;


ods listing close ;
408
409  options source2 ;
410  ods listing ;
411
412
413  filename _RSAS "%sysfunc(pathname(WORK))/temp.sas" lrecl=32767 ;
414  data _null_ ;
415      attrib TMPSTR length=$1000. ;
416      file _RSAS ;
417      TMPSTR = 'proc datasets ' ;
418      put TMPSTR ;
419      TMPSTR = 'nolist' ;
420      put +4 TMPSTR ;
421      TMPSTR = ';' ;
422      put TMPSTR ;
423      TMPSTR = 'contents' ;
424      put +4 TMPSTR ;
425      TMPSTR = 'data=sashelp.iris' ;
426      put +8 TMPSTR ;
427      TMPSTR = 'varnum' ;
428      put +8 TMPSTR ;
429      TMPSTR = ';' ;
430      put +4 TMPSTR ;
431      TMPSTR = 'quit ;' ;
432      put TMPSTR ;
433  run ;

NOTE: 出力ファイル_RSAS :
      ファイル名=C:\Users\~~~\AppData\Local\Temp\SAS Temporary Files\_TD8700_~~~_\temp.sas,
      レコードフォーマット=V,論理レコード長=32767,
      ファイルサイズ (バイト)=0,
      更新日時=2014年12月03日 00時45分22秒,
      作成日時=2014年12月03日 00時26分12秒

NOTE: 8レコードを出力ファイル_RSASに書き込みました。
      最小レコード長は1です。
      最大レコード長は25です。
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.03 秒
      CPU時間            0.01 秒


434
435  %include _RSAS ;
NOTE: %INCLUDE (レベル1)ファイル_RSASはファイルC:\Users\~~~\AppData\Local\Temp\SAS Temporary Files\_TD8700_~~~_\temp.sasです。
436 +proc datasets
NOTE: HTML Bodyファイルの書き込み先: sashtml4.htm
437 +    nolist
438 +;
439 +    contents
440 +        data=sashelp.iris
441 +        varnum
442 +    ;
443 +quit ;

NOTE: PROCEDURE DATASETS処理(合計処理時間):
      処理時間           0.89 秒
      CPU時間            0.78 秒


NOTE: %INCLUDE (レベル1)を終了します。
444
445
446  ods listing close ;
                                            SAS システム                            2014年12月 2日

                                        DATASETS プロシジャ

         データセット名      SASHELP.IRIS                 オブザベーション数            150
         メンバータイプ      DATA                         変数の数                      5
         エンジン            V9                           インデックス数                0
         作成日時            2013/06/20 14:38:09          オブザベーションのバッファ長  48
         更新日時            2013/06/20 14:38:09          削除済みオブザベーション数    0
         保護                                             圧縮済み                      NO
         データセットタイプ                               ソート済み                    YES
         ラベル              Fisher's Iris Data (1936)
         データ表現          WINDOWS_64
         エンコード          us-ascii  ASCII (ANSI)


                                      エンジン/ホスト関連情報

 データセットのページサイズ  65536
 データセットのページ数      1
 データページの先頭          1
 ページごとの最大OBS数       1361
 先頭ページのOBS数           150
 データセットの修復数        0
 ExtendObsCounter            YES
 ファイル名                  C:\Program Files\SASHome\SASFoundation\9.4\core\sashelp\iris.sas7bdat
 作成したリリース            9.0401B0
 作成したホスト              X64_7PRO


                                            作成順の変数

                      #    変数           タイプ    長さ    ラベル

                      1    Species        文字        10    Iris Species
                      2    SepalLength    数値         8    Sepal Length (mm)
                      3    SepalWidth     数値         8    Sepal Width (mm)
                      4    PetalLength    数値         8    Petal Length (mm)
                      5    PetalWidth     数値         8    Petal Width (mm)


                                            ソート情報

                                      ソート順        Species
                                      バリデート済み  YES
                                      文字セット      ANSI


補足説明。
「options source2」は、読み込んだプログラムソースをログに出力するシステムオプション。
dataステップ内でのfileステートメントに一気にpathも含めてファイルを設定出来るが、どうせ後で読み込むのでfilenameステートメントで設定しておいた。
putステートメントの後に、+4などと書くことで適当にインデント設定して書き出し出来るが、ほぼ趣味である。
また、filenameステートメントで、ファイルのpathを記載する代わりに「TEMP」というキーワードでWORKライブラリ内に一時書き出しファイルを作成する事が出来る(#LNxxxxなどという名前でファイルの名前も自動設定される)が、ここでは敢えてしていない(しない方が便利な事もある)。

実際に使う場面。

無論、このサンプルは、単に「出来る!」というものでしかないが、dictionaryテーブルやcontentsプロシージャのデータセット出力と組み合わせる事で出来る事は増える。

*1:ただ、SASマクロ変数は、他の言語での、スコープがある変数と同様に扱ってはいけない、基本的には古い言語と同じくグローバル領域の変数になっちゃうので、元からSASマクロ変数に頼るプログラムにするのはオススメしない)(じゃあどうするの?というと、「WORKライブラリ配下に情報伝える為のデータセットを置いておく」というやり方でよく対応する。

SAS On CentOS手順作成

やってると本当に誰得な感じになっております(現在進行形)。
クライアントHyper-Vの取り扱いがスゲー面倒臭い。


ああ、個人的には、SAS Serverを作るなら、Windowsをオススメします。
Cent OSが面倒とかそういう話ではなくて、RHELとかよりはずっとWindowsの方が楽だしという所です。

SASマクロプログラムの要諦

ネット見てると、SASプログラマーも数えるほどだけどそれなりにいるという事で、昔に比べて色々あるんだけど。

SASマクロ関数でのクォート処理は面倒な部類に入るのでマクロ言語でやるよりdataステップでマクロ変数を読み込む方法とかを使う方が幸せになれますよ、という話。

データステップ100万回      SAS新手一生: シングルコーテーション内のマクロ変数を展開する
SASマクロをウンザリするほど使ってきたが、未だにクォート処理周りは困る事がある。
クォート処理は、SASの内部では特殊な文字を割り当てられている事があり、それがSASマクロ変数展開時に解除されてない為に、SASマクロ変数を使った文字列の結合なんかで困ったりすることがある。


というか、クォート処理みたいなのをSASマクロのプレコンパイルの所で処理してしまうのは、使い勝手の点で問題があるので、最近自分の場合には大抵こうしてる。

%let M=いろは;

data A;
X=%unquote(%bquote('&M'));
run;

%let M=いろは;

data A;
X=symget('M');
run;

同様に、最近は%sysfuncとか殆ど使わなくなった。%sysfunc(exist(~))は別口だけど。
SASマクロのプレコンパイル領域では使える関数がマニアックになりがちなのと、debugが難しくなりがちという欠点があるので、dataステップでやると楽。


自分は、この手の問題では、しばしば、data _null_を使う。
例えば、データセットのOBS数を取り出すのに、

%let _N_OBS = 0 ;
data _null_ ;
if 0 then set DS NOBS=_N_OBS ;
cal symputx('_N_OBS',_N_OBS) ;
stop ;
run ;
%put &_N_OBS. ;

とか、

%let _TIMESTAMP = ;
data _null_ ;
_TMP = put(datetime(),E8601DT.) ;
_TMP = tranwrd(_TMP,"-","") ;
_TMP = tranwrd(_TMP,"T","") ;
_TMP = tranwrd(_TMP,":","") ;
call symputx('_TIMESTAMP',) ;
run ;
%put &_TIMESTAMP. ;

とか。

インライン型のマクロを作らない方が幸せになれる。

%macro Inf( Ds=, Var=Name, Row=11 );
data _null_;
set &Ds(firstobs=&Row obs=&Row);
call symputx('MVar', &Var);
run;
%mend;

%Inf( Ds=Sashelp.Class, Var=Name, Row=11 );
%put &MVar;

とすれば、指定データセットDs・変数Varの第Row行目の値をマクロ変数MVarに代入することができます(MVar=ジョイス)。

そこで、

%let MVar=%Inf( Ds=Sashelp.Class, Var=Name, Row=11 );

としたときに、MVarがジョイスとなるようなマクロ%infは多分できないと思いますが、可能でしたらお教えください。

SASプログラミング掲示板(データステップ100万回)|指定行・変数のマクロ化|トピック表示 > スレッドNo.16

回答者が、RC関数とか使っているが、バグってclose関数呼ばれないと大変面倒臭い事になるのでRC関数とかは全然オススメしない。
インライン型のマクロ関数というのは、結構、エラーになった時に何になるか分からないSASマクロの更に深みにハマる事があるので、そんな事するぐらいなら、素直に構造自体を内部に入れて、普通のマクロにした方が幸せになれる。大抵。時々セミコロン抜けたりするし。

%macro Inf( Ds=, Var=Name, Row=11, Mvar=Mvar);
data _null_;
set &Ds(firstobs=&Row obs=&Row);
call symputx(symget('Mvar'), &Var,'G');
run;
%mend;

%Inf( Ds=Sashelp.Class, Var=Name, Row=11, Mvar=Mvar );

グローバルスコープでのマクロ変数に格納する形になってしまっているが、予めMvarを宣言して格納させる形にするとかなら、逆にマクロ変数がない場合には実行しないようにする方法もある。
ここらへんの「できたらいい」話ってのは結局の所あんまり凝っても仕方ないが。

似たような話で、call executeは最近あんま使ってない。

WORKライブラリの中にSASプログラムファイルを吐き出して%includeして使うとかそんな事をよくしてる。デバッグ楽だし。

何故、統計解析という言葉で、何か大それた事をしているようになるんだろう。

Big Data Analysisが悪いというわけじゃないんだが。
いい加減、どんな手法使うかってより、一体何を考慮したかの方が重視されないものかな。


変な話、

y=f(x1,x2,x3,x4,x5,x6....)

という数式を近似解で解いてくやり方なのよね、統計って。


頼むから、統計手法だけ聞きかじって来ないで。
もう説明が面倒臭いので。

現時点の予定(追加分)

FDAのサイト読解

面倒だが結局読む必要あるかー。

社内向けSDTM開設

この手のスライド資料は、いつかメモ書きから清書する、だけで作れるような仕組みにしたいなあ。

TerminologyのPersonal Dictionary化

Terminologyの取り込み、管理、辞書展開というのまで見据えたい。

現時点の今後の予定。

現時点での今後の予定は以下。

Terminologyの構造化

現時点で提供されているTerminologyは、正直色々な所で使い勝手に難があるので、改造がてら、出来れば、更新通知の機械化等も含め考えたい。

ODM XML解読

しかし、なんかelement定義が中途半端にXMLな感じ。