2018年12月28日金曜日

SASで雪だるまを描く




2018年も本ブログを見て頂いた方、コメントしてくださった方、どうもありがとうございました!
来年もどうぞよろしくお願いいたします!!

SASでグリーティングカードを作ってみました。


以下が今回作ったプログラムです。SAS9.4以降で動作します。
*** 描画用データ作成 ;
data SNOW;
  * ゆきだるま描画用データ作成 ;
  retain x1  0  y1  0;
  retain x2  0  y2 -6.5;
  retain x3 -1  y3 1;
  retain x4  1  y4 1;
  retain x5  0  y5 0;
  retain x6  0  y6 0.5;
  retain x7  0  y7 5.5;
  retain x8  0  y8 7;

  * ゆき描画用データ作成 ;
  call streaminit(777);
  do scene=1 to 5;
     do snow_x = -10 to 10 by 1;
        snow_y = rand('uniform')*30-15;
        output;
     end;
  end;
run;

*** 描画 ;
title ;
options  ANIMATION=START  ANIMDURATION=1   PRINTERPATH=GIF ;

ods noresults;
ods printer file="出力するパスを指定\test.gif";
ods graphics on / height=4in width=4in;

%macro ANIMATION;
  %do i=1 %to 5;
  proc sgplot data=SNOW noautolegend;
     where scene=&i;
     styleattrs wallcolor=skyblue;
     symbolchar name=SNOW char='2744'x;
     scatter x=snow_x y=snow_y /  markerattrs=(symbol=SNOW  size=1.5cm color=white);
     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.3cm color=orange);
     scatter x=x7 y=y7  /  markerattrs=(symbol=TriangleFilled  size=1.2cm color=red);
     scatter x=x8 y=y8  /  markerattrs=(symbol=CircleFilled  size=0.6cm color=white);
     inset "Happy Holidays"  /  position=top textattrs=(color=green size=35cm family='Arial');
     xaxis min=-3 max=3  display=none;
     yaxis min=-5 max=1  display=none;
  run;
  %end;
%mend;

%ANIMATION;

ods graphics on / reset=all;
ods printer close ;
ods results;

options  ANIMATION=STOP ;


2018年12月14日金曜日

現地時間などから日本時間を調べたい




海外のなんかのイベントで「ライブ配信がxx時yy分から行われます」って情報があったとき「日本時間だと何時だろう」って知りたい事ありますよね。




例1

直近だと、自宅で使ってる SAS OnDemand for Academics のメンテナンス終了予定が

    Dec 13, 2018 17:00 UTC

みたいに書かれてて、日本時間でいつ終わるのか知りたいってことがあります。ネットで調べれば速攻で分かりますが、SAS使いとしてはSASで調べてみましょう。


data _null_;
   utc_dt = '2018-12-13T17:00:00'dt;
   jpn_dt = tzoneu2s( utc_dt, 'Asia/Tokyo' );
   put jpn_dt= datetime.;
run;

Log
jpn_dt=14DEC18:02:00:00


なるほど、日本時間だと12月14日の2時にメンテが終わるってことですね。

  • TZONEU2S関数」は、UTC(世界協定時)を指定したタイムゾーンIDの日時に変換します。
  • ちなみに「SAS timezone id」とかでググれば都市ごとのタイムゾーンIDが載ってるSASのリファレンスが見つかるはずです。



例2

テキサス州ダラスで現地時間2018年12月12日23時10分になんかのライブ配信が行われますってなった時に、日本時間だといつか調べたいとします。

data _null_;
   dal_dt = '2018-12-12T23:10:00'dt;
   utc_dt = tzones2u( dal_dt, 'America/Chicago' );
   jpn_dt = tzoneu2s( utc_dt, 'Asia/Tokyo' );
   put jpn_dt= datetime.;
run;

Log
jpn_dt=13DEC18:14:10:00


日本時間で13日の14時10分からライブ配信が開始だってのが分かります。

  • TZONES2U関数」は、指定した日時とそのタイムゾーンIDからUTC(世界協定時)に変換します。
  • ちなみにテキサス州ダラスのタイムゾーンIDがないっぽいので、同じタイムゾーンを持つ 'America/Chicago' を設定しています。(この同じタイムゾーン調べるのも少し面倒)


関数の区別方法

TZONEU2SとTZONES2Uって似てますが、

・TZONEU2S・・・「UTC datetime to SAS datetime」
・TZONES2U・・・「SAS datetime to UTC datetime」

と考えると覚えやすいと思います。




2018年12月6日木曜日

【PROC MEANS】OUTPUTステートメントでの出力変数の指定方法



PROC MEANS(または PROC SUMMARY)の「OUTPUTステートメント」で統計量をデータセットに出力することが出来ます。


今回は統計量を出力する際の変数の指定方法を紹介したいと思います。




例① 分析変数が1つの場合


