他の方のブログでも何度か言及されていますがSASマクロの%doループはちょっと使いにくいところがあります。
[データ解析備忘録]
アルファベットや飛び飛びの値でもループしたい
たとえばSASHELPライブラリにあるCLASS FISH CARSの3つのデータセットをループ処理で
順番にproc printにかけたいなと思えば
%macro mloop;
%let loopval=CLASS FISH CARS;
%do i=1 %to %sysfunc(countw(&loopval));
%let target=%scan(&loopval,&i);
proc print data=sashelp.⌖run;
%end;
%mend;
%mloop;
とかけます。
はい、わかりにくいですね(別にSASマクロをディスってるわけじゃないですよ!)
proc luaの場合、色々と書けるんですが、とっつきやすい1パターン紹介です。
proc lua;
submit;
local target = {'CLASS','FISH','CARS'}
for i, item in ipairs(target) do
sas.submit("proc print data=sashelp."..item..";run")
end
endsubmit;
run;
と、これでOKです。うん、発想的にも見かけ的にもcall executeと同じなので、何やってるかはわかりますね。
上のように実行コードが単純な1行程度なら文字列連結でもいいですけど、複雑になってくると
手に負えないので、キーワードパラメータもちのマクロのように、パラメータを分離して以下のように書くほうが綺麗かな
proc lua;
submit;
local target = {'CLASS','FISH','CARS'}
local code =
[[
proc print data=sashelp.@dataset@;
run;
]]
for i, item in ipairs(target) do
sas.submit(code, { dataset = item } ) --ここでパラメータ指定して実行
end
endsubmit;
quit;
うん、発想はマクロと同じですが、こっちの方が美しいと僕は思いますけどね。
まあ、Luaの中でSASコードを生成して実行する話はまた今度詳しくやります。
さて、今回勉強したいのは target = {'CLASS','FISH','CARS'}の部分で「テーブル」といいます。
Luaにおいて、とても特徴的なのが、データ構造が唯一その「テーブル」というもので管理されるという点です。
「テーブル」と聞いて一般的に想像する形式とは違い、Luaのテーブルは、入れ子にできたり関数を保持できたり、かなり何でもアリな概念だと思ってください。
多分、何回か掘り下げることになりうと思いますが、まずポイントとして、Luaのテーブルは
配列(ARRAY)と連想配列(HASH)の両方の性質を持つという点です。
いや正確には内部的に「Array」管理パートと「Hash」管理パートがあるといった感じですかね。
まあ、まだそんなに難しく考える必要はないです。なんでも入れれると思ってください
proc lua;
submit;
--配列的なテーブル定義の例 とりあえずなんか突っ込んでみる
local sampletable = {'りんご', 999, 'ABCD', 123}
--型を見てみる
print("sampletableの型は",type(sampletable),"だ")
print("===================================")
--ループで1から4番目の要素をプリントする
for i =1 , 4 do
print(i,"=",sampletable[i])
end
--1番目の値を変えて、5番目に値を追加する
sampletable[1]='みかん'
sampletable[5]=456
print("===================================")
--配列テーブルのループ参照には、ipairs関数というものを使って以下の書き方をする。
--とりあえず丸覚えしちゃえばいいと思う
for i, val in ipairs(sampletable) do
print(i, "=",val)
end
endsubmit;
quit;
結果は
文字と数値混ぜれたり、途中で要素増やしたり、なんかSASのarrayをゆるゆるにした感じですね。
代入の仕方は簡単ですね。{}の中に値を区切っていれるだけです。
参照の仕方も、テーブル名[要素番号]でシンプル。
ちなみに
table ={}と空でつくっておいてから
table[1]=1と値をいれるのも当然OK
Luaの通常のインデックスループは
for インデックス変数 = 開始値 , 終了値 ,加算値 do
処理
end
といったごく普通。
ただ、テーブルの中を参照する場合、決まり文句のようなものがあって
for i, 変数 in ipairs(テーブル名) do
変数の中にテーブルの値が入っていく
end
といった書き方をします。上記の例だとvalの中にsampletableの要素が順番にはいる形で
ループが発生します。
ipairs関数はイテレータ関数と呼ばれるもので、配列としてのテーブル(keyが1から始まる数字で連続しているならハッシュでもいいけど)には
これを使います。
つぎに同じテーブルでもハッシュ的にkeyと値で管理してみます
proc lua;
submit;
--ハッシュ的テーブル定義の例 とりあえずなんか突っ込んでみる
local sampletable2 = {fruits='りんご', no1=999, valc='ABCD',[1]=1}
--キーがkey1のデータと、キーが99のデータを追加してみる
sampletable2["key1"] = "data1"
sampletable2[99] = 100
print("===================================")
--キーがvalcのデータを参照
print("キーがvalcのデータは",sampletable2.valc)
--キーが99のデータを参照
print("キーが99のデータは",sampletable2[99])
print("===================================")
--ハッシュテーブルのループ参照には、pairs関数というものを使って以下の書き方をする。
--とりあえず丸覚えしちゃえばいいと思う
for key, val in pairs(sampletable2) do
print(key, "=",val)
end
endsubmit;
quit;
結果は
local sampletable2 = {fruits='りんご', no1=999, valc='ABCD',[1]=1}
について
sampletable2テーブルのキーは
fruits
no1
valc
1
で、それぞれにデータが入ってます。
参照はsampletable2.valc でもsampletable2['valc']でもOK
ハッシュのように管理されているテーブルの場合
ipairsではなくpairs関数を使います。「key」っていう変数名はなんでもいいんですが、
慣習的にipairsではi、pairsではkeyって名前にするみたいです。
多分、もうおなかいっぱいだと思うんですけどもう少しだけ
今まで説明したのはいわゆる1行データ、1次元配列のようなパターンだけでした。
ただ、それだけだと、今後SASデータセットをLuaのテーブルにしたり
Luaのテーブルからデータセットを作る際にちょっとわかりにくくなると思います。
SASデータセットは変数とOBS、つまり2次元になるので。
多次元構造のつくり方はいたって簡単で、テーブルの要素をテーブルにしちゃえばOKですつまり
proc lua;
submit;
local samp3 = {};
samp3[1] = {1,2,3};
samp3[2] = {4,5,6};
samp3[3] = {7,8,9};
--local samp3 = {{1,2,3},{4,5,6},{7,8,9}};でも当然OKよ
--中身を確認するためLuaのテーブル関数tostringを使ってみる
print(table.tostring(samp3))
endsubmit;
quit;
としてみます。以下のようにきちんと2重ループにしてプリントしてもいいけど
for i, val in ipairs(samp3) do
for j, val2 in ipairs(val) do
print(i..'-'..j, "=",val2)
end
end
面倒なので、Luaのテーブル関数(またいずれ説明)tostringを使って、テーブルの中身をみます。
階層構造で表現されます。結果は以下のとおり
ハッシュタイプで階層的に表現したいのなら以下の感じかな
proc lua;
submit;
local samp4 = {};
samp4.row1={col1=1,col2=2,col3=3};
samp4.row2={col1=4,col2=5,col3=6};
samp4.row3={col1=7,col2=8,col3=9};
print(table.tostring(samp4))
endsubmit;
run;
ごめんなさい、もう少しだけ。
sasのdim関数のようなものはないのかということなんですが#テーブル名かtable.size関数があります。
通常の配列的に使用されるテーブルなら結果は同じですが、キー値が文字、あるいは数字であっても1から始まって連番でなければ#の結果は狂います。狂うというか、#はテーブルのArrayパートの1以上の数値の最大値しか参照していない仕様のようです
どういうことかというと
proc lua;
submit;
--ハッシュ的テーブル定義の例 とりあえずなんか突っ込んでみる
local samp5 = {1,2,3,4}
print ('#samp5=',#samp5);
print ('table.size=',table.size(samp5));
--飛び番で定義してみる
samp5[6] = 6
print ('#samp5=',#samp5);
print ('table.size(samp5)=',table.size(samp5));
--0とか文字のキーとかたしてみる
samp5[0] = 0
samp5.key1 ="data"
print ('#samp5=',#samp5);
print ('table.size(samp5)=',table.size(samp5));
endsubmit;
quit;
ということです
これは気をつけないと駄目ですね
ところで最後にYoshihiro Fukiyaさんが嬉しい記事を書いてくれました!!
[みんなでLuaを勉強してみよう!!]
凄く嬉しいです。
参照の論文もおすすめです(ただ、ちょっと気になるところもあって、get関数じゃなくてget_valueじゃない?とか、書かれた時期のメンテナンスバージョンのせいなのかそのまま通すとエラーになる部分がある気がします。そんなことない?)
0 件のコメント:
コメントを投稿