2019年12月1日日曜日

htmlのタグを指定して値をsasデータセットに格納する話

sasでwebスクレイピングってあんまり事例がないと思うので紹介する話です.
スクレイピングしようとするとwebページをhttpプロシジャ使ってhtmlのタグ付きのテキストに変換して,そこから必要な分だけをデータセットに取り込むのがいいかなと記事投稿時点では考えています.

問題になるのが「必要な分だけデータセットに取り込む」の部分かと.vba等のほかの言語ならhtmlのclass名なりid名なりを指定して格納されている値を取れますが,タグ付きのテキストに変換しているのでそんな器用なことができません.所詮テキストです.かと言って取得時にhtmlのタグを使えないとお話にならないので,テキストは@を使えば読み込み箇所を動かせるのを利用して値を取得します.

例えば以下のようなタグ付きのテキストを仮に取得したとします.
今回欲しいのはその中でも「c-rating__val c-rating__val--strong list-rst__rating-val」classの値である「3.49」ですね.


 <p class="c-rating c-rating--xl c-rating--val30 list-rst__rating-total cpy-total-score">
   <i class="c-rating__star list-rst__rating-star">
   </i>
      <span class="c-rating__val c-rating__val--strong list-rst__rating-val">3.49
      </span> </p>
 <p class="list-rst__rvw-count is-highlight">

「3.49」を取るために以下の段階を踏みます.コードの全体は一番下にまとめてあります.
まずdlm="</"とすることで,sasの変数に</ごとに区切りを変えて値を取得できます.こうすることで上のテキストの緑の部分の前後は以下のようにsasデータセットに格納できます.1行が1obsに相当すると思ってください.

</i> <span class="c-rating__val c-rating__val--strong list-rst__rating-val">3.49
</span>
</p> <p class="list-rst__rvw-count is-highlight">

1つの変数に</i> <span class="c-rating__val c-rating__val--strong list-rst__rating-val">3.49と格納できたので,class="c-rating__val c-rating__val--strong list-rst__rating-val">が出てきたら読み込み位置を@で1つ進めて3.49を取得できます.
今回はclassタグを指定していますが,同じことをすればidタグも同様に指定して値が取れるはずです.と言うかidタグがユニークになるのでとれるならidの方がいいですね.

/*--コードの全体像*/
data OUT_P;
 infile F_OUT dlm="</" dsd missover  lrecl=30000 firstobs=1;
 input @ 'class="c-rating__val c-rating__val--strong list-rst__rating-val">' V_VAL ;

 /*--要らないレコードを削除*/  
 if V_VAL = . then delete ;
run;




/*--取得した値をデータセットに上書き ページ数分繰り返せばok*/
proc append base= V_OUT data= OUT_P ;
run ;

proc httpだとurlを指定するのでどうしてもテキストに変換するのは1ページごとになってしまいます.読み込みを複数ページにまたがってやりたい時はproc httpでページを読み込み→テキストをsasデータに変換→欲しい値をproc appendでデータに追加の3つをloopして行えばokと思います.ただし一気にwebページに複数回アクセスすると攻撃扱いされますので,loop内に適宜sleep関数を挟むのが必須です.



2019年11月1日金曜日

話題の食べログの話

食べログの星が3.6と3.8の間に壁がある話を聞いたので,試しにサクッとグラフにした話です.スクレイピングで星を取得してヒストグラム書いてみました.なかなか普段スクレイピングなんてやらないのでだいぶ苦戦しましたがまあ何とか形にはなりました.以下のグラフ見てもらえればわかりますが,私がやった感じだとあんまり壁みたいなのはなかった気がします.下のグラフはx軸は星数,y軸が件数ですね.軸の設定がめんどくさかったのでラベルがV_VALになっていますが,そこに各店舗の星の数が格納されています.
今回は京都のお店を対象にしています.大阪と東京では既にグラフがありますので…
星3.6と3.8で数に大きな差はありますが,グラフはそこまで不自然な形はしてない気がします.もちろんこんなものはデータの抽出の仕方次第でいくらでも結果を変えれるので,これだけを見て食べログがどうとかこうとか言うつもりはありません.特に今回は食べログの口コミ数順に上位1200件を取ってきただけです.どの順番でデータを取るかにもよりますしね.

