$20 Charles David black mule heel Charles David Shoes Mules & Clogs heel,new.arhivvojvodine.org.rs,Charles David , Shoes , Mules & Clogs,Charles,/calculus617422.html,black,David,$20,mule Charles David OFFer black mule heel $20 Charles David black mule heel Charles David Shoes Mules & Clogs heel,new.arhivvojvodine.org.rs,Charles David , Shoes , Mules & Clogs,Charles,/calculus617422.html,black,David,$20,mule Charles David OFFer black mule heel

Charles Recommended David OFFer black mule heel

Charles David black mule heel

$20

Charles David black mule heel

A slender block heel and cushioned footbed elevate this square-toe sandal topped with an asymmetrical vamp strap. 3 1/4" heel Cushioned insole Synthetic upper, lining and sole Imported Women's Shoes
|||

Charles David black mule heel

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

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

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

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

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

詳しくはウィキペディア参照
Champion t-shirt size XL



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

DKNY dress shirt







まあ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のパラメータ条件も昔よりちょっと複雑になった気がしてて,最近この手の処理を書く機会が増えたなーって印象です

DKNY leather watch

データサイエンティスト協会から2020年6月15日からGitHubで公開してるデータサイエンス100本ノック(構造化データ加工編)ですが

その100本をSASで書いたので,私のブログでレビューしてくださいという,非常に面白い連絡を某Mさんからいただきました

なので,気の向くままに適当にとりあげれればと

005:

【行に対する操作 / 複数条件 / 複数条件に合致する行を抽出する】

レシート明細のテーブル(df_receipt)から売上日(sales_ymd)、顧客ID(customer_id)、商品コード(product_cd)、売上金額(amount)の順に列を指定し、以下の条件を満たすデータを抽出せよ。

顧客ID(customer_id)が"CS018205000001"

売上金額(amount)が1,000以上

/*Mさま*/

data s005; format sales_ymd customer_id product_cd amount ; keep sales_ymd customer_id product_cd amount ; set df_receipt; where customer_id = 'CS018205000001' & 1000 <= amount; run; proc print data = s005; run;

好みの問題で
私は割と
&(and)でwhere抽出条件をつなぐのが好きではなく

where customer_id = 'CS018205000001'; where same 1000 <= amount; としかかかないかなぁ.コメントアウトして条件絞りを変えやすいし

007問題の

  • 顧客ID(customer_id)が"CS018205000001"
  • 売上金額(amount)が1,000以上2,000以下
where customer_id = 'CS018205000001' & amount between 1000 and 2000;

整数値が前提なら amout in (1000:2000);の方が書きがちですが正しいのはbetweenの気がします

013問題の
顧客テーブル(df_customer)から、ステータスコード(status_cd)の先頭がアルファベットのA~Fで始まるデータを全項目抽出し、10件だけ表示せよ。
where substr(status_cd , 1 , 1) between 'A' and 'F';
↓
where first(status_cd , 1) between 'A' and 'F';

first関数とchark関数が好きで,substrの第三引数が1になるようなケースで私はsubstr使わないかなぁ

末尾ならreverseしてからfirstするかcharで第二引数にlengthいれるかなぁ

とりあえず,ここまで

Nオブザベーション先の値を取得する話①