proc means data=SASHELP.CLASS;
   var HEIGHT;
   output out=OUT1  min=MIN_HEI  max=MAX_HEI;
run;

または

proc means data=SASHELP.CLASS;
   var HEIGHT;
   output out=OUT2  min( HEIGHT )=MIN_HEI  max( HEIGHT )=MAX_HEI;
run;


・HEIGHTの最小値をMIN_HEI、最大値をMAX_HEIに格納してます。

・基本的に「統計量( 分析変数 ) = 出力変数」という構文ですが、「統計量 = 出力変数」というように分析変数を省略した場合は、VARステートメントに指定した分析変数が使用されます。






例② 分析変数が2つ以上の場合


proc means data=SASHELP.CLASS;
   var HEIGHT WEIGHT;
   output out=OUT3  min = MIN_HEI  MIN_WEI
                              max = MAX_HEI  MAX_WEI;
run;

または

proc means data=SASHELP.CLASS;
   var HEIGHT WEIGHT;
   output out=OUT4  min( HEIGHT WEIGHT ) = MIN_HEI MIN_WEI
                              max( HEIGHT WEIGHT ) = MAX_HEI MAX_WEI;
run;

またはまたは

proc means data=SASHELP.CLASS;
   var HEIGHT WEIGHT;
   output out=OUT3  min( HEIGHT )=MIN_HEI  min( WEIGHT )=MIN_WEI
                              max( HEIGHT )=MAX_HEI  max( WEIGHT )=MAX_WEI;
run;


・HEIGHTとWEIGHTの最小値をそれぞれMIN_HEI、MIN_WEIに格納
・HEIGHTとWEIGHTの最大値をそれぞれMAX_HEI、MAX_WEIに格納




AUTONAMEオプションの利用

proc means data=SASHELP.CLASS;
   var HEIGHT WEIGHT;
   output out=OUT5  min=  max= / autoname;
run;


AUTONAMEオプションをつけると、SASが自動で出力変数名を決めてくれます。
「分析変数名_統計量」という感じの変数名がつけられます。



以下のような書き方も。

proc means data=SASHELP.CLASS;
   var HEIGHT WEIGHT;
   output out=OUT6  min( HEIGHT )= / autoname;
run;




ちなみに指定出来る統計量は、、
  統計量   内容
    N
  NMISS  欠損値の数 
  SUM  合計
  MEAN  平均値
  MIN  最小値
  MEDIAN   中央値
  MAX   最大値   
  RANGE  範囲
  MODE  最頻値   
  STDDEV  標準偏差   
  STDERR  標準誤差   
  Q1  下側四分位点 
  Q3  上側四分位点  
  QRANGE  四分位範囲  
  KURT  尖度
  SKEW  歪度
  …etc

など、その他豊富に取り揃えています。詳細はリファレンスを参照ください。




2018年12月5日水曜日

【PROC UNIVARIATE】OUTPUTステートメントでの出力変数の指定方法




PROC UNIVARIATE の「OUTPUTステートメント」で統計量をデータセットに出力することが出来ますが、「統計量を出力する際の変数の指定方法がよく分からない!」って方がいたので、まとめておきます。




例① 分析変数が1つの場合

proc univariate data=SASHELP.CLASS noprint;
   var HEIGHT;
   output out=OUT1  min=MIN_HEI  max=MAX_HEI;
run;

・分析変数HEIGHTの最小値をMIN_HEI、最大値をMAX_HEIに格納してます。




例② 分析変数が2つ以上の場合

proc univariate data=SASHELP.CLASS noprint;
   var HEIGHT WEIGHT;
   output out=OUT2  min=MIN_HEI  MIN_WEI
                               max=MAX_HEI  MAX_WEI;
run;

または

proc univariate data=SASHELP.CLASS noprint;
   var HEIGHT WEIGHT;
   output out=OUT3  min=MIN_HEI  min=MIN_WEI
                               max=MAX_HEI  max=MAX_WEI;
run;



・分析変数HEIGHTとWEIGHTの最小値をそれぞれMIN_HEI、MIN_WEIに格納
・分析変数HEIGHTとWEIGHTの最大値をそれぞれMAX_HEI、MAX_WEIに格納



ちなみに指定出来る統計量は、、
  統計量   内容
    N
  NMISS  欠損値の数 
  SUM  合計
  MEAN  平均値
  MIN  最小値
  MEDIAN   中央値
  MAX   最大値   
  RANGE  範囲
  MODE  最頻値   
  STDDEV  標準偏差   
  STDERR  標準誤差   
  Q1  下側四分位点 
  Q3  上側四分位点  
  QRANGE  四分位範囲  
  KURT  尖度
  SKEW  歪度
  …etc

その他あわせて40~50個程度の統計量を豊富に取り揃えています。詳細はリファレンスを参照ください。


2018年11月28日水曜日

