The Nameless City

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

今更ながら、Windowsのファイル文字列の取り扱いについて。

IBM Windows のファイル名の文字コードについて - Japan
があったので、大変有り難い。


ほぼそれの内容なのだけど。


いわゆるUTF-16LEでNTFSでは登録されているのだが、ファイルシステムが違う場合、特にFATとかでは日本標準ではS-JISに変換されるよう。
で、このファイルパスがUTF-16LEであるのにも関わらず、各アプリケーションではS-JISとして使われる。

(5) Windows 上で稼動するアプリケーションには、Windows カーネルSJIS に変換して渡す

IBM Windows のファイル名の文字コードについて - Japan

これは、必ずという訳ではなく、APIを経由すれば呼び出しも可能のようなのだけど、Windowsの幾つかのものがそもそもそういうのにマトモに対応していない。


WindowsのPIPE機能をSASで使う時などでモロに引っかかると思われる。

SASインストールに関するTip幾つか。

多くの人が、「インストーラーに従ってインストールすれば問題ないよね?」的な発想でインストールされると思います。


特に問題がなければそれでだいたいは大丈夫です。間違ってません。
ただ、たまーに問題が発生するのですが、そこら辺を自力解決というには、ちとインストーラーも含めて親切になり過ぎてます。


まあ、滅多に発生はしないのですが、発生すると「ああ、そういや昔からこんなトラブルあったなあ」と古い人は懐かしく思うんじゃないでしょうか。

前提として

SAS英語圏、頑張ってもLatin-1のあんまり拡張使わない領域で構築されてます。
それ故に、英語圏パッケージでありがちな間違いをよくしています。
そこら辺のトラブルを避けるなら、まずその辺りを考慮し、ダブルバイト文字をとにかく混入させないようにしてください。
また、インストール前の処置は重要です。アンチウィルスソフト停止、はまだいいんですけど、それからSASデータ実行防止の例外に登録しておくことも必要です。

ユーザ名に日本語が入っていると、インストーラーが「インストールの後処理」でコケる。

インストールの後処理で、sasv9.cfg周りの設定をやっているように思うのですが、その際にコケる事があります。
今、Windows8.1ではoutlookアカウントに紐付ける形でアカウントを最初に作る事が多々あり、そのアカウント(管理者権限持ち)でインストールすると、SASバッチで何かしている時にコケてるようです。


Windowsの場合には、大概Administratorが使えるのでそれで実行するのが最善ですが、そうでない場合には、インストール用の英文字だけで構成されたユーザを作成してインストールして下さい。

OQツール実施で、不特定な箇所でFailが発生する。

よく画面眺めていると、データ実行防止機能でsas.exeを止めたと、タスクトレイにポップアップで表示されます。
これは、64bit版SASではなく32bit版SAS限定で発生します。イベントログにはたいていOffice IMEに対してsas.exeが不正アクセスっぽい事してると警告が上がります。
データ実行防止機能の例外に登録してください。

OQツールで、正常にインストールできたのにFailが出る。

レポートにsastestのプログラムが出ているので、それをデフォルトで起動するSAS DMS上で再実行して見て下さい。多分たいていはSASの日本語版が起動すると思います。
proc compareで間抜けにも、日本語の文字と英語の文字を比較している結果が表示されてるかと思います。原因はそれです。

setup.exeを実行し、言語の選択をした後に起動するまでに時間が異様にかかる。

たいていは、アンチウィルスソフトが悪さをしています。インストール中には実行を停止してみて下さい。
Analytics Proでトータル三時間もあればインストール出来るのですが、これを知らなくて辛抱強く待つ人もいます。出来なくもないですが、びっくりするぐらい時間がかかるのでこちら推奨です。

他人の性能に振り回されない為の、大局観。

gin-kei.hatenablog.com
見て。


まあ、タイトル通りですが。
他人の能力というのは過信してはならず、予定通りに進んでなさげな場合には、マイルストーンを適切に作らせるとか、そういう事をしていかなければならない、という気がします。


ちなみに、定型的にミスが発生する場合には、チェックリストなどを作成してもらい、それに従ったチェックをさせるとか、そういう事をしていかないと、レビューアーが増えません。


まあありがちといえばありがちなんですが、社会人の心得とか意識とかに任せるといつまでもそこら辺に忙殺されるので、体系化してフローに組み込んでしまうというのがいいですねん。

p値はnに影響を受けるものなので。

b.hatena.ne.jp
で、ブコメでもちょっとコメントしてますが。