スクレイピングの方法をありのままに書くことはちょっとよろしくない気がするのであんまり書きませんが,proc httpでwebページをテキスト化して取得→要る部分だけデータセット化→sgplotでヒストグラムです.sgplotのヒストグラムとtextステートメントが同時に使えなかったので,グラフ内の3.6とかの文字列はsgannoでやっていますがたぶん他に手はあると思います.

/*
以下が実際にグラフを書いた時のプログラムです
V_OUTに星の数が,V_ANNOにグラフ内の文字列が入っています.
*/

%sganno ;   *--アノテート使う準備;

data V_ANNO ;
   
    %SGTEXT(label="↓3.6", x1=3.6, y1=215, drawspace="DATAVALUE") ;
    %SGTEXT(label="↓3.8", x1=3.8, y1=27 , drawspace="DATAVALUE", anchor="left") ;

run ;

title "京都のお店1200件 口コミ数順" ;
proc sgplot data=V_OUT sganno=V_ANNO ;
  histogram V_VAL / scale=count nbins=40;
  
  xaxis grid
        values=(3.0 to 5.0 by 0.2)
        offsetmin=0.05
        offsetmax=0.05
  ;
  yaxis offsetmax=0.05 ;
run ;

2019年10月25日金曜日

PharmaSUG SDE Tokyo 2019に参加した話


PharmaSUG SDE tokyoに参加した.何やら台風が来ているが無事雨もなく開催となった.傘持つだけで荷物が増えるので持たないとなると荷物が減って楽である.この時は後の雨の悲劇のことなんて知らずにのんきなもんである.六本木なんてオサレタウンに来ることなどないので来るだけで面白い.テレビドラマの広告がこんなにあるなんてなかなか見られない.というか世間にはこんなにドラマがやっていたのか.
前日は簡単な準備と確認.こんなものはおまけなのでさっさと済ませて飲み会へ.私の英語力がなさ過ぎてなんとyesとthank youくらいしか言えることがなくコミュニケーションに支障しかない.すごく気を遣ってくださったのがひしひしと感じるが英語力がついていかない.結局会話の半分くらいしかわからずほうほうの体で飲み会を終えホテルに戻る.めっちゃゆっくりしゃべってくれたことと,適宜話を振ってくださったことに感謝の気持ちしかないほんとありがとうございます.
当日は早めに会場入りをする.普段会社行くより早い時間に起きるとは思いもしなかったが普段が遅すぎるだけか.当日私にできることなんて非常に限られているので隅っこのほうでバタバタと動くしかない.途中でお茶をポロリしたが決して遊んでいるわけではなく大まじめです.ドリフやってんじゃねえのになんで落ちるかな.午後からもパラパラと来場されたので受付が追い付かない部分もあったが大崩れすることもなくまあまあで終われたと思いたい.今年のノベルティはもうちょっとひねれたんじゃないかと反省するが,考えたときにはあれがbestだったので致し方ない.思う存分栓を抜いてください.来年のノベルティがどうなるかは全く未定ですが,ハードルは極めて低いと思いますので是非来年はいいものを誰か考えてくださいね.
会の後は車屋に隣接したオサレ店にて飲み会.会場近くにあんなお店あるとかやはり東京は店の数が違うと実感.ポテト一つとっても美味しかったし何より乾杯がスパークリングワイン.どうせビールと高を括っていたところに運ばれてくるワイングラスは通常の3倍くらい趣があった.いとおかし.sas社の人も参加されていたのでここぞとばかりにviyaの話を聞いてみるが,やはり会社に導入しているbaseSASを置き換えるくらいの規模感で導入しないとお値段がお値段してそうな感じか.機能を絞った相談とかもいけそうな感じがあったが,機械学習系を積まないとviyaの良さがないよなあ.guiでの操作は強いがそこだけならイマイチ感ありますあります.そして機械学習系を積むとお値段がお値段すると...飲み終わりにすげー雨に降られてびしょびしょでホテルに戻ったが今年のPharmaSUGはこれにて終了.皆様ありがとうございました.帰りの新幹線で前日準備の際に頂いたblood orangeのチョコをよばれる.やっぱりチョコのおいしさは共通だね.オレンジがほんのり効いてて甘すぎず,酒が飲めない分茶が進む進む.やっば新幹線で書いてたら気分悪くなってきた

2019年10月1日火曜日

グラフの背景にgrid線を入れる話