コンパイルしたマクロを削除する





マクロを削除する方法1

%SYSMACDELETE  マクロ名1 マクロ名2 ・・・ ;

  • 「WORK.SASMACR」の中にあるマクロを削除することが出来ます。
  • 削除したいマクロ名を列挙します。


%sysmacdelete test1 test2 ;






マクロを削除する方法2

PROC CATALOG  CAT=ライブラリ名.カタログ名  ET=MACRO;
     DELETE  マクロ名1  マクロ名2 ・・・ ;
RUN;

  • 対象のライブラリとカタログ名を指定することが出来ます。
  • 削除したいマクロ名を列挙します。


proc catalog cat=mymac.sasmacr et=macro;
   delete test1 test2 test3;
run;






マクロを削除する方法3  (注意事項あり)

PROC CATALOG  CAT=ライブラリ名.カタログ名  KILL FORCE;
RUN;

  • カタログ内の「全エントリー」を削除します。
  • 「全エントリー」とはマクロだけでなくフォーマットなどのあらゆるエントリーを含みます。
  • 「KILL」オプションは PROC CATALOG 内の他のオプションやステートメントより先に動作します。たとえばDELETEステートメントで削除するマクロを指定していても意味はなく、カタログ内の全エントリーを削除してしまいます。


2018年11月22日木曜日

FIRSTOBS=, OBS=, WHERE を組み合わせたときの挙動




データセットオプション「FIRSTOBS=」と「OBS=」の紹介と、WHEREステートメントを組み合わせて使用した時の挙動について、まとめておきます。



SAS認定試験「Clinical Trials Programming Using SAS 9」のサンプル問題に、こらら3つの機能を組み合わせた時の挙動について出題があったので、試験を受ける方の参考にもなればと思います。



Sample data
data a;
   do i=1 to 10;
     output;
   end;
run;





FIRSTOBS=オプション

・処理(読込み)を開始するオブザベーション番号を指定できます。

proc print data=a (firstobs=3);
run;




OBS=オプション

・何オブザベーション目まで処理するか(読み込むか)を指定できます。

proc print data=a (obs=3);
run;




FIRSTOBS=, OBS=, WHERE の組み合わせ

ここから本題。以下のプログラムで結果が予測できればバッチリです。

proc print data=a (firstobs=2 obs=3);
  where i>5;
run;


解説
・まずWHEREでオブザベーションを抽出。
・抽出したオブザベーションに対して、FIRSTOBS=とOBS=を適用。
・FISTOBS=とOBS=で共通する範囲にあるオブザベーションが処理対象となる。


以下にイメージを載せておきます。


ちなみに

SETステートメントとかでもこれらのオプションが使えます。

data b;
  set a (obs=3);
run;



注意


以下の記事で、FISTOBS=オプション自体の落とし穴も紹介しています。
FIRSTOBSオプションの落とし穴




2018年11月20日火曜日

FORMATの落とし穴【デフォルト長】




以下のプログラムと結果をご覧ください。結果は想定通りでしょうか?

proc format;
   value $FMT
       "a"    = "x"
       "abc" = "y"
   ;
run;

data DT1;
   format VAR1 $FMT.;
   VAR1 = "abc";
run;

  VAR1  
  x 

変数VAR1は "abc" なのでフォーマット$FMTを貼りつけると "y" と表示されるはずが、 "x" が表示されてしまいました。



原因

これは「デフォルト長 (Default Length)」というものが関係してます。
例えば、今回の例で

proc format;
   value $FMT
       "a"    = "x"
       "abc" = "y"
   ;
run;


右側に定義した値 ("x" と "y") の文字の長さは1バイトです。
この長さが、そのフォーマットの「デフォルト長」というものになります。
(定義した値が複数ある場合は、右側の文字の長さが最も大きいものがそのフォーマットの「デフォルト長」になる)



そしてこのフォーマット$FMTは、以降のFORMATステートメントでデフォルト長となった1バイトしか視界に入らなくなります。
どういう事かというと、、

data DT1;
   format VAR1 $FMT.;
   VAR1 = "abc";
run;

変数VAR1 の値 "abc" から先頭の1バイトの値 "a" だけを見て対応するフォーマット値を表示しようとします。



ちょうど PROC FORMAT で "a" = "x" と定義してたので、この値が表示されちゃったというわけですね。

proc format;
   value $FMT
       "a"    = "x"
       "abc" = "y"
   ;
run;


解決策

変数VAR1 の値 "abc" の文字の長さは3バイトなので、3バイト全部読み込んで表示する必要があります。
解決策としては以下の2つの方法があります。


解決策①
proc format;
   value $FMT (default=10)
       "a"    = "x"
       "abc" = "y"
   ;
run;

data DT1;
   format VAR1 $FMT.;
   VAR1 = "abc";
run;

  VAR1  
  y 

