ラベル ODS OUTPUT の投稿を表示しています。 すべての投稿を表示
ラベル ODS OUTPUT の投稿を表示しています。 すべての投稿を表示

2021年12月5日日曜日

「ODS OUTPUT」で変数属性が自動調整される件



「PROC SQL」と「ODS OUTPUT の PERSIST=RUN」のコンボ技 の記事内で


ODS OUTPUTで出力すると変数属性(変数名、length、formatなど)が自動調整される場合がある

と言ったんですが、じゃあ実際どう自動調整されるのか。


結論からいって、

  • 変数属性は自動調整されるけど、データの中身は無事のよう(リファレンスに詳しい挙動が書いてないので、環境やバージョンによって挙動が同じかは不明。。各自で挙動確認してみてください)
  • 私はこのコンボ技を単純な頻度集計で使う程度で支障ないですが、変数属性が自動調整されてしまうのがまずい状況下では、ご注意下さい。




「ODS OUTPUT 」で変数属性がどう自動調整されるのか見てみる。

実行日: 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で出力したやつ)


例②「データセットd2」を使って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などを参照

他にも私が把握しきれていないだけで「変数属性が自動調整」されるパターンはあると思います。


同じ変数名(かつ文字変数)のlengthが大きい方に合わせられたのと、同じ変数名で型が違う場合に勝手にrenameして両方保持してくれるのは、便利っていえば便利ですね。


2021年11月15日月曜日

「PROC SQL」と「ODS OUTPUT の PERSIST=RUN」のコンボ技



まず今回のコンボ技を理解するには「ODS OUTPUTのPERSIST=RUN」の知識が必要なので、先に以下リンク記事をご覧ください。

ODS OUTPUTで出力結果を結合する②PERSIST=RUN

 

それでは本題。以下のプログラムをご覧ください。

ods select none;
ods output SQL_Results(persist=run)=out1;

proc sql;
 select count(*) as c1 from sashelp.class;
 select count(*) as c1 from sashelp.cars;
 select count(*) as c1 from sashelp.baseball;
quit;

ods output close;
ods select all;


一応、細かく説明しておくと、

  • PROC SQLは対話型プロシジャなので、1つのプロシジャで複数の集計をいっぺんに行ってます。
  • 「ODS OUTPUTのPERSIST=RUN」で対話型プロシジャ内のそれぞれの集計結果について、出力オブジェクトの名前が共通するものを縦結合しています。(今回の場合はすべての集計で、出力オブジェクトの名前が「SQL_Results」という名前で共通している)
  • 「_RUN_」という変数が勝手に作られました。RUNグループのIDと推測されます。SQLの場合は「RUNステートメント」は記述不要なので、実行グループ(セミコロン「;」で終わる文)のIDですね。
  • ちなみにPROC SQLで「NOPRINTオプション」を設定するとODS OUTPUTが使えない(データセットに出力されない)ので注意



ID振って縦結合してくれるのが便利。他の方法でも同じこと出来るけど、こっちのほうが文が短く済む場合があります。


ただし私自身、単純な集計でしか使ったことないので、各自利用する際は結果が希望通りのものになるか、ご確認下さい。

また、ODS OUTPUTで出力すると変数属性(変数名、length、formatなど)が自動調整される場合があるのでご留意ください。(以下リンク記事参照)

「ODS OUTPUT」で変数属性が自動調整される件



2021年11月1日月曜日

ODS OUTPUTで出力結果を結合する②PERSIST=RUN



前回の続きで、今回はODS OUTPUTの「PERSIST=RUN」というオプションを紹介します。


まずは以下の例をご覧ください。

データセット「SASHELP.CLASS」と「SASHELP.BMT」の定義情報をデータセットOUT1に出力しています。特に問題なく出力できています。

ods output Variables = out1;

   proc datasets lib=sashelp;
      contents data=class;
      contents data=bmt;
   run;
   quit;

ods output close;

データセット OUT1



ここから本題。DATASETSプロシジャは対話型プロシジャです(対話型プロシジャについては以下参照)

https://sas-boubi.blogspot.com/2018/01/runquit.html


そして今度は、説明のため、以下の通りあえて対話型プロシジャを意識した書き方に変えて実行してみます。

😟失敗例

ods output Variables = out1;

   proc datasets lib=sashelp;

      /* RUNグループ1: データセットに出力される */
      contents data=class;
      run;

      /* RUNグループ2: データセットに出力されない */
      contents data=bmt;
      run;

   quit;

ods output close;

データセットOUT1

対話型プロシジャの結果のうち、最初のRUNグループの結果しかデータセットに出力されませんでした。