このサンプルサイズでは、検定をしても仕方がない。

まあ、A/Bでそれぞれn=10000みたいな状況の場合には、統計手法の選択もクソもなく、ほぼほぼ元々の母集団での比率に差があればpは0に近くなります。
計算するだけ時間の無駄です。比率出して比較すればいいだけです。


ちなみに、SASのコードで書くとこんな感じですが。

data test ;
	attrib pattern length=$1. ;
	attrib cv length=8 ;
	attrib imp length=8 ;
	input pattern cv imp ;
	cards ;
a 30097 100399
b 28901 99896
; run ;

data test2 ;
	set test ;
	attrib event length=$4. ;
	attrib uu length=8 ;
	event = 'cv' ;
	uu = cv ;
	output ;
	event = 'miss' ;
	uu = imp-cv ;
	output ;
run ;

proc freq data=test2 ;
	table pattern * event /cmh ;
	weight uu ;
quit ;
                                    FREQ プロシジャ

                                 表 : pattern * event

                       pattern        event

                       度数          |
                       パーセント    |
                       行のパーセント|
                       列のパーセント|cv      |miss    |   合計
                       --------------+--------+--------+
                       a             |  30097 |  70302 | 100399
                                     |  15.03 |  35.10 |  50.13
                                     |  29.98 |  70.02 |
                                     |  51.01 |  49.75 |
                       --------------+--------+--------+
                       b             |  28901 |  70995 |  99896
                                     |  14.43 |  35.45 |  49.87
                                     |  28.93 |  71.07 |
                                     |  48.99 |  50.25 |
                       --------------+--------+--------+
                       合計             58998   141297   200295
                                        29.46    70.54   100.00


                             pattern * event の要約統計量

               Cochran-Mantel-Haenszel 統計量 (テーブルスコアに基づく)

           統計量    対立仮説                  自由度          値      p 値
           ----------------------------------------------------------------
             1       相関統計量                     1     26.3809    <.0001
             2       ANOVA 統計量                   1     26.3809    <.0001
             3       一般関連統計量                 1     26.3809    <.0001


                               共通オッズ比と相対リスク

    統計量               手法                       値                95% 信頼限界
    ------------------------------------------------------------------------------
    オッズ比             Mantel-Haenszel        1.0516        1.0316        1.0721
                         ロジット               1.0516        1.0316        1.0721

    相対リスク (列 1)    Mantel-Haenszel        1.0362        1.0222        1.0503
                         ロジット               1.0362        1.0222        1.0503

    相対リスク (列 2)    Mantel-Haenszel        0.9853        0.9797        0.9909
                         ロジット               0.9853        0.9797        0.9909


                               標本サイズの合計 = 200295

このモデルをそのまんま使って、ただし、

a 30097 100399
b 28901 99896

a 30 100
b 29 100

a 300 1004
b 289 999

