2024年6月2日日曜日
【PROC FORMAT】数値フォーマットの落とし穴
2023年5月7日日曜日
「PROC REPORT」の「COMPUTE」がうまく動かないんだけど②
- まず、XをORDER変数にしています。またORDER変数が欠損値のオブザベーションも出力対象とするため、MISSINGオプションを指定。
- 次にCOMPUTEで「if x=. then z="Y"」として、Xが欠損値のオブザベーションに"Y"のフラグを立てたんですが、、想定では1、2オブザベーション目だけに"Y"が入るはずなのに、4、6オブザベーション目にも"Y"が入っている?!

あと、失敗例①を知っちゃうと、なんとなく以下のようになるのでは?と思っちゃいません?(失敗例①で「X=.」と判定されたオブザベーションが、今回「X=2」になると思ってしまった)

ORDER変数(GROUP、ANALYSIS、ACROSS変数とかも?)に対して、COMPUTEで値をいじると、うまく絡み合ってくれなくて、想定外の結果になる場合があるのでご注意ください。
そもそも元の変数値をPROC REPORT上でいじるのって、あまりやらない、というか良くないのかも?
2023年5月4日木曜日
2023年4月22日土曜日
サブセット化IFでありがちな落とし穴
SASプログラマ歴が長いと、みんなこれ経験してるんじゃなかろうか。
😕失敗例1: 「_N_」と組み合わせて失敗しがち
変数Xに1~連番を入れたいのに、なんかおかしい。
解説
- まずデータステップは内部の動きとして、
- 次に「_N_」は内部の動きとして、
_N_=1 のオブザベーションがたまたまサブセット化IFによって「処理継続・出力」の対象外になっているので、「IF _N_=1 THEN DO; ~END;」の中の処理も動いていません。
上の例ではエラーが出て分かりやすいですが、書き方によってはエラーが出ない場合もあって失敗に気づかない可能性もあるので要注意です。
😕失敗例2: 「END=オプション」と組み合わせて失敗しがち
data dt3; set sashelp.class end=_eof; if age=12; if _eof then y=1; keep age y; run; |
フラグ立ってないじゃないか!
これは「END=オプション」の挙動を勘違いしていると起こりやすいです。
「END=オプション」はSET文・WHERE文で読み込まれる最後のオブザベーションに作用しますが、その最後のオブザベーションがサブセット化IFによって「処理継続・出力」の対象外になっているためです。
あと思いつくのが、以下で紹介している「FIRST.BY変数」「LAST.BY変数」も、サブセット化IFと組み合わせると、同様の原理で意図しない結果になりやすいですね。
「FIRST.BY変数」と「LAST.BY変数」で、グループ毎の最初と最後のオブザベーションを特定する。
2023年1月1日日曜日
SASで年賀状2023
SASで年賀状をつくりました(ほぼ「SASで年賀状」の焼き直し)
2023年もどうぞよろしくお願いいたします!