グラフをsgplotで書く時に,軸のメモリごとの格子線を背景に入れるオプションの紹介です.英語だとbackgroundgridですかね.これが見つからなくてちょっと時間かかったんですよね...意外とこの手の微妙なオプションがなかなか紹介されていなくて辛い.

yaxis/xaxisステートメントにgridオプションを指定すると目的の格子線が入ります.
例えば以下の画像だとy軸のメモリごとにうっすら格子線が入っています.通常だとこんな線はなく背景は真っ白ですね

/*-以下プログラム-*/
data HOGE ;
    do i = 1 to 5 ;
        X = i ;
        Y = x ;
        output ;
    end ;
run ;
   
proc sgplot data = HOGE;
    series x = X y = Y ;
   
    xaxis
        offsetmax=0.05
        offsetmin=0.05
    ;
    yaxis
        grid  /*これがgridを出すオプション--xaxisにも同じ指定が可能*/
        offsetmax=0.05
        offsetmin=0.05
    ;
       
run ;

2019年9月15日日曜日

sasユーザー会2019に行ってきました


SASユーザー会2019に行ってきた
去年の台風が思い起こされるも今年は無事晴れ,雨の無い移動はなんと楽なんだろうか.会場が例年と違って赤坂見附とわれても会場の最寄り駅までは何とかなるので大した違いがない.問題は駅から会場までの間である.東京駅から地下鉄に乗っ1本,非常にアクセスがよい赤坂見附.会場は大学のキャンパスのようだがこれがまた田舎者には気後れするほどのオサレ会場.惜しむらくは建物内にご飯食べるところがあるにも関わらず「館内イベントのため休業」せっかくのカフェテリアが泣いてるぞ.私の初日のお昼は天下一品になりました.
今年のユーザー会の目玉は何といってもods excelとマクロクォーティングか.マクロクォーティングなんて正直関数を適当に当てはめて意図通りに行ってればokみたいなとこあるので,あれだけきれいにまとまった資料を見ると浅はかな自分が悲しくなる.あんないい感じの資料を公開してくれるのはありがたい話である.
ods excelも気になる.excel2016でddeが怪しいという噂を幾つか聞くので,新しいexcel操作方法は大変気になるところ.実際に使う使わないは別にしてもしもに備えるためにも知るくらいはしといたほうが良いんじゃなかろうか.word2016でデフォルトフォントが変わったおかげでrtf出力でゴタゴタしたと思ったら今度はexcel.officeの更新は影響範囲がとても大きい.
1日目がスムーズに終わったのでホテルに帰って発表練習.日頃から30分も話すことなんてないので口が回らないがなんとか話し終えれるくらいには準備して2日目の発表に備えて早めにお休みする.当日は30分枠のうち25分ほど使って話してしまう.ちょっとギリギリまで話しすぎたせいで質問が中途半端になった.次の機会には少し控えめに構成してみようか.なにせ30分発表なぞこれまでの中でやったことがあまりないので機微がわからない.こればかりは慣れかなあ...
今年のユーザー会も無事終わって何より.来年のめどが何も立っていないが果たして来年はどうなるか,ネタがまにあうのだろうか.

2019年9月1日日曜日

streamプロシジャでマクロ変数を出力する

最近proc streamに凝っていますが、なかなか思うように扱えないですね。悲しい。
引っかかって解決したところを時々こうやって取り上げていければと思います。

今回はstreamプロシジャでマクロ変数を出力する話です。普通に書けばそのマクロ変数は展開されてしまいますが、クォートすれば展開させずにマクロ変数そのものを出力できます。


たとえば以下のように実行すると、マクロ変数hogeを展開して_outに出力します。当然マクロ変数hogeを作っていないと展開できない旨のerrorが返ります。

filename _out hogehoge\piyo.sas ;
proc stream outfile=_out resetdelim="rd" noabsscmt ;
begin
&hoge
;;;;


以下のように&をクオートするとマクロ変数hogeは展開されず、&hogeという文字列が_outに出力できます。あくまで文字列としてマクロ変数名を出すだけなのでマクロ変数hogeを事前に作っていなくてもerrorは返りません。

filename _out hogehoge\piyo.sas ;
proc stream outfile=_out resetdelim="rd" noabsscmt ;
begin
%str(&)hoge
;;;;

よく考えるとクオートすればいい話なのですがだいぶと難儀しました。まだまだstreamプロシジャと良い関係になれません。

2019年8月1日木曜日