で同じように検定してみましょう。
(見ればわかると思いますが、サンプルサイズを元の千分の一、百分の一という感じにしてます)

                                  FREQ プロシジャ

                               表 : pattern * event

                     pattern        event

                     度数          |
                     パーセント    |
                     行のパーセント|
                     列のパーセント|cv      |miss    |   合計
                     --------------+--------+--------+
                     a             |     30 |     70 |    100
                                   |  15.00 |  35.00 |  50.00
                                   |  30.00 |  70.00 |
                                   |  50.85 |  49.65 |
                     --------------+--------+--------+
                     b             |     29 |     71 |    100
                                   |  14.50 |  35.50 |  50.00
                                   |  29.00 |  71.00 |
                                   |  49.15 |  50.35 |
                     --------------+--------+--------+
                     合計                59      141      200
                                      29.50    70.50   100.00


                           pattern * event の要約統計量

             Cochran-Mantel-Haenszel 統計量 (テーブルスコアに基づく)

         統計量    対立仮説                  自由度          値      p 値
         ----------------------------------------------------------------
           1       相関統計量                     1      0.0239    0.8771
           2       ANOVA 統計量                   1      0.0239    0.8771
           3       一般関連統計量                 1      0.0239    0.8771


                             共通オッズ比と相対リスク

  統計量               手法                       値                95% 信頼限界
  ------------------------------------------------------------------------------
  オッズ比             Mantel-Haenszel        1.0493        0.5713        1.9270
                       ロジット               1.0493        0.5713        1.9270

  相対リスク (列 1)    Mantel-Haenszel        1.0345        0.6739        1.5880
                       ロジット               1.0345        0.6739        1.5880

  相対リスク (列 2)    Mantel-Haenszel        0.9859        0.8241        1.1795
                       ロジット               0.9859        0.8241        1.1795


                              標本サイズの合計 = 200
                                    FREQ プロシジャ

                                 表 : pattern * event

                       pattern        event

                       度数          |
                       パーセント    |
                       行のパーセント|
                       列のパーセント|cv      |miss    |   合計
                       --------------+--------+--------+
                       a             |    300 |    704 |   1004
                                     |  14.98 |  35.15 |  50.12
                                     |  29.88 |  70.12 |
                                     |  50.93 |  49.79 |
                       --------------+--------+--------+
                       b             |    289 |    710 |    999
                                     |  14.43 |  35.45 |  49.88
                                     |  28.93 |  71.07 |
                                     |  49.07 |  50.21 |
                       --------------+--------+--------+
                       合計               589     1414     2003
                                        29.41    70.59   100.00


                             pattern * event の要約統計量

               Cochran-Mantel-Haenszel 統計量 (テーブルスコアに基づく)

           統計量    対立仮説                  自由度          値      p 値
           ----------------------------------------------------------------
             1       相関統計量                     1      0.2183    0.6403
             2       ANOVA 統計量                   1      0.2183    0.6403
             3       一般関連統計量                 1      0.2183    0.6403


                               共通オッズ比と相対リスク

    統計量               手法                       値                95% 信頼限界
    ------------------------------------------------------------------------------
    オッズ比             Mantel-Haenszel        1.0469        0.8638        1.2688
                         ロジット               1.0469        0.8638        1.2688

    相対リスク (列 1)    Mantel-Haenszel        1.0329        0.9018        1.1831
                         ロジット               1.0329        0.9018        1.1831

    相対リスク (列 2)    Mantel-Haenszel        0.9866        0.9324        1.0440
                         ロジット               0.9866        0.9324        1.0440


                                標本サイズの合計 = 2003

という感じで、「同傾向のデータでも増やすとより有意を見つけやすくなります」。


元の話に戻って考えてみればいいのですが、

  • 「Aだと10万人来てコンバージョンしてくれた(?)のが3万人」
  • 「Bだと10万人来てコンバージョンしてくれた(?)のが2万9千人」

というぐらいな話なのです。
この3万/10万と2万9千/10万で表されるような「サンプルでの比率」は「母集団の比率の推定値」になるわけなんですが、そうした時に「母集団でも確かにこの比率の差が発生する」という事を仮説として考え、その対立仮説「母集団ではこの比率の差がない」の確率を求めているというのがこのp値です。
まあ、例えば「想定される母集団が例えば10万人程度だったら」、ほぼ全例調査なわけで、そりゃその1千人の差は確かでしょうし、

  • 「Aだと100人来てコンバージョンしてくれた(?)のが30人」
  • 「Bだと100人来てコンバージョンしてくれた(?)のが29人」

だと、先ほどの話よりもサンプルが偶然そうなってただけの可能性があるわけです。


そもそもこんな例数で検定かけるとかwというものです。

連続性の補正。

"2-sample test for equality of proportions with continuity correction" は 2 郡の比率が等しいかどうかを検定したことを意味しています。その際、連続性の補正 (continuity correction) を行った旨が表示されていますが、連続性の補正の有無で経営判断が変わるほど結果に影響を及ぼすことはまずないと思われるので気にしなくて大丈夫です。

A/B テストで施策の効果を検証!エンジニアのための R 入門 - クックパッド開発者ブログ

連続性の補正というのは、カイ二乗検定は、この分布が連続した関数であるカイ二乗分布に従うという仮定で行うのですが、実際には人が0.1人とかそんな分離の仕方しないので、その補正の為に行うものです。
で。
これだけ例数が多いと、一人程度での不連続はあんま意識しなくていいので補正は入れなくてもいいでしょう。


そこら辺の丁寧な説明は
Fisherの正確検定かカイ2乗検定か
とかにあります。
ちなみに、他によく使うのは青木先生の所とか。


経営判断が変わるほど結果に影響を及ぼすことはまずないと思われる」は、サンプルサイズが小さい時には可能性としてありますが、サンプルサイズが小さい時には、二項検定使うでしょうね。
というか、A/Bテストの結果が経営判断に直結するというのは、そもそも会社の経営として頭がおかしだけという話もありますが。

CVRが30%とか言っている時に、1%の差は意味があるんだろうか。

