複数回transposeしてからmergeする前にちょっと考えてみてって話

例えば以下のデータセットから

data Q1;
ID="001";VISITN=1;A=1;B=2;C=3;output;
ID="001";VISITN=2;A=3;B=4;C=5;output;
ID="002";VISITN=1;A=6;B=7;C=8;output;
ID="002";VISITN=2;A=9;B=0;C=1;output;
run;











以下の全転置データセットを作りたい場合










proc transpose data=Q1 out=_A prefix=A_;
  var A;
  id VISITN;
  by ID;
run;
proc transpose data=Q1 out=_B prefix=B_;
  var B;
  id VISITN;
  by ID;
run;
proc transpose data=Q1 out=_C prefix=C_;
  var C;
  id VISITN;
  by ID;
run;
data RES1;
merge _A _B _C;
by ID;
drop _NAME_;
run;

と書いたりします。

もちろん、これで正解です。
変数が10個なら10回transposeかければいいんですが、ちょっと工夫を考えてみます

例えば、今回の場合だと「ID」 と「パラメータ」と「値」で構成される縦積み構造にしてから転置すれば1回の転置でスムーズに結果がだせます。

つまり

data _Q1;
set Q1;
array ch A--C;
do over ch;
VNAME =vname(ch);
VAL=ch;
output;
end;
keep ID VNAME VISITN VAL;
run;
proc transpose data=_Q1 out=RES2(drop= _NAME_) delimiter=_;
  var VAL;
  id VNAME VISITN;
  by ID;
run;








というわけ(IDの複数指定ができるようになったのは9.3から)。

1回途中で作ってる縦積み構造(_Q1)の中身をみてみると




















となるわけです。


別にこっちの方法が正解だというわけではなく、状況に応じて考えましょうという話ですね。対象が文字列変数の場合、縦積み構造にするときに文字切れしないように最大lengthにしなければならないので注意が必要です。

SASの場合、BYやCLASSステートメントが強力なので、インプットやアウトプットの形にちょっと頭を使えば、同じ機能のステップを反復する必要がない状況が多いです。

まあ、言いたいことを一言で言うと、データステップも楽しい世界だよってことです。


影の薄い%syscallの話

例えば以下のようにマクロ変数が3つあったとします。

%let a = 3;
%let b = 1;
%let c = 2;

あんまりやらない処理ですが、a,b,cの中身の値を昇順ソートして
a=1,b=2,c=3と再代入したいとします。

実は1行で書けますが、ぱっとコードが思いつきますか?

知ってるか知らないかですが、以下のように書けます

%syscall sortn(a,b,c)

で終わりです。
確認してみると

%put &=a  &=b  &=c;





というわけ。
%sysfuncはよく知られてるけど、それのコールルーチン版の%syscallはあんまり知られてない。

まあ、確かに使わざるをえない機会はそれほど多くない気がする。



libname excelで参照しているデータセット(エクセルSheet)がダブルクリックで開けない

地味に困ってる人が多いみたいなのでメモ。

libname excelして、ライブラリ参照しているエクセルシートを、ダブルクリックで
データビューから直接見ようとすると開けずにエラーになる現象。

もしSASが9.4で64bitで動いてるなら

options validmemname=extend;

した後にもう一回挑戦してみましょう。たぶん開けるのでは?

以下のProblem Noteで言及されてました

「Problem Note 51691: SAS® VIEWTABLE does not display an Excel sheet that is accessed using a LIBNAME statement」
http://support.sas.com/kb/51/691.html


ODS EPUBで電子書籍を作って遊ぶ話

この季節、新しい人と触れ合ったりすると、これからSASを始める日本全国のSASユーザーに、もっと役に立つ基礎的かつ実用的な記事を書くぞ!って気持ちが湧いたりします。

湧いたりはするんだけど、実際書いてみるとこのザマですよ。
駄目なんですよ、こういう内容しか書けないんです。SAS勉強するならSAS忘備録みましょう、書籍の「統計解析ソフトSAS」読みましょうね。
ここはSASのジョークサイトだと思ってください。

というわけで、SAS9.4から追加されたods epubでepub形式、ようするに電子書籍リーダー用のファイルを作れるようになりました。

自分はタブレット端末持ってないし、本も紙でしか見たことないような遅れ人なんですが
後ろの席に座ってる人がやたらとEPUB形式の魅力を語ってくれるので興味をもっちゃいました。

以下のコードを実行します。

ods escapechar="^";
ods graphics on;

ods epub file="XXXXXXX/sassyama.epub"
title="SASはたのしい"
options(creator="SASYAMA" cover_image="XXXX/美濃囲い画像.jpg");
proc odstext;
p "実行コード"/style=[font_size=8];
p "proc freq data=sashelp.class;^{newline 1} tables age*sex/plot=freq;^{newline 1} run;"
/style=[font_size=8 font_style=Italic];
run;