今実行しているプログラムのパスを取得する話

プログラムの保存場所を取得する話はあっちこっちで見かけます。
いちいちパスを変更しなくて良くなるので事故が減るのは良い事ですね。
何度パスを変え忘れがあって意図しないところに出力したかわかりません。

sysinから取ったりSAS_EXECFILEPATHから持ってきたりと色んな手がありますね。
sysinはバッチモードで取れてくるんでしたっけ、私は専らSAS_EXECFILEPATHを使っているので詳しくわかりませんが。以下の2行を実行すると、プログラムの現在のパスとファイル名が取れます。

%let _fullpath = %sysget(SAS_EXECFILEPATH);
%put &=_fullpath ;

ここまではよくあるのですが、先日プログラムのパスの一個上の階層を取りたい時がありました。
他の人のプログラムをパクリに行くと以下のような記述があって、".."の意味がさっぱりわからなかったんです。後で調べると".."はSASじゃなくてコマンドプロンプトの書き方のようでした。

x "cd パスの名前";
filename xxpath "..";
%let _rootdir=%sysfunc(pathname(xxpath));

xコマンド使っているところを見落としていたんですね・・・CDでパスに移動して、その一個上を".."で取得しています。
取得したパスをxxpathで格納して、それをマクロ変数に回収している書き方ですね。CMDでの書き方なので、filename xxpath "..\..\.."; と書けば3個上が取れたり、
filename xxpath "..\hoge"; とすると「1個上のパスのhogeフォルダ」までのパスが取れたりと割と柔軟に指定出来ますね。

あんまりやりすぎるとプログラムが複雑になりますが、変更忘れを防ぐには自動取得がいいかもです

2019年7月1日月曜日

2群の幾何平均の群間比の信頼区間の話

2群の幾何平均の群間比とその信頼区間を出力したい時は,PROC TTESTが使えますという話です.

幾何平均自体はほかのプロシジャでも出せますが,幾何平均の群間の比が出せるのはttestくらいかなと思っています.ほかでも出せたら是非教えてください.単なる群の幾何平均の信頼区間はsurveymeansでも出せますが.

ttestプロシジャにdist=lognormalを指定すると幾何平均が出せます.出力の中のConfLimitsをデータセットにすると,その中に群間の幾何平均の比の信頼区間が入っています.

proc ttest data=sashelp.class dist=lognormal alpha=0.05 ;
    class sex ;
    var weight ;

    ods output ConfLimits = CL ;
run ;

取得したデータセットのうち,Geomeansが幾何平均,LowerCLとUpperCLが幾何平均の信頼区間です.このほかにもCVで変動係数やその信頼区間が取得できますが,今回は載せていません.

この内のClassが"Raito(1/2)"となっているのが群間の比です. そのまんまですね.見た通り1/2,今回で言うところのF/Mしか出せません.M/Fが欲しいときはMを1,Fを2にした別変数作って,その変数をclassに指定してください.Format作ってorder=formattedを指定しても行けますが,私はいつも別変数作っちゃいますね...

またMethodがPooledは2群の分散が等しい場合,Satterthwaiteは分散が等しくない場合です.welchのほうが聞き馴染みがありますが...大体の場合はPooledですかね?

今回はalphaを明示していますが,指定しないと0.05 が当たります.つまり95%信頼区間ですね.alphaに0.1を指定すると90%信頼区間を出してくれます.データセットではなく結果ウィンドウの表示を見ると何%の信頼区間が出ているかを表示してくれています.本来はデフォルトの0.05をあえて指定する必要はあまりないんですが,書かないとピンとこないと言う説があるのも確かです.

2019年6月6日木曜日

sas製品版でよく使うショートカット

先日のsas勉強会の中でおすすめのショートカットの話がありました.当日は時間がなくてスルーしてしまったのですが,ほかの人がどんなショートカットを使っているかはやはり気になりますね.
製品版のsasを使う上で私が使うことのあるショートカットを紹介します.ほかにもこんなのあるよって方は教えてほしいですね...

F3        プログラムの実行
F5        拡張エディタ表示
F6        ログ画面表示
ctrl+F4 カーソルの当たっているウィンドウを閉じる
ctrl+E  カーソルの当たっている画面をクリア(もっぱらログ削除用)