😄解決策1(グループ毎にODS OUTPUTを記述

proc datasets lib=sashelp;

   /* RUNグループ1 */
   ods output Variables = out1;
   contents data=class;
   run;

   /* RUNグループ2 */
   ods output Variables = out2;
   contents data=bmt;
   run;

quit;
ods output close;

データセットOUT1

データセットOUT2

上の例では「RUNグループ」毎にODS OUTPUTを記述して、「RUNグループ」毎にデータセットを出力しています。プロシジャの中にODS OUTPUTを記述しているので、ちょっと違和感を感じるかもしれませんが、ちゃんと動きます。



もうひとつ解決策。これが紹介したかったんですが「PERSIST=RUN」というオプション。

以下のように、対話型プロシジャ内で、出力オブジェクトの名前が一緒であれば(今回の場合は「Variables」)、結合してデータセットに出力することが出来ます。


😄解決策2(PERSIST=RUN

ods output Variables(persist=run) = out1;

   proc datasets lib=sashelp;

      /* RUNグループ1 */
      contents data=class;
      run;

      /* RUNグループ2 */
      contents data=bmt;
      run;

   quit;

ods output close;

データセットOUT1

注意点:
ODS OUTPUT内に対話型プロシジャを複数書いても、出力できるのは最初のプロシジャのみ。




ちなみに、SQLプロシジャも対話型プロシジャですが「RUNグループ」ではなく、以下のように実行グループ(セミコロン「;」で終わる文)毎に実行されます。そこが違うだけでSQLプロシジャについても今回紹介した2つの解決策を適用できます。

ods output SQL_Results(persist=run) = out1;

   proc sql;

      /* 実行グループ1 */
      select count(*) as c1 from sashelp.class;

      /* 実行グループ2 */
      select count(*) as c1 from sashelp.bmt;

   quit;

ods output close;

データセットOUT1

PERSIST=オプションはSQLプロシジャと相性が良いので、今度その辺りも紹介したいと思います。



2021年10月26日火曜日

ODS OUTPUTで出力結果を結合する①PERSIST=PROC



ODS OUTPUTの「PERSIST=オプション」について、2回に分けて詳しく紹介していきます。

まず1回目は「PERSIST=PROC」について。 



以下の失敗例をご覧ください。

ODS OUTPUTの中にプロシジャを複数記述したけど、最初のプロシジャの結果しかデータセットに出力されませんでした。


😟失敗例

ods output Moments = out1;

    /* データセットに出力される */
    proc univariate data=sashelp.class;
        var height;
        where age=13;
    run;

    /* データセットに出力されない */
    proc univariate data=sashelp.class;
        var weight;
        where age=14;
    run;

ods output close;



そこで「PERSIST=PROC」というオプションの出番です。

以下のように、プロシジャ間で出力オブジェクトの名前が一緒であれば(今回の場合は「Moments」)、結合してデータセットに出力することが出来ます。


😄成功例(PERSIST=PROC

ods output Moments (persist=proc) = out1;

    proc univariate data=sashelp.class;
        var height;
        where age=13;
    run;

    proc univariate data=sashelp.class;
        var weight;
        where age=14;
    run;

ods output close;



ただし、以下のように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


2018年2月15日木曜日

ODS OUTPUT の落とし穴 (2)



以下記事の続き。
ODS OUTPUT の落とし穴 (1)



以下のプログラム、WARNINGが出ちゃいますが、どこがおかしいか分かりますか?

proc glm data=sashelp.class;
   class sex;
   model height = sex;
run;

ods output Summary=OUT1;
proc means data=sashelp.class;
   var height;
run;
ods output close;


ログ
WARNING: 出力'Summary'は作成されていません。出力オブジェクト名、ラベル、パスが正しく記述されているかを確認してください。また、要求した出力オブジェクトを作成するために、
         適切なプロシジャオプションが使われているかも確認してください。たとえば、NOPRINTオプションが使われていないことを確認してください。
WARNING: プロシジャステートメントの終わりを検出したので、現在のODS SELECT/EXCLUDE/OUTPUTステートメントをクリアしました。
         対話型プロシジャ(終了するにはquit;をタイプします)が終了していないかもしれません。



この落とし穴にはまってる人たまに見かけます。


これは対話型プロシジャを使う場合に起こる落とし穴です。
(対話型プロシジャについては、「RUNとQUITの違い」をご覧ください。)


今回の例で使用しているGLMプロシジャは対話型プロシジャです。
「QUIT」と書くか、別のDATAステップ・PROCステップが現れるまで、GLMプロシジャは起動したままになります。
なので、以下の青文字部分がGLMプロシジャに対する処理だと判断されてしまいます。


proc glm data=sashelp.class;
   class sex;
   model height = sex;
run;

ods output Summary=OUT1;
proc means data=sashelp.class;
   var height;
run;
ods output close;



解決方法はご存じの通り、「quit;」を入れるだけです。

proc glm data=sashelp.class;
   class sex;
   model height = sex;
run;
quit;

ods output Summary=OUT1;
proc means data=sashelp.class;
   var height;
run;
ods output close;




そもそもSASのマニュアルにも対話型プロシジャにQUITを入れていない例があるので、
この問題に気づかなくても無理はないっちゃないんですが。。


ちなみに、今回紹介した落とし穴は「ods output」だけでなく、「ods select」「ods exclude」とかでも同様に起こる問題です。

2017年9月13日水曜日

ODS OUTPUT の落とし穴 (1)



まず以下うまくいく例をご覧ください。

うまくいく例
ods output OneWayFreqs=OUT1;

   proc freq data=SASHELP.CLASS;
      tables SEX AGE;
   run;

ods output close;



次にうまくいかない例。

うまくいかない例
ods output OneWayFreqs=OUT1 (where=(AGE=13));

   proc freq data=SASHELP.CLASS;
      tables SEX AGE;
   run;

ods output close;

ログ
ERROR: 変数AGEはファイルWORK.OUT1にありません。


WHERE=オプションを追加したらERRORが出ちゃいました。
構文的には問題ないのに何故でしょう?



原因


以下のように ODS TRACE で出力オブジェクト名をログに表示してみると、、

ods trace on;

proc freq data=SASHELP.CLASS;
   tables SEX AGE;
run;

ods trace off;

ログ
出力の追加 :
-------------
名前 :         OneWayFreqs
ラベル :       一元表
テンプレート : Base.Freq.OneWayFreqs
パス :         Freq.Table1.OneWayFreqs
-------------

出力の追加 :
-------------
名前 :         OneWayFreqs
ラベル :       一元表
テンプレート : Base.Freq.OneWayFreqs
パス :         Freq.Table2.OneWayFreqs

出力オブジェクト「OneWayFreqs」が2つ出てますね。

PROC FREQ の TABLESステートメントで「tables SEX AGE」と書いているため、
SEXとAGEそれぞれに出力オブジェクト「OneWayFreqs」が出来ている事を表しています。



そしてここから予想になるのであしからずという感じですが、

ods output OneWayFreqs=OUT1 (where=(AGE=13));

   proc freq data=SASHELP.CLASS;
      tables SEX AGE;
   run;

ods output close;

と書いたときの挙動として、

① 変数 SEX の集計結果をデータセット OUT1 に出力
② 変数 AGE の集計結果をデータセット OUT1 に出力

となりますが、どうやらWHERE=オプションは上記①②のそれぞれで実行されている気がします(色々実験した結果の予想)

つまり①の時点では出力データセットに 変数 AGE の集計結果が存在していないので、「where=(AGE=13)」は ”存在していない変数に対するWHERE条件” となりERRORが出てしまったというわけですかね。

PROC FREQに限らず、同じ名前の出力オブジェクトが複数出力されるようなケースでは同様に注意が必要です。



内部挙動は予想になってしまうので、
この辺ちゃんと説明しているサイトなどご存知の方いたら教えて下さい。


ちなみに、以下記事でその他の落とし穴も紹介しています。
「ODS OUTPUT」の落とし穴 (2)

2015年7月18日土曜日

集計結果をデータセットに出力する「ODS TRACE」と「ODS OUTPUT」




「まとめといて!」 という依頼があったので、簡単にまとめたいと思います。

サンプルデータ
data DT1;
input A;
cards;
10
20
30
;


サンプルに対してMEANSプロシジャで要約統計量を求めて、その結果をデータセットに吐き出したいとします。


①まずは「ODS TRACE」で出力オブジェクトの名前を調べる

ods trace on;
proc means data=DT1;
   var A;
run;
ods trace off;

ログ

集計プロシジャを「ods trace on;」と「ods trace off;」で囲って実行すると、ログに上のような表示が現れます。
ログに出てきた「名前」のところにあるキーワードを覚えておきましょう。上の例では「Summary」というキーワードになってますね。


②「ODS OUTPUT」で集計結果をデータセットに出力する

ods output Summary=OUT1;
proc means data=DT1;
   var A;
run;
ods output close;

結果データセットOUT1




今度は集計プロシジャを「ods output 名前 出力データセット名;」と「ods output close;」で囲い直します。
名前のところに先ほどのキーワード「Summary」を当てはめます。

実行すると上の通り、見事集計結果のデータセットが出来上がりです。


ここで注意!
  • プロシジャ側でNOPRINTオプション等で結果ビューアへの出力を抑制すると、ODS OUTPUTが使えない(データセットに出力されない)ので注意
  • 同じプロシジャでもSASのバージョンによって出力オブジェクトの名前や出力データの構造が変わってるかもしれません。別のバージョンで実行する場合、その点よく確認した方がいいです。
  • また日本語版や英語版などの違いによって、出力データセット中の格納値もその国の言語になっている場合があるので注意が必要です。

あと、たまに見かけるミスを以下記事で紹介しているので、あわせてご参照ください。