年賀状をつくるプログラム (SAS9.4M7で動作確認)
*** 描画用のパーツ ; data rabbit; x1= 0; y1= 0; x2= 0; y2=-6.5; x3=-0.31; y3= 1.2; x4= 0.31; y4= 1.2; x5= 0; y5= 0.15; x6= 0; y6= 0.7; x7=-0.5; y7= 5; x8= 0.5; y8= 5; x9=-0.5; y9= 5.5; x10=0.5; y10=5.5; x11=-0.8; y11=0.1; x12=0.8; y12=0.1; run; *** 描画 ; title ; ods graphics on / height=10cm width=14.8cm; proc sgplot data=rabbit noautolegend; styleattrs wallcolor=khaki; /* Text */ inset "HAPPY NEW YEAR" / position=top textattrs=(color=brown size=31cm); inset "20" / position=bottomleft textattrs=(color=brown size=140cm ); inset "23" / position=bottomright textattrs=(color=brown size=140cm ); /* rabbit */ scatter x=x1 y=y1 / markerattrs=(symbol=circlefilled size=3cm color=white); scatter x=x2 y=y2 / markerattrs=(symbol=circlefilled size=4cm color=white); scatter x=x3 y=Y3 / markerattrs=(symbol=circlefilled size=0.2cm color=black); scatter x=x4 y=Y4 / markerattrs=(symbol=circlefilled size=0.2cm color=black); scatter x=x5 y=y5 / markerattrs=(symbol=ArrowDown size=0.5cm color=black); scatter x=x6 y=y6 / markerattrs=(symbol=TriangleDownFilled size=0.2cm color=black); scatter x=x7 y=y7 / markerattrs=(symbol=CircleFilled size=1cm color=white); scatter x=x8 y=y8 / markerattrs=(symbol=CircleFilled size=1cm color=white); scatter x=x9 y=y9 / markerattrs=(symbol=CircleFilled size=1cm color=white); scatter x=x10 y=y10 / markerattrs=(symbol=CircleFilled size=1cm color=white); scatter x=x7 y=y7 / markerattrs=(symbol=CircleFilled size=0.7cm color=mistyrose); scatter x=x8 y=y8 / markerattrs=(symbol=CircleFilled size=0.7cm color=mistyrose); scatter x=x9 y=y9 / markerattrs=(symbol=CircleFilled size=0.7cm color=mistyrose); scatter x=x10 y=y10 / markerattrs=(symbol=CircleFilled size=0.7cm color=mistyrose); scatter x=x11 y=y11 / markerattrs=(symbol=CircleFilled size=0.4cm color=pink); scatter x=x12 y=y12 / markerattrs=(symbol=CircleFilled size=0.4cm color=pink); xaxis min=-3 max=3 display=none; yaxis min=-5 max=1 display=none; run; |
2022年11月29日火曜日
SQLプロシジャのINTO句の注意点
SQLのINTO句を使用する際の注意点を2つ紹介したいと思います。
1. 変数にフォーマットが割り当てられている場合
data DT1; format X yymmdd10.; X=1; run; proc sql noprint; select X into :X trimmed from DT1; quit; %put &X; ログ 1960-01-02 |
マクロ変数にはフォーマットをあてた値が格納されるようです。この挙動、SASのリファレンスに書いてない気がする。。
proc sql noprint; select X format=best16. into :X trimmed from DT1; quit; %put &X; ログ 1 |
2. 数値変数かつフォーマットが割り当てられていない場合
data DT2; X=123.456789; run; proc sql noprint; select X into :X trimmed from DT2; quit; %put &X; ログ 123.4568 |
デフォルトの挙動として「BEST8.」の出力形式で格納されるようです。
状況によっては、もとの数値がだいぶ削られちゃいますね(これはINTO句に限ったことではないですが。。)
2022年7月9日土曜日
【PROC FORMAT】CNTLIN=オプションの落とし穴
まず、前提知識として「PROC FORMAT入門9 : CNTLIN=オプション 」をご参照下さい。
そして、ここから本題。以下の例をご覧ください。
失敗例
①まず「データセット」から「フォーマット」を生成
②実データに先ほどのフォーマットを当ててみる。
あれ?!
フォーマット当てた結果、2行目の欠損値に「その他」が当てられてしまってる?!
解説
「CNTLIN=」に使用するフォーマット生成用のデータセットに注目
実は上の "OTHER" が、範囲指定としての「OTHER」として解釈されてしまってます(範囲指定?という方は「PROC FORMAT入門3 : 範囲の指定」を参照)
要は、以下のようにFORMATプロシジャで①みたいな定義をしたつもりが、②の定義をしてしまった、ということ。
proc format; /* ① */ value $TEST_ "OTHER" = "その他" ; run; proc format; /* ② */ value $TEST_ OTHER = "その他" ; run; |
余計なことをしてますね。。
ちなみに、"OTHER" 以外にも、FORMATプロシジャの予約語的なキーワードは気を付けた方が良いです(色々実験してみたところ、"LOW", "HIGH" なども同様に範囲指定の意味として解釈されてしまいました。たぶん他にもあると思います)
解決策
今回の例では "OTHER" を範囲指定の意味ではないと明示してあげる必要があります。上でフォーマットを作り直したので、再度このフォーマットをあてたデータセットを見てみましょう。