私は SASの各ウィンドウを最大化して使っているので,しょっちゅうログとエディタを行き来します.実行するとログ画面が一番前に来る設定にしているのですが,エディタに戻るときはF5で戻ります.
一時はログを縦長にして右に,エディタはできるだけ大きくして左にとかやってましたが,最近はもっぱら全部最大化してます.なのでログ画面と拡張エディタを同時に並べてみるとかはほとんどないですね...

2019年6月1日土曜日

変数の型を調べる話

変数の型を調べる方法を紹介するお話です.

proc contentsとかいろいろありますが,今回はvtype関数を取り上げます.この関数でデータセット内の変数を指定すると,その変数が文字か数値かが返ってきます.複数の変数を調べるときはcontentsプロシジャ使うほうがいいですが,こちらの関数も小回りが利いて使うときがたまにあります.

試しに動かしたときのログが以下です.データセットhogeに数値変数aと文字変数bを作ります.
変数aの型をatypeに,bの型をbtypeに取得しているのが次のデータステップです.結果もログに表示させていますが,数値変数ならNが,文字変数ならCが得られます.

そんなに変数の型が変わるのはそもそもよくないのですが,知ってるとデータステップ内で取得できるので便利です.
72 data hoge ; 73 a = 1 ; b = "TST" ; 74 run ; NOTE: データセットWORK.HOGEは1オブザベーション、2変数です。 NOTE: DATAステートメント処理(合計処理時間): 処理時間 0.00 秒 CPU時間 0.00 秒 75 76 data hoge2 ; 77 set HOGE ; 78 79 ATYPE = vtype(a) ; 80 BTYPE = vtype(b) ; 81 82 putlog "ATYPE= " ATYPE ; 83 putlog "BTYPE= " BTYPE ; 84 run ; ATYPE= N BTYPE= C NOTE: データセットWORK.HOGEから1オブザベーションを読み込みました。 NOTE: データセットWORK.HOGE2は1オブザベーション、4変数です。 NOTE: DATAステートメント処理(合計処理時間): 処理時間 0.00 秒 CPU時間 0.01 秒

2019年5月28日火曜日

SASの勉強会に行ってきた話


05月24日、早くも5回目となったsasの勉強会があった。
参加者が18人、一人5分としても発表に80分かかる計算とは恐れ入る。会場はご厚意により3時間いただいていたので余裕と思ったのが間違い。1730から初めて終わったの21時過ぎ、3時間半かかるとは夢にも思わず。2次会を8時半から抑えていたのを慌てて9時に変更するもそれでも遅刻、お店の方には大いに迷惑をかけてしまった

そろそろ参加者全員が発表するのは時間的に厳しいんじゃ、などと思うがそれだといろんな発表が聞けないジレンマ、やっぱりいろんな人のネタ聞きたいやん?どこぞのナントカRみたいに土日に一日かけてやるしかないのか,いやでもあれも結局登壇者は数少ないしなあ...
メールベースの連絡はもはや限界なので新たなツールを模索する.正直共有twitterかなんか使えば十分連絡は可能なのだがそれだとやはり面白くない.せっかくやしなんか新しいの使いたいよね.うまくいくかは知らんけど.

私の発表資料はこちらに置いています.まあ大したことない日付formatの話なのでいちいち乗せるほどでもない気がしますが...せっかくなので供養がてら置きます.swayは神アプリなのでこういう時とても便利ですね.もうちょっと工夫もできるのですが,面倒なのでそのまま書き出しどーんで終わってますが...

2019年5月1日水曜日

luaでcall executeっぽいことを実行する話

call executeはご存知の方も多いと思います.データセットの変数を使ってプログラムを繰り替えすやつですね.もちろん便利は便利なんですが,セミコロンを間違ったりなんだりと書き方がややこしいと個人的に思います.
 データセットの変数を読み込んで繰り返しプログラムを実行するのを,executeではなくluaで書くこともできることの紹介です.


例えばa,b,cの三つのデータセットに対してcontentsプロシジャを実行するプログラムをexecuteで書くと以下の通りです.正直このくらいのプログラムなら複雑なこともないのですが...

* --- 読み込み元 ;
data _IN ;

  _dt = "A" ; output ;
  _dt = "B" ; output ;
  _dt = "C" ; output ;

run ;

*----- execute 実行;

data _null_ ;
    set _IN ;
   
    call execute("proc contents data=" || _DT || " varnum short ;") ;
    call execute("run ;") ;
   
run ;