「DEFAULT=オプション」によってこのフォーマットのデフォルト長を 10 に広げています。


解決策②
proc format;
   value $FMT
       "a"    = "x"
       "abc" = "y"
   ;
run;

data DT1;
   format VAR1 $FMT10.;
   VAR1 = "abc";
run;

  VAR1  
  y 

FORMATステートメントで、フォーマット名の後ろに幅を指定することも出来ます。




See also

インフォーマットでも同様の落とし穴があります。
INFORMATの落とし穴【デフォルト長】


ポイント
・文字値から数値に変換するインフォーマットの場合、「左側」に定義した文字の長さがデフォルト長になる。
・フォーマットの場合、「右側」に定義した文字の長さがデフォルト長になる。


2018年11月1日木曜日

SQLビューは同じ変数名が複数あってもエラーにならない




SQLビューの落とし穴です。以下をご覧ください。

proc sql;
   create view MYVIEW as
   select  NAME,
              111 as VAR1,
              222 as VAR1
   from  SASHELP.CLASS;
quit;


proc print data=MYVIEW noobs;
run;


なにこれ、VAR1が2つ存在しちゃってる、エラーにもなってない。。
実はCREATE VIEWでは、同じ名前の変数がいくつも存在する状態を作り出すことが出来てしまうんです。



では、先ほどのSQLビューに対して、VAR1を選択してPROC PRINTすると、、

proc print data=MYVIEW noobs;
   var VAR1;
run;


1個目のVAR1がプリントされました。2個目はどうした。



私自身、この挙動を知らずにミスったことがあります。
その時の状況として、変数が数十個あるデータセットに対して、

select  *, x+y as var1

みたいなselect文を書いたSQLビューを作りました。ですが元々データセットにVAR1っていう変数が存在していて、それを知らずにSQLビューの中でもVAR1っていう変数を作ってしまっていました。

このSQLビュー内で作ったVAR1に対して、

proc means data=○○;
   var var1;
run;

みたいな事をやろうとしたところ、元々データセットに存在しているVAR1に対して集計が行なわれてしまい、結果がおかしい!?ってなりました。



See also...
SQLビューの作成と落とし穴①

2018年10月26日金曜日

マクロステートメント「%IF」の落とし穴





落とし穴の例


以下、%IFで「1」と「1.0」が等しいかどうかを評価しています。(結果は等しくないって出ちゃいます)

%macro test;
        %if 1 = 1.0  %then %put Equal; %else %put Unequal;
%mend;
%test

ログ
Unequal





以下、%IFで「3 / 2」と「1」が等しいかどうかを評価しています。(結果は等しいって出ちゃいます)

%macro test;
        %if (3 / 2) = 1  %then %put Equal; %else %put Unequal;
%mend;
%test

ログ
Equal 




%IFの挙動

・評価する値がすべて整数の場合、「数値」として評価が行われる。
例)「2 < 3」「(1 + 2) = 3」


・評価する値がすべて整数でも、割り算等の式が含まれていて計算結果が小数になる場合、小数部分を破棄してから評価が行われる。
例)「(3 / 2) = 1」… 3 / 2 → 1.5 → 1


・評価する値に小数や文字を含む場合、「文字値」として評価が行われる。
例)「1 < 1.0」「a < b」




小数を扱う場合、以前紹介した「%sysevalf関数」を使います。
http://sas-boubi.blogspot.com/2016/08/11-evalsysevalf.html

%macro test;
        %if %sysevalf( 1 = 1.0 ) %then %put Equal; %else %put Unequal;
%mend;
%test

ログ
Equal



%macro test;
        %if %sysevalf( (3 / 2) = 1 )  %then %put Equal; %else %put Unequal;
%mend;
%test

ログ
Unequal

2018年10月15日月曜日

マクロ内でCARDSが使えないので工夫してみる。




そろそろコタツみかんが出来る時期ですね。



さて、タイトルの通りマクロ内ではCARDSが使えません。
以下の例を見てみましょう。

%macro test;

data out1;
   infile mytemp lrecl=30000;
cards;
1 2 aaa
3 4 bbb
5 6 ccc
;

%mend;
%test

ログ
 ERROR: マクロTESTがCARDS(データ行)を生成しました。不当な結果の原因になるかもしれません。DATAステップとマクロの実行を中止します。
 ERROR: マクロTESTの実行を中止します。




原因はSAS社のFAQやデータステップ100万回でも言及されてますね。

https://www.sas.com/offices/asiapacific/japan/service/technical/faq/list/body/ba243.html
http://sas-tumesas.blogspot.com/2015/01/cards.html

マクロの仕様上、マクロソースは半角スペース(空白)が詰められてリフォーマットされる、
それによって改行位置がわからなくなってしまう、、
なので仕様上の制限として使用できないようにしている、、だそうです。


