WordleをSASで実装してみた.判定はdo overとhash iterator object), 出力はSAS RWIを使ってみた

 Wordleっていうのが流行っていて,最近,やってる人多いなぁって思ってました
(最初はなんかみんな,謎の図形をSNSにあげてて,なんかの暗号?こわいと思ってました)

いわゆる文字当てゲームで
5文字単語の答えに対して6回チャレンジできるのですが

アルファベットとその位置が回答とあっていたら緑

アルファベットはあってるけど位置が違う場合は黄 って感じで,こちらのチャレンジに対してのフィードバックが返ってくるので,それを手掛かりにできるだけ早く正解に辿り着きましょうってゲームです

詳しくはウィキペディア参照
https://ja.wikipedia.org/wiki/Wordle



昨晩
SASユーザーなら何使ってアルゴリズム組むんだ?って海外の方が問いかけました?
Arrayか? hash objectか? IMLか? 正規表現か?

https://twitter.com/cjdinger/status/1503707344907251715







まあarrayが一位だろうと思いつつ,海外はhashの支持率高くていいなぁって,私も第一感hashなんですよね
https://twitter.com/Poswith1/status/1503712048282476544












私の敬愛する方が,こんなこといっちゃうので,色々とお祝いしたいこともあったので
書いてみました

インターフェースをマクロウインドウとかで作ってもよかったんですが
SAS ondemandとかで動かないので,ちょっとそこは手抜き

まず,答えとなる単語をデータセットにしたものを

どっかの永久ライブラリにでもおいておく

data ANS;

NO=1;WD="R";output;

NO=2;WD="E";output;

NO=3;WD="B";output;

NO=4;WD="U";output;

NO=5;WD="S";output;

run;


んで

解答は6回挑戦できるので
まあ,1回答1行ずつ文字いれて実行するイメージで


data DT1;

/*Try-1*/

C1="A";C2="R";C3="I";C4="S";C5="E";output;

/*Try-2*/

C1="R";C2="O";C3="U";C4="T";C5="E";output;

/*Try-3*/

C1="R";C2="U";C3="L";C4="E";C5="S";output;

/*Try-4*/

C1="R";C2="E";C3="B";C4="U";C5="S";output;

/*Try-5*/

C1="";C2="";C3="";C4="";C5="";output;

/*Try-6*/

C1="";C2="";C3="";C4="";C5="";output;

run;


で,肝心の判定と出力部分ですが

解答のデータセットをハッシュ反復子オブジェクト,

挑戦回答を横持ちでdo overでループして

_i_とハッシュの格納Noが一致して,かつデータが一致しているか

データはあるけど_i_とNoが不一致かで判定するようにしました

で,データステップでの判定の流れからダイレクトに美しめの出力を作りたかったので

マイナーですがSASのReport Writing Interface(RWI)オブジェクトを利用してみました


data _NULL_;

length background $50.;

set DT1;

where ^missing(cats(C1,C2,C3,C4,C5));

if 0 then set ANS;

if _N_=1 then do;

declare hash h1(dataset:"ANS",ordered:"Y");

declare hiter hi1('h1');

h1.definekey("NO");

h1.definedata("NO","WD");

h1.definedone();

end;

array  AR C1-C5;

dcl odsout ob();


  ob.layout_gridded( columns:5, rows:1 ,column_gutter: '2mm');

  do over AR;

  position=_i_;

  put position=;

  rc = hi1.first();

  do while (rc = 0);

   if position=NO and AR=WD  then background= "green";

   else if missing(color) and AR=WD  then background= "darkyellow";

rc = hi1.next();

  end;

  if missing(background) then background="gray";

  text=cats(" color=white height=1.5cm width=1.5cm fontsize=5 vjust=center background=",background);

      ob.region();

       ob.table_start();

          ob.row_start();

               ob.format_cell(data:AR,style_attr:text);

          ob.row_end();

       ob.table_end();

   call missing(background);

end;

   ob.layout_end();

 run;


















どうでしょう,わりと再現度高くないですかね?
ハッシュ反復子オブジェクトとRWI,両方,データステップ内でのオブジェクトなので
そこで判定とレポートがそのままの流れでできて美しいと感じたのですがどうなんでしょう


SASプログラマにとって大切なこと(Maxims of Maximally Efficient SAS Programmers)

 
PythonZen & PEP 8 検定試験 っていう面白い認定試験があったので受けてみました.
90点(70点ライン)でした. Pythonのコーディングの啓蒙を兼ねた面白い試験でした













PEP8はコーディング規約みたいなもので
PythonZenは心構え的なイディオム集です

1.Beautiful is better than ugly.
2.Explicit is better than implicit.
3.Simple is better than complex.
4.Complex is better than complicated.
5.Flat is better than nested.
6.Sparse is better than dense.
7.Readability counts.
8.Special cases aren't special enough to break the rules.
9.Although practicality beats purity.
10.Errors should never pass silently.
11.Unless explicitly silenced.
12.In the face of ambiguity, refuse the temptation to guess.
13.There should be one– and preferably only one –obvious way to do it.
14.Although that way may not be obvious at first unless you're Dutch.
15.Now is better than never.
16.Although never is often better than right now.
17.If the implementation is hard to explain, it's a bad idea.
18.If the implementation is easy to explain, it may be a good idea.
19.Namespaces are one honking great idea – let's do more of those!

基本的にPythonだけでなく全言語に通じるところのある,まさに「禅」ですね