ここからが同じ内容をluaで書いたプログラムです.executeでは読み込む変数を持ったデータセットをsetしていますが,luaだとopenしてget_valueします.実際に実行したいプログラムを””で挟まないので,マクロ変数を使うときにクオートを気にしなくていいのがストレスが少ないです.実に毛根に優しい.
ただし実行ログにsas.submitで展開されるプログラムが表示されるのですが,その時にマクロ変数は展開されないままっぽいです.実に惜しい.@@で挟んだluaのローカル変数は展開されてくれるんですがね...

*---- lua実行 ;

%let _opt = short ;

proc lua ;
submit ;

local code = [[
  proc contents data = @target@ varnum &_opt. ;
  run ;
]]

local _inid = sas.open("_IN")

while sas.next(_inid) do
  sas.submit(code, {target = sas.get_value(_inid , "_dt") })
end

sas.close(_inid)

endsubmit ;
quit ;

lua実行ログの一部が以下の通りです.読み込むデータのBとCはろぐに出ますが,オプションのshortは展開されずにマクロ変数の名前が表示されています.
実行するとちゃんとshortオプション効いてるので ,luaとしてはマクロ変数を展開せずに実行時に展開しているんでしょうね.

proc contents data = B varnum &_opt. ;
run ;
NOTE: PROCEDURE CONTENTS処理(合計処理時間):
処理時間 0.00 秒
CPU時間 0.01 秒
proc contents data = C varnum &_opt. ;
run ;
NOTE: PROCEDURE CONTENTS処理(合計処理時間):
処理時間 0.00 秒
CPU時間 0.01 秒

2019年4月7日日曜日

BBQにお呼ばれした話

bbq行ってきました.

知人にお呼ばれしてBBQへ参加しました.風の吹き抜ける川沿い,心配されていた天候も晴れで気持ちのいいBBQでした.bbqといえば終了後の網と炭の処理がとても面倒くさいですが,燃料付きのインスタントコンロのおかげでその辺の処理がはるかに楽でした.
あんな商品あるんですね...知らなかったです.燃料付きなので火おこしも着火するだけ,あのややこしい蒔組もいりません.なんと楽なんだ.

そして出てくる食べるものが多いのはもはやよくある事か.いやおいしい肉でしたよ?サイコロステーキとかカルビとか.食べてもまだ出てくる肉,飲んでも減らないドリンク.量もおいしさも大満足のBBQでした.1000mlの缶ビールなんて生まれて初めて見ましたよ私.ええ.あと名前だけ聞いたことのあったよなよなエール.あれおいしいらしいですね.

前日に激しい運動をしたせいで肩と背中と太ももが筋肉痛でしたが,とてもおいしく楽しかったです.川沿いのタワマンとか環境がすげえよ
激しい運動で消費したカロリーを一気に取り戻したBBQでした.呼んでくださってありがとうございました.

明日には筋肉痛治まってくれるといいんですが.

2019年4月1日月曜日

streamプロシジャ_noabsscmtオプション

ちまたで話題のstreamプロシジャの紹介を私もしようと.まあ便利ですからねえアレ
beginから一番最後の;;;;までの内容を外部ファイルに出力することができるやつですね.

この記事ではstreamプロシジャのnoabsscmtオプションの紹介です.
このオプションはコメントを外部ファイルに出力するのに必要なものです.

%let _mac1 = "tst" ;

filename tst "st2.txt" ;
proc stream outfile=tst resetdelim="rd" noabsscmt ;
begin
hoge rd newline ;
/*ここはコメント*/ rd newline ;
hage rd newline ;
/*&_mac1*/
;;;;

とすると,以下のように出せます.
今回はst2というテキストファイルへの出力です.
ただしコメント部分のマクロ変数は展開されません.
hoge
/*ここはコメント*/
hage
/*&_mac1*/

2019年3月1日金曜日

luaでプログラムをループさせる話

luaでプログラムをループさせると言う試みです.
今までSASではマクロがその役割を担ってきましたが,luaでも出来ますよと言う紹介です.

以下に載せているのは,workにある3つのデータセット(tst tst1 tst2)の更新日時をログに表示するプログラムです.
マクロで書く場合とまったく同じ結果が得られますし,マクロで書くより実行ログが見やすいです.