「それでも、マクロの中でCARDSのように読み込むテキストを指定したいんだ!」という質問を頂きます。
色々方法はありますが、今回2通りの解法を載せておきます。



SAS9.3以前の場合
%macro test;

filename mytemp temp;
data _null_;
   file mytemp;
   put "1 2 aaa";
   put "3 4 bbb";
   put "5 6 ccc";
run;

data out1;
   infile mytemp lrecl=3000;
   input x y z:$10.;
run;

%mend;
%test

プログラムの流れ
・filenameで一時的なファイル参照を作って、
・file/putでこのファイル参照に値を書き出して、
・infile/inputで書きだした値を読み込み



SAS9.4以降の場合
%macro test;

filename mytemp temp;
proc stream resetdelim="_reset" outfile=mytemp;
begin    _reset;
1 2 aaa  _reset newline;
3 4 bbb  _reset newline;
5 6 ccc  _reset;
;;;;

data out1;
   infile mytemp lrecl=3000;
   input x y z:$10.;
run;

%mend;
%test

プログラムの流れ
・filenameで一時的なファイル参照を作って、
・proc streamでこのファイル参照に値を書き出して、
・infile/inputで書きだした値を読み込み

リセットデリミタの設定がちょっとめんどくさいです。



2018年10月9日火曜日

「=:」を使った前方一致による比較と落とし穴





文字列の比較で使える「=:」を紹介します。
落とし穴もあるので、それも含めて紹介していきます。



簡単な例


Sample data
data TEST;
input X:$3.;
cards;
ab
abc
cab
;




data OUT1;
    set TEST;
    if X =: "ab" then FLG1=1;
run;



変数Xが "ab" で始まる値かどうか、という比較を行っています。


詳しく説明すると、「=:」の両辺に指定した変数または文字列のうち、LENGTHが大きい方は、LENGTHが小さい方の長さに切り捨てて比較を行ないます。


ここでいうLENGTHとは以下を指します。
 文字列の場合 

文字列の長さ」
 変数の場合

 固定長の文字変数の場合
「文字列と末尾の空白を含む長さ(つまり変数に定義した長さ)」



上の例では、「X =: "ab"」で、
・左辺に指定した変数Xに定義されたlengthは「3」
・右辺に指定した "ab" は2バイトなのでlengthは「2」

この2つのlengthで小さいのは「2」なので、左辺の変数値を2バイトに切り捨てると以下の通り。

ab → ab
abc → ab
cab → ca

この切り捨てた値と右辺の "ab" がイコールか否か、という判定を行います。






注意が必要な例



data OUT2;
    set TEST;
    if X =: "abcd" then FLG1=1;
run;



「X =: "abcd"」で
・左辺の変数Xに定義されたlengthは「3」、
・右辺の "abcd" は4バイトなのでlengthは「4」、

lengthが小さいのは「3」なので、右辺の文字 "abcd" を3バイトで切り捨てると "abc" となります。


この "abc" が変数Xとイコールか否か、という判定を行います。
この切り捨てルールを知らないと、一見「変数Xが "abcd" で始まる値かどうか」と思ってしまいがちですが、間違いです!





今回の例のデータでもし「変数Xが "abcd" で始まる値かどうか」を判定したいなら、以下のように他の方法を使ったほうが安全です。

data OUT3;
    set TEST;
    if index( X, "abcd" )=1 then FLG1=1;    * 方法1 ;
    if find( X, "abcd" )=1 then FLG2=1;       * 方法2 ;
run;




2018年10月1日月曜日

【効率を意識したプログラム】WHEREとサブセット化IFの使い分け




「WHERE」と「サブセット化IF」の違いを知って使い分けようっていう話です。



「WHERE」と「サブセット化IF」の比較


Sample data
data test;
   length col1-col200 $200.;
   array ar(*) col:;
   do i=1 to dim(ar);
       ar(i) = "abcdefg";
   end;
   do i=1 to 50000;
      output;
   end;
run;



サンプルデータから「WHEREステートメント」を使ってオブザベーションを抽出したときの処理時間とCPU時間を見てみます。

data out1;
   set test;
   where i<100;
run;

ログ
       処理時間           0.40 秒
       CPU時間            0.39 秒



同様のオブザベーションの抽出を「サブセット化IF」を使って処理時間とCPU時間を見てみます。

data out1;
   set test;
   if i<100;
run;

ログ
       処理時間           0.53 秒
       CPU時間            0.54 秒


WHEREステートメントの方が処理時間が短いですね。何故このような違いが起きたのでしょうか。





「WHERE」と「サブセット化IF」の違い


・実行されるタイミングが違う
 WHERE

 データを読み込む時に実行される
 サブセット化IF 

 データを読み込んだ後のデータステップ内で実行される

WHEREの方が実行されるタイミングが早いんで、処理時間が短くなるというわけですね。