proc freq data=sashelp.class;
tables age*sex/plot=freq;
run;
ods epub close;

結果、出力されたファイルを何かしらのソフトで開きます。
画像はChromeのアドオンのReadiumってやつで開いてます。

まずは表紙、特に意味もなく美濃囲いの美しい画像を使ってみました

















んで、中身


















たぶん、こだわりだしたらキリがないくらい色々できるみたい。

コード書いてるときは、100%おふざけのつもりだったんだけど、テキストとか画像とか、SAS出力を簡単にまとめれるし、リンクみたいなのも作れそうだから、何か仕事にも使えるかも。

例えばね、実行プログラム自身をproc streamでincludeして、どうにかodstextで表示したりして、実行ログとアウトプット、主要なデータセットのビューを埋め込んだりして
、もしかしたら仕様書の記述も加えたりしたら、なかなか面白い解析メタデータブックみたいなのができそうですね。
それを例えば新人教育の教材として、epub形式で配布するとかさ。

ああ、でもそれならYoshihiro Fukiyaさんが提案されてる「情報共有としてのSAS & Jupyter Notebookの使用」の方がよいのかな

まあ、あと例えば営業の人が全員ipad支給されてたりすることあるじゃないですか、そういう部隊にSASアウトプットを即時的に資料提供するのに使ったりとかね。
ないしは日報とかでその日の売り上げレポートをバッチでばらまくとか。

おお、なんかそれらしくまとまった。
やっぱり新しいアイデアは、遊びから始まるということで。こじつけだけど。







3回話しかけると襲い掛かってくる村人をコルーチンで表現する話

最初にちょっとしたSAS絡みのニュース
①SASユーザー総会のサイトがオープンした。聴講するより発表した方が楽しいので、アブストラクトだしましょうという話
http://www.sascom.jp/sug17/

②生物統計ハンドブックが12年ぶりに新版で内容変わるみたいなので、医薬系ユーザーは会社にねだって買って貰っとこうという話
http://www.scientist-press.com/11_339.html

さて、本題、例えばSASでロールプレイングゲームを作ろうと思います。
ある村人に話しかけると、村の名前を言ってくれるのですが、三回同じように話しかけると会話内容が変わって戦闘になるというイベントを作りたいと思います。
この3回話しかけると処理が変わるという機能をSASで実装するにはどうしますか?
今までは村人の会話挙動をSASマクロで実装して、村人に話しかけた回数をグローバルマクロ変数かSASデータセットに保存する必要がありました。
しかし、たかだか村人一人の挙動制御のためにグローバルマクロ変数を一つ作っているとキリがないです。
何がいいたいかというと、SASマクロの弱点は、ある機能に紐づく値をその機能自信に保持させれないということです。だってローカルマクロ変数は実行終わると消えちゃうんだもん。

そこでProc Luaの出番です。以下のコードで村人の挙動を作成します

proc lua;
submit;
function murabito()
    print("ここはSASむらです。")
    coroutine.yield()
    print("ここはSASむらです。")
    coroutine.yield()
    print("しつこいな、ころしてやる")
    return "----戦闘開始-----"
end
-- コルーチンを作成
co=coroutine.wrap(murabito)
endsubmit;
quit;

で、実際に3回話しかけてみると

proc lua;
 submit;
 -- 1回目話しかける
 print(co())
 -- 2回目話しかける
 print(co())
 -- 3回目話しかける
 print(co())
 endsubmit;
quit;












これがLuaのコルーチンです。色々あって奥が深いんですけど、とりあえず簡単な例です。
ようはcoroutine.yield()と書いたポイントで、一旦処理を中断して、戻すことができるわけです。次に呼び出された際はcoroutine.yield()の次の行から実行されます。
ちなみに戻り値も設定できます。
関数をコルーチンにするにはcoroutine.wrapを使えばOKです。

実は、ある機能自体に状態を記憶させるのは、コルーチン以外にもクロージャという機能で実装できます。
詳しくは過去記事「クロージャってなんじゃろ?」
http://sas-tumesas.blogspot.jp/2016/11/blog-post.html
をどうぞ。

どっちを使えばよいかは、好みとケースバイケースですが、今回みたいな単純な処理の場合、コルーチンの方がわかりやすいかもですね。カウント変数いらないし。


sgplotで日本地図を書いて新入社員の気をひこうって話

春ですね。
新SASプログラマーが増えるこの季節。

ベテランのSAS使いである皆様方は基本的には無口か、話が面白くない人が多いと思うので(偏見)、新人が気を使って適当な話題をふろうとして「SAS社ってどこにあるんですか?」とか当たり障りのないことを訊いたりするわけですよ。

そこで「六本木にあるよ」とか答えてるようじゃ、クソですよ。面白くもなんともない。

そういうときは敢えて何も答えずに、高速タイピングを駆使して
以下のプログラムを打ち込んで(5秒以内が望ましい)、F3かF8をターン!!とおっさんらしく、うるさく弾いて実行!!