繰り返しの対象となったプログラムのコードがログに表示されますし,luaのテーブルで与えた可変の情報が代入された状態でログにSASコードが生成されます.
その分ログが長くなりがちですが,どこで何が代入されてプログラムが実行されているのかがログを見て一発でわかるのでとても修正がしやすいです.
マクロを使うと結局ここではマクロ変数に何の値が入っているのかを確認するのが煩雑だと思います.mprintとか駆使すればいけますが ,アレもいまいち見やすい形でログに表示されないのではないでしょうか.

なにはともあれ,下のプログラムと実行ログを見てみてください.
以下実行プログラム

data tst ;
a = 1 ;
run ;

data tst1 ;
a = 1 ;
run ;

data tst2 ;
a = 1 ;
run ;

proc lua terminate;
    submit ;

    -- ログ整理用ダミー
    sas.submit([[
        data _null_ ;
        run ;
    ]])

    dataset = {}
    dataset[1]  = "work.tst"
    dataset[2]  = "work.tst1" 
    dataset[3]  = "work.tst2" 

    for i, tag in ipairs(dataset) do
        sas.submit([[
            data _null_ ;
                RCO = open("@tag@") ;
                RC = attrn(RCO , "modte") ;
                CL = close(RCO) ;
                format RC e8601dt. ;
                putlog "@tag@:" RC ;
            run ;
        ]])
    end

    endsubmit;
run ;
/*実行ログ*/

2460   proc lua terminate;
2461       submit ;
2462
2463       -- ログ整理用ダミー
2464       sas.submit([[
2465           data _null_ ;
2466           run ;
2467       ]])
2468
2469       dataset = {}
2470       dataset[1]  = "work.tst"
2471       dataset[2]  = "work.tst1"
2472       dataset[3]  = "work.tst2"
2473
2474       for i, tag in ipairs(dataset) do
2475           sas.submit([[
2476               data _null_ ;
2477                   RCO = open("@tag@") ;
2478                   RC = attrn(RCO , "modte") ;
2479                   CL = close(RCO) ;
2480
2481                   format RC e8601dt. ;
2482                   putlog "@tag@:" RC ;
2483               run ;
2484           ]])
2485
2486       end
2487
2488       endsubmit;
2489   run ;
NOTE: Lua initialized.
        data _null_ ;
        run ;
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.00 秒

            data _null_ ;
                RCO = open("work.tst") ;
                RC = attrn(RCO , "modte") ;
                CL = close(RCO) ;
                format RC e8601dt. ;
                putlog "work.tst:" RC ;
            run ;
work.tst:2019-01-12T20:01:32
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.01 秒
      CPU時間            0.01 秒

            data _null_ ;
                RCO = open("work.tst1") ;
                RC = attrn(RCO , "modte") ;
                CL = close(RCO) ;
                format RC e8601dt. ;
                putlog "work.tst1:" RC ;
            run ;
work.tst1:2019-01-12T20:01:32
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.00 秒

            data _null_ ;
                RCO = open("work.tst2") ;
                RC = attrn(RCO , "modte") ;
                CL = close(RCO) ;
                format RC e8601dt. ;
                putlog "work.tst2:" RC ;
            run ;
work.tst2:2019-01-12T20:01:32
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.00 秒

NOTE: TKLua terminated.
NOTE: PROCEDURE LUA処理(合計処理時間):
      処理時間           0.35 秒
      CPU時間            0.20 秒

2019年2月13日水曜日

指定したライブラリのパスをログに表示する話

ライブラリのパスをログに表示させる話です.
pathname関数でパスの場所が取れるので,それをputlogしています.
どこのデータセットつかったのか,読み込んだデータが最新を格納している場所からちゃんと取れているのかを聞かれる時ありますから...

どうせログは保存するのだから,ログに出しておけば安心という考えです.
過剰な記録かもしれませんが...

以下にプログラムと実行ログおいておきます.

libname hoge "/home/xxxx/sasuser.v94/hoge" ;

/*---------- 指定したライブラリのパスをログに表示 ----------*/
data _null_ ;
    V_PATH = pathname("hoge") ;
    putlog "hoge:" V_PATH ;
run ;

/*---------- 実行ログ ----------*/
 71         data _null_ ;
 72             V_PATH = pathname("hoge") ;
 73             putlog "hoge:" V_PATH ;
 74        
 75         run ;

 hoge:/home/xxxx/sasuser.v94/hoge

2019年2月1日金曜日

データセットの更新日を取得する話