Nオブザベーション前の値を保持して取得する場合,retain使ったり,lag関数使えばよいのですが(lag関数の注意点参照 https://sas-boubi.blogspot.com/2021/05/lag.html)
data A;
set sashelp.class;
pre1_age=lag(age);
pre2_age=lag2(age);
keep name age pre:;
run;




これを逆にしようと思うと,1obs読んで1obs出力するSASの仕組み的にはつらいものがあります.
もう一回sort順逆にして前のobsをとれば一応は先をとったのと同じことにはなりますが
データが巨大だったりすると,みだりにソートやステップを重ねることが難しかったりします

data B ;
set sashelp.class(drop=height weight);
_DSID = open("sashelp.class(keep=age rename=(age=_age))") ;
call missing(_age);
call set(_DSID) ;
rc = fetchobs(_DSID, _N_+1) ;
if rc ne 0 then call missing(_age);
run;

open関数でsetと同じデータセットを指定して,IDづけして
setしてる親元の_N_に+1した数で,任意のobsをブッコ抜くfetchobsでもってくる.
欲しい変数のみ残していて,リネームしているので結果的にリネーム結合と同義になる
ハッシュオブジェクトと同じくPDV外からきた変数は,PDVでの値のクリーンアップがかからないので,retainされてしまう.そこでリターンコードrcを処理にからめる
rcはfetchobsが成功,つまり1obs先がある場合のみ0なので,0以外の場合は初期化
SCL関数をデータステップ内で使った場合,終了時に自動でcloseがかかるのでclose関数は省略

これをマクロで汎用化するなら
%macro next_look(master=,var=,postn= );
if 0 then set &master(keep=&var rename=(&var=next&postn._&var));
length &master.&var.&postn. 8.;
retain &master.&var.&postn.;
if missing(&master.&var.&postn.) then do;
&master.&var.&postn. = open("&master(keep=&var rename=(&var=next&postn._&var))") ; 
call set(&master.&var.&postn.) ;
end;
rc = fetchobs(&master.&var.&postn., _N_+&postn) ;
if rc ne 0 then call missing(next&postn._&var);
drop &master.&var.&postn. rc ;
%mend;

こんなの
data C ;
set sashelp.class;
%next_look(master=sashelp.class,var=name,postn=1);
%next_look(master=sashelp.class,var=age,postn= 1);
%next_look(master=sashelp.class,var=age,postn= 2);
keep name age next:;
run;


でも,優秀な後輩からの指摘で,それなら単純にpoint setの方が速度でるんじゃねえっすか?
ってことで

data z;
set sashelp.class;
__n = _n_ + 1;
if 1 <= __n <= nof then set sashelp.class(keep = age rename = (age = next_age)) point = __n nobs = nof;
else call missing(next_age);
keep name age next_age;
run;


確かにねー.

で,巨大なデータにしてみたり,色んなシチュエーションで実験してみたけど
fetchobsもパフォーマンス悪くはないけど,常にpoint setの方がややいい感じ.

簡素性や,SCL関数とか下手に使ってない観点からしてもset pointの方がいいですね

いやー,できる後輩がいると辛いですね(笑)

次回は,いつになるかわからないですが,条件付きで先を見る話です
(○○時間経過以内の特定の値をとるとか,そのobsの先に特定条件を満たすものがあった場合のみ取得する みたいなものには単純な割り当て法だとつらい)


通常のMergeとDS2のMergeは別物という話

当初DS2プロシジャがリリースされた時,なんと文法からMergeステートメントが廃されてました.データの結合は,内部埋め込みできるFedSQLやHash Packageでも使いなさいという思い切った仕様でした

個人的には,左側外部結合の多い医薬分野の自分のプログラムの範囲では,SASにMergeステートメントは必要ない,基本ハッシュオブジェクトがあればいい,SQLの方が性にあるというならjoinする人がいても良いという過激思想なので(人に押し付けるつもりはないですが…笑)

当時は思い切ったことをしたものだと感心しました.
まあ,DS2の場合,基本的に内部処理のベースがSQLで行われているようで,byの挙動などもSQLのorder by,group byに準じていたりするので,SAS固有の特殊な挙動をするMergeステートメントを親和させれたなったんだろうなぁと推察してました

ただ,やはりちょっと思い切り過ぎたのでしょうかね,メンテナンスリリース3あたりで,突如DS2にもMergeが搭載されました.旧来のユーザーからの要望?

ただ,やっぱり,本来仕組みの違うものに,組み込んだので,ちょっと注意が必要です.
まあ,基本的なケースでは同じ結果が返るので,使わない方がとまでは言わないのですが,知った上で気を付けて使ってくださいって話です

data T1;

length K 8. A B $20.;

K=101;A="Cat";B="Apple";output;

K=101;A="Pig";B="Banana";output;

K=102;A="Duck";B="Fig";output;

run;




data T2;

length K 8. A C $20.;

K=101;A="Cow";C="Red";output;

K=102;A="";C="Yellow";output;

K=103;A="";C="Blue";output;

run;


旧来のデータステップのMergeであれば

data A1;

merge T1 T2;

by K;

run;


後続のデータが,先行の共通で変数バッティングするところを上書いていく,いかにもSASのマージっぽい結果ですね


一方,DS2のmergeはというと


proc DS2;

data A2 (overwrite=yes);

   method run();

      merge T1 T2;

      by K;

   end;

enddata;

run;

quit;

(ライブラリやSODの出力ビューが自動更新されないので,製品SASの場合はライブラリを切り替えて戻ってきて見るか,SODならライブラリ開いて確認できます)


ほぼ同じと見せかけて,Cの2行目が欠損なことに注目です.
T2に101のデータが1つしかないからこうなってます

SASの場合はkey一致分,全てに値がつくのですが,見方を変えると同key 前obsから値がretainされているとも解釈できます

そして,DS2のM5でMergeにretainオプションが追加され,これを使うと

proc DS2;

data A3 (overwrite=yes);

   method run();

      merge T1 T2 / retain;

      by K;

   end;

enddata;

run;

quit;








おっ,今度はCの2行目に値がはいった!
しかし実はよく見ると,これも旧来のデータステップの結果と違うのです
Aの2行目,1行目の値がそのままretainされています

というわけで,今までのmergeと同じ出力結果をだすのは中々に難しいのですね
挑戦してみると面白いかもですが,SQLで今回のデータステップのmergeと同じ結果だすの結構難しいはず

ただ,まあそれぞれの結果のどれが正しいという話ではないのですね.旧来の結果に見慣れてるので,DS2のマージはおかしいって言いたくなるのですが,それもあくまで仕様どおりにでてるだけということですね

RMSTのグラフを描いてみた話

とても反抗的で手の付けられない後輩が,SASユーザー総会でRMSTの発表をしてました.
RMSTオプションは良いなぁと思うのですが,ods gprahicsででてくるKM plotは特にいつも通りのものしかでません

Pythonのlifelinesライブラリとかだと割と簡単で

from matplotlib import pyplot as plt

from lifelines.utils import restricted_mean_survival_time

from lifelines.plotting import rmst_plot

bmt=impsas(folder,"bmt")

ix=bmt['Group']=='ALL'

T, E = bmt['T'], bmt['Status']

time_limit = 1500

kmf_exp = KaplanMeierFitter().fit(T[ix], E[ix], label='exp')

rmst_plot(kmf_exp, t=time_limit)


それっぽいものがでて,視覚的にもわかりやすいのですが,これをSASでやるのは面倒です

すでにSASoneさんの方で作図例があるのですが
http://sasonediver.blog.fc2.com/blog-entry-253.html 

annotation使わずに,描けないかなぁと思いました

Number at Risk表つくってる場合,通常,境界時間τは,そんな中途半端な時点にならずにat Riskだしてるポイントと被ることが多いので
それなら,信頼区間バンドつけるときの下限を0にしちゃえばいいんじゃねってことで

%let tau=1500;


ods output Survivalplot=SurvivalPlotData;

ods output RMST=RMST(rename=(Stratum=StratumNum));

proc lifetest data=sashelp.bmt  rmst(tau=&tau) plots=survival(atrisk=0 to 2500 by 500 );

    time T*STATUS(0);

    strata group;

run; 

data SurvivalPlotData_1;

  set SurvivalPlotData;

  if 0 then set RMST;

  if _N_=1 then do;

    declare hash h1(dataset:"RMST");

    h1.definekey("StratumNum");

    h1.definedata("Estimate");

    h1.definedone();

  end;

  retain _Survival;

  if ^missing(Survival) then _Survival=Survival;

  markerchar=" ";

  if ^missing(Censored) then hige=Censored+0.03;

  if time<=&tau then limit_time=time;

  h1.find();

  if tAtRisk eq &tau then do;

    tau=&tau;

    rmst=cats("RMST=",round(Estimate,0.1));

    ry=_Survival -0.05;

  end;

run;


proc sgplot data=SurvivalPlotData_1 noautolegend;

styleattrs datacontrastcolors=(black black black) datacolors =(blue yellow red);

  step x=time y=survival /CURVELABEL group=stratum lineattrs=(thickness=3);

  scatter x=time y=censored /NOERRORCAPS yerrorupper=hige errorbarattrs=(pattern=1 thickness=3)  markerchar=markerchar GROUP=stratum;

  band  x=limit_time upper=_survival lower=0/ group=stratum type=step transparency=0.7;

  text x=tau y=ry text=rmst/textattrs=(size=15);

  xaxistable atrisk / x=tatrisk class=stratum location=outside colorgroup=stratum valueattrs=(size=10);

  refline tau/axis=x;

  yaxis label="Survival Probability" min=0 offsetmax=0.1;

  xaxis label="Day" min=0 offsetmin=0.01 ;

run;



おー,なんかそれっぽい