data my_map;
set mapsgfk.japan end=eof;
ID=cats(ID,SEGMENT);
output;
/*緯度経度を追加*/
if eof then do;
 XX=139.729075;
 YY=35.660409;
 text="このへんにあるよ";
 output;
end;
run;

proc sgplot data=my_map noborder noautolegend;
/*地図*/
polygon x=LONG y=LAT id=id /
 fill outline lineattrs=(color=lightGray thickness=1)
 fillattrs=(color=Azure);
/*散布図とテキストを重ねてる*/
scatter x=XX y=YY;
text x=xx y=YY text=text/position=BOTTOMRIGHT ;
xaxis display=none;
yaxis display=none;
run;

アウトプットにどかんと





















となるわけです。(散布図は六本木ヒルズの緯度経度を指定してます)
なんて、ユーモアがあってデキる人だ!一生ついていきます!となること請け合いです。

まあ、真に受けて実践したら、キモがられること請け合いですけどね。

でも、たまにはSASのお勉強の間に、こういうの挟むのもいいんじゃないですかね。
(僕のようなふざけたSAS使いになってしまっても責任はとれない)

さて、本題。
sgplotのpolygonステートメントは凄い便利。
あとtextプロットが地味だけどスゲー使える。
gmapとちがってSAS GRAPHのライセンスもいらないし、緯度経度データがあればすぐかける。しかも他のグラフと簡単に重ねれるので、エリアごとの人口とか売上とかを図示するのも比較的かんたんにかける。

解析対象集団の構成フロー図とか、従来なら図としても表としてもプログラミングしにくい系のものも簡単に作れるし、こりゃあ役に立ちますぜ、奥さん。



Proc Luaのsas.write_dsに流す際にvarsテーブルで定義情報を制御してやろうじゃないって話

忘備録の「PROC LUAによる変数作成と行追加のはなし」を読み返してふと思ったこと
(http://sas-boubi.blogspot.jp/2016/09/proc-lua_26.html#comment-form)

既存データセットを上書き更新せずに変数追加はできない。
それはよくわかる。仕様ならしようがない。

代わりにsas.submitでコード流してくれとSAS社からのお達し。
SASデータセットの操作はSASコード実行するのが一番速いので、SAS社の言ってることは正しい。わかる。

けど、それじゃ、Luaで操作してるって感じがないから残念っていうmatsuさんの気持ちも凄いわかる。

じゃあさ、どうせ、setでデータセット読み込んで、データセットを上書き更新しちゃうならsas.load_dsでデータセットをLuaのテーブルにしちゃってさ、
ディスクリプタ部に該当するvars子テーブルの中身をいじって再度write_dsしちゃうのはどう?
処理時間的には非効率だけど、Lua書いてるって感じがして面白いのと、
Lua固有の操作で属性に関与できるから可能性のある考えじゃない?っていう提案。

要するに以下みたいなこと


data Q1;
VAR1=1;VAR2="A";output;
VAR1=2;VAR2="B";output;
run;









proc lua ;
submit;
local TB=sas.load_ds("Q1")
TB.vars.var3={type="C",length=20,label="ラベル"}
sas.write_ds(TB,Q1)
endsubmit;
quit;











write_dsはload_dsで作成される構造と同じテーブルを渡すことで
LuaテーブルからSASデータセットを作ってくれる。

おさらいしておくと、load_dsで作成される構造とは

proc lua;
submit;
local TB=sas.load_ds("Q1")
print(table.tostring(TB))
endsubmit;
quit;

で、自分で確認してほしんですけど(画像でかくなるから)、
テーブル直下に[1]とか[2]とかってobs番号がキーになって子テーブルができて、その中に
各変数名がキーになって値がはいる。
また、それとともにキー[vars]として子テーブルができて,その中に変数名がキーになって子テーブルができて、その中にさらに属性値名がキーになって値が入るわけです。
テーブルの入れ子構造で、本当面白い。

ちなみに、当然の疑問として、varsテーブルを作らずにwrite_dsするとどうなるかという話ですが、以下を実行してみてください。

proc lua ;
submit;
local TB={[1]={X=1,Y="A"}}
sas.write_ds(TB,"Q2")
endsubmit;
quit;

文字値のレングスのデフォが何故か500になったりするから要注意ね。

あと、Luaのテーブルに変換して、またSASデータセットに変換するという迂回をしてるんで速度はsas.submitで捌くのに比べると当然、ゲロ遅くなります。

あと当たり前だけど、ハッシュ型において、絶対的な格納位置みたいな概念ないから
SASデータセットにした際の変数位置って、どうやって制御したもんかなっていうのも課題としてあります。
正直、データハンドリングの本質的には、変数の格納位置なんて本来はどうでもいい。
そこを処理のとっかかりにしたり、やたら縛られるSASが独特なわけで。
本当は、んなもん飾りですよ。偉い人にはそれが、、、。