・実行できる環境の違い
 WHERE

 データステップおよび(ほとんどの)PROCステップで実行できる
 サブセット化IF 

 データステップでのみ実行できる





「サブセット化IF」の利点


サブセット化IFが効果的な例として、以下のサブセット化IFについての記事の例②で紹介しているようなケースです。
【サブセット化IF】条件に一致するオブザベーションのみ処理を継続する。

data out2;
  set dt1;
  bmi = weight / ((height/100)**2);
  if bmi>=25;
run;


身長と体重からBMIを計算したあと、「if bmi>=25」でBMIが25以上のオブザベーションを残すようにしていますが、
このBMIはデータステップ内で計算しているので、「where bmi>=25」と書くことは出来ませんよね(データを読み込む時には、まだBMIが計算されていないので)




2018年9月26日水曜日

2つの文字を大文字・小文字区別せずに比較する





クマのプーさんの実写映画のやつ観に行ってきました。
プーさんが異常に可愛かったです。あとプーさんってぬいぐるみ設定だったんですね、知らなかった。。




プーさんの話は置いといて、
タイトルの通り、2つの文字を大文字・小文字区別せずに比較する方法を紹介したいと思います。


サンプルデータ
data test;
input a:$10. b:$10.;
cards;
pooh POOH
honey hunny
;



方法①
data out1;
  set test;
  if upcase(a) = upcase(b) then same_flg=1;
run;



半角のアルファベットを大文字にするupcase関数を使っています。
両変数を大文字にして比較しています。



方法②
data out2;
  set test;
  if compare(a,b,'i') = 0 then same_flg=1;
run;



compare( 対象変数1, 対象変数2, 'i' )」というように、3つめの引数に「'i'」と指定することで大文字・小文字区別せずに比較することができます。
戻り値が「0」なら一致してるということです。

(ただし、compare関数は日本語などのマルチバイトの文字には対応していないので、マルチバイトを含む場合は使用できません!ご注意ください!!)



どっち使うかは好みの問題ですね。
例えるなら、upcaseを使う方法はプーさんで、compare関数を使う方法はピグレットみたいな。





2018年9月18日火曜日

【OPTIONS APPEND / INSERT】システムオプションに設定値を追加する。




「APPENDシステムオプション」または「INSERTシステムオプション」を使うと、特定のシステムオプションの既存の設定値に、更に設定値を追加することが出来ます。

何言ってるの?って感じだと思うので、例をご覧ください。



以下、FMTSEARCHシステムオプションの設定値をログに出力しています。

%put %sysfunc(getoption(fmtsearch));

ログ
(WORK LIBRARY)


WORK、LIBRARYの順に設定されていますね。




「APPENDシステムオプション」を使う

options append=(fmtsearch=myfmt);
%put %sysfunc(getoption(fmtsearch));

ログ
(WORK LIBRARY MYFMT)



MYFMTというライブラリが末尾に追加されました。
APPENDシステムオプションは、システムオプションの末尾に設定値を追加することが出来ます。


構文

 OPTIONS APPEND=( システムオプション名 = 末尾に追加する設定値 );





「INSERTシステムオプション」を使う

options insert=(fmtsearch=myfmt2);
%put %sysfunc(getoption(fmtsearch));

ログ
(MYFMT2 WORK LIBRARY MYFMT)



MYFMT2というライブラリが先頭に追加されました。
INSERTシステムオプションは、システムオプションの先頭に設定値を追加することが出来ます。


構文

 OPTIONS INSERT=( システムオプション名 = 先頭に追加する設定値 );






その他メモ


FMTSEARCH、SASAUTOSなどで設定値の追加をすることができます。
ただし、以下のように何回も実行すると、同じ設定値が追加され続けちゃうんで、気を付けてくださいね。

options append=(fmtsearch=abc);
options append=(fmtsearch=abc);
%put %sysfunc(getoption(fmtsearch));

ログ
(WORK LIBRARY ABC ABC)





2018年9月10日月曜日

PROC SQL / FedSQL / DS2 の「STIMERオプション」でログに各ステップ毎のパフォーマンスを表示する





以下はPROC SQLで何個かステートメントを書いて実行した結果

proc sql;
  create table dt1 as select * from sashelp.class;
  create table dt2 as select * from sashelp.cars;
quit;



ログ
 72         proc sql ;
 73           create table dt1 as select * from sashelp.class;
 NOTE: テーブルWORK.DT1(行数19、列数5)が作成されました。

 74           create table dt2 as select * from sashelp.cars;
 NOTE: テーブルWORK.DT2(行数428、列数15)が作成されました。

 75         quit;
 NOTE: PROCEDURE SQL処理(合計処理時間):
       処理時間           0.00 秒
       CPU時間            0.01 秒



続いてSTIMERオプションをつけた場合

proc sql stimer;
  create table dt1 as select * from sashelp.class;
  create table dt2 as select * from sashelp.cars;