そういやこういうのの,SAS版ってあったかなぁと思うのですが
Kurt Bremser さんがフォーラムのリーダーもつとめるパワーユーザーで
その方が色んなところで発表されてる
Maxims of Maximally Efficient SAS Programmers
が面白いです
禅は対照的で,なんかマッチョな感じ(本人も)なのですが

https://communities.sas.com/t5/SAS-Communities-Library/Maxims-of-Maximally-Efficient-SAS-Programmers/ta-p/352068


ログ読め,ドキュメント読め,データ見ろ とかほんと,もっともなこといってます
13番とか結構,ぐっときました
「今はそれを学ぶ時間がない」と決して言うな は グサッときますよね

Maxim 13

When you're through learning, you're through.

(Will Rogers, John Wooden)

As long as you keep your ability and will to learn, you are alive. When you stop learning, you may not be dead, but you start smelling funny. Never say "I don't have the time to learn that now". The time to learn is NOW.

"Much have I learned from my teachers, more from my colleagues, but most from my students." - from the Talmud.

Nオブザベーション先の値を取得する話②条件付き先読み

例えば

data temp;

ID=1;time=input("10MAR20:07:51:00",datetime.);val=9;output;

ID=1;time=input("10MAR20:07:52:00",datetime.);val=8;output;

ID=1;time=input("10MAR20:07:53:00",datetime.);val=10;output;

ID=1;time=input("10MAR20:07:54:00",datetime.);val=11;output;

ID=1;time=input("10MAR20:07:55:00",datetime.);val=13;output;

ID=1;time=input("10MAR20:07:56:00",datetime.);val=9;output;

ID=1;time=input("10MAR20:07:57:00",datetime.);val=9;output;

ID=1;time=input("10MAR20:07:58:00",datetime.);val=8;output;

ID=1;time=input("10MAR20:07:59:00",datetime.);val=7;output;

ID=1;time=input("10MAR20:08:00:00",datetime.);val=6;output;

ID=1;time=input("10MAR20:08:01:00",datetime.);val=5;output;

ID=1;time=input("10MAR20:08:02:00",datetime.);val=4;output;

ID=1;time=input("10MAR20:08:03:00",datetime.);val=10;output;

ID=1;time=input("10MAR20:08:04:00",datetime.);val=11;output;

ID=2;time=input("10MAR20:07:51:00",datetime.);val=8;output;

ID=2;time=input("10MAR20:07:52:00",datetime.);val=9;output;

ID=2;time=input("10MAR20:07:53:00",datetime.);val=8;output;

ID=2;time=input("10MAR20:07:54:00",datetime.);val=9;output;

ID=2;time=input("10MAR20:07:55:00",datetime.);val=9;output;

ID=2;time=input("10MAR20:07:56:00",datetime.);val=8;output;

ID=2;time=input("10MAR20:07:57:00",datetime.);val=12;output;

ID=2;time=input("10MAR20:07:58:00",datetime.);val=12;output;

ID=2;time=input("10MAR20:07:59:00",datetime.);val=14;output;

ID=2;time=input("10MAR20:08:00:00",datetime.);val=8;output;

ID=2;time=input("10MAR20:08:01:00",datetime.);val=15;output;

ID=2;time=input("10MAR20:08:02:00",datetime.);val=13;output;

ID=2;time=input("10MAR20:08:03:00",datetime.);val=10;output;

ID=2;time=input("10MAR20:08:04:00",datetime.);val=10;output;

format time datetime.;

run;




















こんな感じに,1分刻みに値が記録されたデータがあったとします
で例えば値が10未満のものにフラグをたてるとかなら

if val < 10 then FL="Y";

でOKです

ところが難しいのが,
10未満の値が観測されてから後5分以内に10以上の値が観測されなかった場合のみ,その時点にYをたてて.といわれたらどうしましょう?

簡単なのはデータのソート順をひっくり返して後ろからみるとかなのですが
今回,仮の前提としてデータが巨大で,安易にひっくり返しソートがうてないという状況としてみましょう

そうなるとSASの1obsよんで1obs出力する方法だと先読みしにくいのですね
完全に1分刻みが確定してるなら前回の方法で5obs先までみればいいのですが
色々な要因で測定メッシュが確定しない場合もあるかもしれません(というかよくあります…)

そうなるとちゃんと時間と条件を加味して先読みしたいと.
そういう時にハッシュオブジェクトが活きてくるんですね

必要最低限な変数に絞って自己参照していけばいいので

data wk1;
set temp;
if _N_=1 then do;
call missing(of _time _val);
    declare hash h1(dataset:"temp(rename=(time=_time val=_val))",multidata:"yes",hashexp:16);
        h1.definekey("ID");
        h1.definedata("_time","_val");
        h1.definedone();
    end;
if val < 10 then FL="Y";
 do while ( h1.do_over()=0 );
      if 0<_time-time<=300 then do;
            if _val>=10 then call missing(FL);
      end;
 end;
drop _val _time;
run;

じゃっかん無駄のある処理(自分より前のデータからもいちいちスタートしてる)ですが
とりあえず単純化した例を示したかったのです

















multidataでIDでkey重複した状態でハッシュオブジェクトを定義しておいて
do_overメソッドで同IDのデータを総なめして,5分以内にアンチ条件に合致するレコードがあればフラグをおると.
そうすると結果的に5分間条件持続の確認されたものにフラグがたつと(正確にはレコード終わりの終了5分前のところからは5分間確認できてなくてもフラグがたつ)

なんか臨床の分野でもePROやデバイスが増えたり,time to eventのパラメータ条件も昔よりちょっと複雑になった気がしてて,最近この手の処理を書く機会が増えたなーって印象です