はい!さっきまで2行目の欠損値が「その他」になっちゃってましたが、今度は問題なさそうです。
最後に
私が把握しているのは "OTHER", "LOW", "HIGH" のみで、その他にFORMATプロシジャの予約語的なキーワードに引っかかるものがあるかもしれません。
今回の場合だと"OTHER"に対してHLOという変数を作ってNullに設定することで解決しましたが、その他の私が把握していないキーワードに対して、各自SASのリファレンスや「PROC FORMAT入門9 : CNTLIN=オプション」などを参考にして、他の必要な変数を設定してあげる必要があります。
2022年6月29日水曜日
SASで花火を描く
暑中お見舞い申し上げます。

SASで花火描いてみました。三角関数をつかってるだけの単純なプログラムです。
/* 花火描画用データ */ data fireworks; rad = constant('pi')/180; call streaminit(777); do i = 1 to 5; xpos = rand("uniform",-30,30); ypos = rand("uniform",-30,30); do rmax = 0, 36; do r = 0 to rmax by 4; do a = 0 to 360 by 15; x=cos(a*rad)*r+xpos; y=sin(a*rad)*r+ypos; output; end; end; end; end; run; /* 花火描画 */ title; ods graphics / height=20cm width=20cm; options animation=start animduration=1.2 printerpath=gif; ods noresults; ods printer file="出力するGIFファイルのフルパスをここに指定。ファイル名は「fireworks.gif」とか適当に" ; %macro m_fireworks(i=,color=); proc sgplot data=fireworks aspect=1 noautolegend; styleattrs wallcolor=midnightblue; scatter x=x y=y / markerattrs=(symbol=circlefilled color=&color size=0.1cm ); xaxis min=-50 max=50 display=none; yaxis min=-50 max=50 display=none; inset "Summer Greetings to All."/ position=bottom textattrs=(color=lightblue size=25cm family="Arial"); by rmax; where i=&i; run; %mend; %m_fireworks(i=1,color=yellow); %m_fireworks(i=2,color=skyblue); %m_fireworks(i=3,color=lightgreen); %m_fireworks(i=4,color=yellow); %m_fireworks(i=5,color=lightpink); ods printer close; ods results; options animation=stop; ods graphics / reset=all; |
2022年3月31日木曜日
「PUT "WARNING-"」みたいな書き方。
今回は、以下で紹介した記事のおまけ的内容。
最初に
上の記事のおさらい。
以下サンプルのように「NOTE」「WARNING」「ERROR」のいずれかのキーワードとコロン「:」をつけてPUTすると、ログ上に色つきの独自メッセージを表示できる。
data _null_; put "NOTE: 独自メッセージ1"; put "WARNING: 独自メッセージ2"; put "ERROR: 独自メッセージ3"; run; ログ ![]() |
本題
上の例でコロン(:)ではなくハイフン(-)に変えると、、
data _null_; put "NOTE- 独自メッセージ1"; put "WARNING- 独自メッセージ2"; put "ERROR- 独自メッセージ3"; run; ログ ![]() |
活用例
これは、以下のような使い方が出来ます。
data _null_; put "WARNING: 独自メッセージ1"; put "WARNING- 独自メッセージ1の続き"; run; ログ ![]() |
ただし、1つだけ残念ポイント。
%PUTで同様のテクニックを使おうとすると、、
%put WARNING: 独自メッセージ1; %put WARNING- 独自メッセージ1の続き; ログ ![]() |
ログを見てみると、間にプログラムの文が挟まっていて、WARNINGメッセージが分割されて見えてしまうところ。残念よ。
2022年2月11日金曜日
「PUT 変数名=」という書き方
PUTステートメントの基本的なテクニックの紹介。
基本的なテクニック
data test; set sashelp.class; if age=13 then put height=; run; ログ height=56.5 height=65.3 height=62.5 |
以下のように、ちょっと勿体ない書き方をしがち。
ちょっと勿体ない例
data test; set sashelp.class; if age=13 then put "height=" height; run; ログ height=56.5 height=65.3 height=62.5 |
2021年12月5日日曜日
「ODS OUTPUT」で変数属性が自動調整される件
「PROC SQL」と「ODS OUTPUT の PERSIST=RUN」のコンボ技 の記事内で
ODS OUTPUTで出力すると変数属性(変数名、length、formatなど)が自動調整される場合がある
と言ったんですが、じゃあ実際どう自動調整されるのか。
結論からいって、
- 変数属性は自動調整されるけど、データの中身は無事のよう(リファレンスに詳しい挙動が書いてないので、環境やバージョンによって挙動が同じかは不明。。各自で挙動確認してみてください)
- 私はこのコンボ技を単純な頻度集計で使う程度で支障ないですが、変数属性が自動調整されてしまうのがまずい状況下では、ご注意下さい。
(実行日: 2021/12/03, 実行環境: SAS Ondemand for Academics [SAS 9,4M6])
/* d1 */ data d1; length a b $10. c e 8.; label c = "test1"; format c yymmdd10.; a="aaa"; b="bbb"; c=1; e=1; run; /* d2 */ data d2; length a $20. b c d e 8.; label c = "test2" d="test3"; format c d time5.; a="aaa"; b=1; c=1; d=1; e=1; run; |
「PROC SQL の SELECTステートメント」の結果を「ODS OUTPUT」を使ってデータセットに出力してみます。
例①「データセットd1」を使ってODS OUTPUTで出力
ods select none; ods output SQL_Results=out1; proc sql; select * from d1; quit; ods output close; ods select all; 各データセットの変数属性 データセットd1 ![]() データセットout1 (ODS OUTPUTで出力したやつ) ![]() |
ods select none; ods output SQL_Results=out2; proc sql; select * from d2; quit; ods output close; ods select all; 各データセットの変数属性 データセットd2 ![]() データセットout2 (ODS OUTPUTで出力したやつ) ![]() |
例③「データセットd1とd2」を使って「ODS OUTPUT の PERSIST=」でプロシジャ内の結果を結合して出力
ods select none; ods output SQL_Results(persist=run)=out3; proc sql; select * from d1; select * from d2; quit; ods output close; ods select all; 各データセットの変数属性 データセットout1 (例①で、d1を使ってODS OUTPUTで出力したやつ) ![]() データセットout2 (例②で、d2を使ってODS OUTPUTで出力したやつ) ![]() データセットout3 (例③で、「ODS OUTPUT の PERSIST=」で出力したやつ) ![]() |
- formatを割り当てていない数値変数はなぜかbestフォーマットが割り当てられてた(これはどちらかというとSQLプロシジャ側のODS出力時の設定によるものと思われる。このようにODS出力時に勝手にformatが割り当てられる場合がある。割り当てられるformatはプロシジャによって様々)
- 例①の変数Eなどを参照
- 「ODS OUTPUT の PERSIST=」で出力結果を結合すると、
- 同じ変数名(かつ文字変数)でlengthが違う場合 = 大きい方に合わせられた
- 例③の変数Aなどを参照
- 同じ変数名で型が違う場合 = 片方の変数がrenameされた。
- 例③の変数Bなどを参照(結合後、変数Bと変数B2に分かれてる)
- 同じ変数名でラベルが違う場合 = 最初のデータセットのラベルになった
- 例③の変数Cなどを参照
- 同じ変数名でフォーマットが違う場合 = formatが削除された
- 例③の変数Cなどを参照
- 片方のデータセットにしか変数がない場合 = 変数がkeepされた。あとformatを割り当てた変数はちゃんとformatが残った。
- 例③の変数Dなどを参照
2021年11月15日月曜日
「PROC SQL」と「ODS OUTPUT の PERSIST=RUN」のコンボ技
まず今回のコンボ技を理解するには「ODS OUTPUTのPERSIST=RUN」の知識が必要なので、先に以下リンク記事をご覧ください。
「ODS OUTPUTで出力結果を結合する②PERSIST=RUN」
それでは本題。以下のプログラムをご覧ください。
- PROC SQLは対話型プロシジャなので、1つのプロシジャで複数の集計をいっぺんに行ってます。
- 「ODS OUTPUTのPERSIST=RUN」で対話型プロシジャ内のそれぞれの集計結果について、出力オブジェクトの名前が共通するものを縦結合しています。(今回の場合はすべての集計で、出力オブジェクトの名前が「SQL_Results」という名前で共通している)
- 「_RUN_」という変数が勝手に作られました。RUNグループのIDと推測されます。SQLの場合は「RUNステートメント」は記述不要なので、実行グループ(セミコロン「;」で終わる文)のIDですね。
- ちなみにPROC SQLで「NOPRINTオプション」を設定するとODS OUTPUTが使えない(データセットに出力されない)ので注意
ただし私自身、単純な集計でしか使ったことないので、各自利用する際は結果が希望通りのものになるか、ご確認下さい。
また、ODS OUTPUTで出力すると変数属性(変数名、length、formatなど)が自動調整される場合があるのでご留意ください。(以下リンク記事参照)
2021年11月1日月曜日
ODS OUTPUTで出力結果を結合する②PERSIST=RUN
前回の続きで、今回はODS OUTPUTの「PERSIST=RUN」というオプションを紹介します。
まずは以下の例をご覧ください。
データセット「SASHELP.CLASS」と「SASHELP.BMT」の定義情報をデータセットOUT1に出力しています。特に問題なく出力できています。
ここから本題。DATASETSプロシジャは対話型プロシジャです(対話型プロシジャについては以下参照)
https://sas-boubi.blogspot.com/2018/01/runquit.html
そして今度は、説明のため、以下の通りあえて対話型プロシジャを意識した書き方に変えて実行してみます。
😟失敗例
対話型プロシジャの結果のうち、最初のRUNグループの結果しかデータセットに出力されませんでした。
😄解決策1(グループ毎にODS OUTPUTを記述)
上の例では「RUNグループ」毎にODS OUTPUTを記述して、「RUNグループ」毎にデータセットを出力しています。プロシジャの中にODS OUTPUTを記述しているので、ちょっと違和感を感じるかもしれませんが、ちゃんと動きます。
もうひとつ解決策。これが紹介したかったんですが「PERSIST=RUN」というオプション。
以下のように、対話型プロシジャ内で、出力オブジェクトの名前が一緒であれば(今回の場合は「Variables」)、結合してデータセットに出力することが出来ます。
😄解決策2(PERSIST=RUN)
PERSIST=オプションはSQLプロシジャと相性が良いので、今度その辺りも紹介したいと思います。
2021年10月26日火曜日
ODS OUTPUTで出力結果を結合する①PERSIST=PROC
ODS OUTPUTの「PERSIST=オプション」について、2回に分けて詳しく紹介していきます。
まず1回目は「PERSIST=PROC」について。
以下の失敗例をご覧ください。
ODS OUTPUTの中にプロシジャを複数記述したけど、最初のプロシジャの結果しかデータセットに出力されませんでした。
😟失敗例
そこで「PERSIST=PROC」というオプションの出番です。
以下のように、プロシジャ間で出力オブジェクトの名前が一緒であれば(今回の場合は「Moments」)、結合してデータセットに出力することが出来ます。
😄成功例(PERSIST=PROC)
ただし、以下のようにPERSIST=オプションと一緒に「BYステートメント」が使われていると、WARNINGが出て、正しい結果が得られないようです。
😞注意(BYステートメントは使えないっぽい)
ods output Moments (persist=proc) = out1; proc univariate data=sashelp.cars; var msrp; by make; run; proc univariate data=sashelp.cars; var invoice; by make; run; ods output close; --- LOG -------------- WARNING: ODSでは、データセットWORK.OUT1をRUNグループ間で保持できません。BY変数が含まれているからです。データセットは第1 RUNグループが終了すると終了します。 |
To be continued...
https://sas-boubi.blogspot.com/2021/11/ods-outputpersistrun.html
2021年7月22日木曜日
Graph Template Language(GTL)入門:凡例の設定 (DISCRETELEGEND)
GTLで凡例を設定する「DISCRETELEGEND」と「MERGEDLEGEND」を紹介します。
まずは例を示して、その後構文を紹介したいと思います。
例
proc template ; define statgraph MYTEMP ; begingraph; layout overlay; scatterplot x=HEIGHT y=WEIGHT / group=SEX name="sca" ; regressionplot x=HEIGHT y=WEIGHT / group=SEX name="regr" ; discretelegend "sca" "regr" / title="凡例"; endlayout; endgraph; end; run; proc sort data=SASHELP.CLASS out=CLASS; by SEX; run; proc sgrender data=CLASS template=MYTEMP ; run; |
- 「SCATTERPLOT」で凡例の参照名 "sca" を定義しておく
- 「REGRESSIONPLOT」で凡例の参照名 "regr" を定義しておく
- 「DISCRETELEGEND」で参照名 "sca" と "regr" を指定してこれらの凡例を表示しています
- それぞれのPLOTステートメントで「GROUP=SEX」としているので、グループ変数をSEXとして凡例が表示されています。
構文
PROC TEMPLATE ; DEFINE STATGRAPH テンプレート名; BEGINGRAPH ; LAYOUTステートメント; PLOTステートメント / NAME = "参照名"; DISCRETELEGEND "参照名" / オプション; ENDLAYOUT; ENDGRAPH; END; RUN; |
- PLOTステートメントの「NAME=」で凡例の参照名を設定 (任意の名前でOK)
- DISCRETELEGENDで参照名を指定して、凡例を表示します。
DISCRETELEGENDのオプション (随時追加予定)
| 設定内容 | 設定 |
| 凡例タイトル | title = "タイトル" |
| 凡例タイトルのボーダー | titleborder = true | false |
| 凡例タイトルの書式 | titleattrs = ( family = 'フォント' size = サイズ color = 色 weight = bold /* 太字 */ ) |
| 凡例の書式 | valueattrs = ( family = 'フォント' size = サイズ color = 色 weight = bold /* 太字 */ ) |
| 凡例の書式で文字の大きさを変えたときに 凡例のマーカー等も大きくするか | autoitemsize = true | false |
| 凡例のボーダー | border = true | false |
| 凡例の表示順 (横順か縦順か) | order = rowmajor | columnmajor |
| 凡例の縦位置 | valign = top | bottom | center |
| 凡例の横位置 | halign = right | left | center |
| 凡例の背景色 | backgroundcolor = 色 |
| 「order = rowmajor」指定時に 水平方向に表示する凡例エントリの数 | across = 数 |
| 「order = columnmajor」指定時に 垂直方向に表示する凡例エントリの数 | down = 数 |
proc template ; define statgraph MYTEMP ; begingraph; layout overlay; scatterplot x=HEIGHT y=WEIGHT / group=SEX name="sca" ; regressionplot x=HEIGHT y=WEIGHT / group=SEX name="regr" ; mergedlegend "sca" "regr" / title="凡例"; endlayout; endgraph; end; run; proc sort data=SASHELP.CLASS out=CLASS; by SEX; run; proc sgrender data=CLASS template=MYTEMP ; run; |
- PLOTステートメントに「GROUP=」で、グループ化した値を凡例に表示している場合
- マーカーと線を結合する場合のみ?に有効らしい
- MERGEDLEGENDで指定可能なオプションはDISCRETELEGENDとほぼ同じ



