quit;


ログ
 74         proc sql stimer;
 NOTE: SQL Statement処理(合計処理時間):
       処理時間           0.00 秒
       CPU時間            0.00 秒
     
 75           create table dt1 as select * from sashelp.class;
 NOTE: テーブルWORK.DT1(行数19、列数5)が作成されました。

 NOTE: SQL Statement処理(合計処理時間):
       処理時間           0.00 秒
       CPU時間            0.00 秒
     
 76           create table dt2 as select * from sashelp.cars;
 NOTE: テーブルWORK.DT2(行数428、列数15)が作成されました。

 NOTE: SQL Statement処理(合計処理時間):
       処理時間           0.00 秒
       CPU時間            0.00 秒
     
 77         quit;
 NOTE: PROCEDURE SQL処理(合計処理時間):
       処理時間           0.00 秒
       CPU時間            0.00 秒


ログにステートメント毎のパフォーマンスが表示されるようになります。
「PROC DS2」や「PROC FedSQL」でも同様にSTIMERオプションを設定できます。
私自身はDS2使う時によくこのオプション指定することはあります。


※「STIMERオプション」を利用するには、システムオプションで「OPTIONS STIMER」か「OPTIONS FULLSTIMER」も設定されている必要があります。


2018年9月4日火曜日

PROC DS2でタイムゾーン関係の処理をする「TZパッケージ」



DS2プロシジャの「TZパッケージ」でタイムゾーン関係の処理ができます
アメリカとかだとタイムゾーンが地域によって違うから、パッケージ化するほどの需要あるんですかね。



以下は簡単な例です。

 proc ds2;
 data _null_;
    method init();
       dcl package tz t();
       dcl double utc tokyo having format nldatm20.;
       dcl double tokyo_offset having format time8.;

       /* UTC timeを取得 */
       utc = t.getutctime();
 
       /* Local timeを取得 */
       tokyo = t.getlocaltime('Asia/Tokyo');

       /* Offsetを取得 */
       tokyo_offset = t.getoffset('Asia/Tokyo');

       put utc=;
       put tokyo=;
       put tokyo_offset=;

    end;
 enddata;
 run;
 quit;

ログ
 utc=2018/09/04 11:56:47
 tokyo=2018/09/04 20:56:47
 tokyo_offset= 9:00:00


メソッドの中で「’Asia/Tokyo’」というように地域を指定していますが、タイムゾーンIDといって、「SAS timezone id」とかググれば地域ごとのタイムゾーンIDが載ってるSASのリファレンスが出てくるはずです。

あとは構文自体も大したことないんで、詳細はリファレンスを見てみてください。




とりあえず私は業務で使う事ないんで、暇つぶし用のプログラムを書いて遊んでます。

 proc ds2;
 data _null_;
    method init();
       dcl package tz t();
       dcl double tokyo newyork honolulu vancouver having format nldatm20.;

       tokyo         = t.getlocaltime('Asia/Tokyo');
       newyork    = t.getlocaltime('America/New_York');
       honolulu    = t.getlocaltime('Pacific/Honolulu');
       vancouver = t.getlocaltime('America/Vancouver');

       put tokyo=;
       put newyork=;
       put honolulu=;
       put vancouver=;

    end;

 enddata;
 run;
 quit;

ログ
 tokyo=2018/09/04 20:51:00 
 newyork=2018/09/04 07:51:00
 honolulu=2018/09/04 01:51:00
 vancouver=2018/09/04 04:51:00


適当な地域の現在時刻を調べて「あーニューヨーカーは今頃寝てるのか、いやむしろ寝てないのかな」とか妄想するために使うものだと思っています。
一応、夏時間も考慮されているっぽい(夏時間が存在する地域でちゃんと夏時間の日時になってたんで)




2018年9月2日日曜日

【小ネタ】抽出条件のないWHEREステートメント





以下のように抽出条件のない空のWHEREステートメントを書いてもエラーになりません。

data a;
   set sashelp.class;
   where;
run;

抽出条件が掛かれていないので、すべてのオブザベーションが読み込まれます。




これが出来るからなんなのか、、っていうと、例えば以下のようなマクロを定義したい場合ですね。

* マクロ定義 ;
 %macro test( wh= );

   data a;
      set sashelp.class;
      where &wh;
   run;

 %mend;


* マクロ実行 ;
 %test( wh=age=13 )  /* マクロ実行:抽出条件を指定する場合 */
 %test( wh= )              /* マクロ実行:抽出条件を指定しない場合 */



抽出条件があればマクロ変数whに条件を指定すればいいし、なければ何も指定しなければいいだけ。
抽出条件がある場合とない場合で%ifとか使って分岐処理とかさせる必要ありません。







ただ、where=データセットオプションでは条件を空にするのはダメのようです。

data a;
   set sashelp.class (where=());
run;