という事を考えた方がいいです。逆に、「どれぐらいの差があった時に検出できるようにするか」という事を検討していった方がいいでしょう。
A/Bテストを「した方がいいのか」というのも、一つの判断になります。

余談。

このあたりの人に、たまにあるのですが、CVRの足し算引き算して前後比較するのに躊躇いがない人がたくさんいます。
一見正しそうですが、サンプルサイズおんなじだった?とかそういう情報ガスガス落としたスライド見る事も多いです。


また、1回のテストでは、どうしてもそれに紛れ込んでくる影響が気になります。タイミングの影響がそこはかとなく存在したりする場合もあるわけですよ。そういうすべての要因を考慮するのは難しいのですが、

  • 理想の条件でテストすれば、実態に合わないテストになる
  • 実態に合わせてテストすると、どうしても沢山の要因を考えないといけない

という二律背反な所があります。
1%ぐらいのCVRなら、幾らテストで高い評価を得てたとしても、おそらくどんなユーザに届いたかとかの方が影響が大きく、そこに金をかけるのは効果的ではない、というような感じですね。


まああと、A/Bテストとかで画面考えてくのって、どちらかと言うとメンテナンスモードの改修で使えるか、ぐらいですねえ本当は。
サイトの成長期なら、テストで出てくる確率ほどの信頼性は当然ないです。検定とかではあくまでテストの対立・帰無仮説を包む大きな仮説が成り立つ時、使えるというものだったりするので。

SASのバージョンアップはかなりツライ。

まあ、SASに限らずだけど。
SASの資格は大したものは持っていないのだけど、体系だってSASを学んだと言うより鬼のような現状に鍛え上げられたという感じなので、ほぼほぼ大概の事は何とかなるようになってる。


んだけど、流石にSAS/GRAPH周りは普段やってない(やる気しない)のでキツい。


EMFという形式でファイルが出力出来るんだが、バージョン違いだとIEだと見えない、という事象が発生した。9.1.3と9.4。
ペイントだと見える。
意味が分からんので、GDEVICEプロシージャで眺め、差分をModifyしたが、結果変わらず。
テクサポに投げたら、9.3でその辺りだいぶ変わってるとの事。うん、まあ、html標準出力とかは大枠は知ってるけど、多分それ関係ないねんと思いながら眺め倒す事にしたら、EMF"Plus"という記述が。


ほう。
Dual形式では見えるとか、EMF形式のパターンでも見えたので、おそらくこれと。
勘弁してくれ。


ファイル拡張子も含め、EMFとして組まれているシステムをどうやって改造すりゃいいのか途方に暮れる。
無論、GDEVICEでエントリ置き換える事もやってみたが、全く効果なし。
どうやらGDEVICEで設定されるレベルでは挙動変えられないっぽい。


悩みに悩んだが、ふと思い立ち、SASのRegistryがあるのを思い出した。たまにfontregでその辺り弄る人はいるだろうけど。
EMF設定のRegistryをEMFPlusの設定で上書きし、無事動く事を確認。


そろそろ、SASの仕組みの裏側覗くような仕事は辞めたい。
正直な所、コレをクリアしたらまた大した事のない風に思われるんだろうなあ。


誰やねん、簡単に移行出来るって客に言ったヤツ。

システムは生物です。消費期限があります。

古いシステムを騙し騙し使う、というのは、実際とてもコストがかかる事が多いのですが。


ほぼほぼその辺りの話は全てお話しているハズなんですが、「聞いてない」というお客さんは多いです。


どんなシステムも、バージョンアップで見た目とか挙動とか変わります。APIも変わるでしょう。
細かくバージョンアップ対応するならいいんですけど、一段飛ばしみたいな事するのは、無理ですからね。
Windows 2003 Server→2008→2008 R2→2012→2012R2
おおよそ五年ぐらいで一度ちゃんと手を入れないといけないという事です。これを10年とかにしてしまうと、その時点で出血確定です。

SASインストール時に、アンチウィルスソフトは「絶対に」止めましょう。死ぬほど暇ならいいですが。

SASインストールのお話ですが。


SASのサーバコンポーネント、jarファイル多いんですが、アンチウィルスソフトによって内部をサーチされる時に、異様に時間がかかります。
今の所、インストールに失敗するケースはありませんが、人間が音を上げるくらい時間が掛かるので、停止して下さい。
ウィルスチェックをしたい場合には、メディアを最初にアンチウィルスソフトでチェックして、でお願いします。


実績値としても、二三時間削れるので。

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 ;