指定したデータセットの更新日を取得してログに表示します.
いつのどのデータセットつかって作業したか,を残しておいたほうがいいときに使っています.
どうせログは保存するのだから,ログに出しておいたら確認できるやろ,の精神ですね.

attrn関数のmodteを指定すると,更新日時が取れます.
指定を変えると作成日時も取れるらしいですがためしていません.
取れてきたものにe8601dtのフォーマットあててputlogしています.

いつのつかってたっけ?でドキッとすることがなくなるので便利なんですが,ここまで残す必要がある時があるかと言うと...微妙ですね...
以下にプログラムと実行時のログを残します.
sas studioで実行しています.製品版でも大差ないはずです.

/*---------- ライブラリ指定 ----------*/

libname hoge "/home/xxxx/sasuser.v94/hoge" ;

/*---------- 指定したデータの更新日をログに表示 ----------*/

data _null_ ;
   RCO = open("hoge.card") ;
   RC = attrn(RCO , "modte") ;
   CL = close(RCO) ;
   format RC e8601dt. ;
   putlog "card:" RC ;
run ;

/*---------- 実行ログ ----------*/
 71         data _null_ ;
 72            RCO = open("hoge.card") ;
 73            RC = attrn(RCO , "modte") ;
 74            CL = close(RCO) ;
 75        
 76            format RC e8601dt. ;
 77            putlog "card:" RC ;
 78         run ;

 card:2018-01-22T13:15:42

2019年1月24日木曜日

冬のSAS忘年会にいってきた話

12月21日,SASの発表会に行ってきた.
年末だけあって何分立て込んでいたがなんとか間に合う時間に退社し,いそいそと会場に向かう.
当日はあいにくの雨,ただでさえ寒いのにさらに冷えるのはほんと勘弁してもらいたい.
途中にある松屋で牛飯をかきこんでから会場に入る.松屋は牛丼ではない,牛飯です.
会場に入って待つことしばし,まあ来るわくるわ参加者が.途中から何人来るかの把握をあきらめていたが,やはり今回は多かった.全部で10人ちょっとか.
とりあえず椅子だけ並べて会長の100万回の管理人さんの挨拶を頂いて,さて皆で発表.
1人5分程度のライトニングセッションなのだがコレが気楽で良い.30分質疑込みとか重すぎてしんどいだけである.
グラフから始まり文字コードなどのバラエティに富んだ発表がたくさんだった.よくもまあそんなネタが出てくるもんである.
終わった後は飲み屋で串を食べ食べ他愛も無い話に花が咲く.私はお酒はダメなのでミックスジュースだった
気づくと終電の時間が近く,食べ過ぎの体に鞭打って駅に走りとても苦しかった.たまたま電車が遅れていたので終電に間に合い無事帰宅と相成った.

毎度場所の選定から飲み会のセットまでやってくださる方には頭が上がらない.
この場を借りて感謝をお伝えします.ありがとうございます.
そろそろ人数が増えてきたので,どこか良い感じの場所があるといいなあ,どこかに親切なS○S社とかいませんかね.
連絡手段もメールはそろそろ限界ですしなあ

2019年1月1日火曜日

文字列の前後のスペースを除いてマクロ変数を作る話

たとえば"  test"のような,マクロ変数にしたい文字列の前後に要らないスペースがある時,
スペースを取り除いてマクロ変数にすることがあると思います.
今回の例で言うと"test"と言う値をマクロ変数に格納する場合です.

そんな時はcall symputxが便利ですよ,と言う話です.
call symputxだと前後のスペースを取り除いてマクロ変数にしてくれます.
call symputx("a" , "  test")と,call symput("a" , left(trim("  test") ))は同じです.

割と色んな場所で見かける紹介なのですが,あえてcall symputでマクロ変数を作っていてスペースを取り除き忘れたために
意図しない挙動を取ったプログラムを見かけたので私も紹介します.

/*---------- 以下マクロ変数作成時のプログラムの例 ----------*/
data _null_ ;
    call symput("a1"  , "  test") ;
    call symput("a2"  , left(trim("  test") )) ;
    call symputx("a3" , "  test") ;
run ;

%put &=a1 ;
%put &=a2 ;
%put &=a3 ;

/*---------- 作成したマクロ変数をlogに表示 ----------*/
39
40   %put &=a1 ;
A1=  test
41   %put &=a2 ;
A2=test
42   %put &=a3 ;
A3=test