ログ
 ERROR: WHERE式の読み込み中に、構文エラーが発生しました。

 ERROR 22-322: 構文エラーです。次のいずれかを指定してください: 名前, 引用符で囲んだ文字列, 
               数値定数, 日時定数, 欠損値, INPUT, PUT.  

 ERROR 76-322: 構文エラーです。ステートメントを無視しました。

 ERROR 6-185: データセットのオプションリストに')'のかっこがありません。

 ERROR 79-322: )を指定してください。

 NOTE: エラーが発生したため、このステップの処理を中止しました。
 WARNING: データセットWORK.Aは未完成です。このステップは、0オブザベーション、
          0変数で停止しました。
 WARNING: このステップを中止したため、データセットWORK.Aを置き換えていません。


ログが怒りすぎ、なんかかわいいです。



なので私は以下のように書いてSASをごまかしています。

data a;
   set sashelp.class (where=(1));
run;

「1」は真(true)の意味です。
常に条件が真(true)の状態、つまり全オブザベーションが読み込まれます。


「0」ってのもあります。

   set sashelp.class (where=(0));


この「0」は偽(false)の意味です。
常に偽(false)の状態、つまり全オブザベーション読み込まれません。



勿論この書き方はwhereステートメントでも通用します。

data a;
   set sashelp.class;
   where 1;
run;


2018年8月30日木曜日

【効率を意識したプログラム】KEEPやDROPの利用




DROPとKEEPを活用しようっていう話です。



Sample data
data test;
   length col1-col200 $200.;
   array ar(*) col:;
   do i=1 to dim(ar);
       ar(i) = "abcdefg";
   end;
   do i=1 to 50000;
      output;
   end;
run;


200個の文字変数を持つ、50000obsのデータセットを作りました。



このデータセットを以下のようにSETした時の処理時間とCPU時間を見てみます。

data out1;
   set test;
run;

ログ
       処理時間           1.95 秒
       ユーザーCPU時間    0.32 秒
       システムCPU時間    1.64 秒



ここで例えば変数col1~col10だけが必要な変数だったとしたら、、

data out1;
   set test (keep=col1-col10);
run;

ログ
       処理時間           0.85 秒
       ユーザーCPU時間    0.03 秒
       システムCPU時間    0.83 秒


SETする時に読み込む変数をKEEPかDROPで絞ると、処理時間などが少し短くなります。





また、データステップ中に作成した一時変数や、使わなくなった変数は以下のように、、

data out1;
   set test ;
   keep col1-col10;
run;


または

data out1 (keep=col1-col10);
   set test ;
run;

データセットへ出力する時に変数をKEEPかDROPで絞ってサイズを軽量化すれば、その後このデータセットを読み込む時の処理時間なども短くなる事が期待できますよね。




このように効率を意識しながら、KEEP・DROPを指定する場所を使い分ける事がポイントです。

まぁ軽いデータだったり、実行に時間がかからないプログラムだったら、ここまで意識してKEEP・DROPを使わなくてもいいと思います。(多用しまくるとプログラムがごちゃっとした見た目になりがちだし)



【DROP・KEEPステートメント】データセットから特定の変数を削除または残す



2018年8月27日月曜日

【効率を意識したプログラム】FULLSTIMERオプション





プログラムをちょっと工夫して、実行時間を少しでも短縮しようっていうシリーズ記事です。



まずは「FULLSTIMERシステムオプション」について。

このオプションを指定すると、データステップやプロシジャを実行した時の処理時間やメモリなどの詳細なパフォーマンスがログに表示されるようになります。




以下は「SAS OnDemand for Academics」での実行結果です。

 options fullstimer;

 proc means data=sashelp.class;
    var height;
 run;


ログ
 NOTE: データセットSASHELP.CLASSから19オブザベーションを読み込みました。
 NOTE: PROCEDURE MEANS処理(合計処理時間):
       処理時間           0.02 秒
       ユーザーCPU時間    0.02 秒
       システムCPU時間    0.00 秒
       メモリ             8707.03k
       OSメモリ           36040.00k
       タイムスタンプ     2018/08/27 午後12:25:17
       ステップ数                        26  スイッチ数  0
       ページフォルト回数                0
       ページリクレーム回数              1973
       ページスワップ回数                0
       自発的コンテキストスイッチ回数    14
       非自発的コンテキストスイッチ回数  0
       ブロック入力操作回数              0
       ブロック出力操作回数              8


うるさいくらい表示されますね。(ていうかSAS OnDemandだとFULLSTIMERがデフォルト設定になってます)
ただ、Windows環境で実行すると表示されるのは8つくらいなので、そんなに多くはないです。



「NOFULLSTIMER」で設定を元に戻せます。

 options nofullstimer;




プログラムの書き方によってどのくらいパフォーマンスが変わるんだろうって調べる事があるんで、結構使うオプションです。