2017年9月28日木曜日

「%IF条件」で「IN」を使う。(その2)




以下の記事をご覧ください。
「%IF条件」で「IN」を使う。


上の記事では、システムオプション「minoperator」 と 「mindelimiter」を指定すると、
「%IF条件」で「IN」が使えるようになると紹介しました。




もうひとつ別の書き方として、以下のようにも書けます。

%let MVAR1=1;

%macro TEST  /  minoperator mindelimiter=',' ;
      %if  &MVAR1 in (1,2)  %then %put NOTE: マクロ変数 MVAR1には 1 か 2 が設定されています;
%mend;

%TEST;


  • マクロ定義に直接このオプションを指定すると、そのマクロでのみオプションが有効になります
    • システムオプション「minoperator」「mindelimiter」の設定よりも優先される
  • minoperator」 でINが使えるようになります
    • 「IN」の代わりに「#」でもOK( %if &MVAR1 # (1,2) %then ... )
  • mindelimiter」 には値を区切る時の区切り文字(シングルバイト1文字)を指定
    • % & ' " ( ) ;」といったマクロ等で意味を持ちそうな区切り文字は設定できません
    • 区切り文字はシングルクォーテーションで囲む必要がある(ダブルクォーテーションではダメ)


📝注意



2017年9月25日月曜日

【PROC REPORT】RTF出力時の細かい罫線の設定



PROC REPORTでRTF出力する際に、セル毎の細かい罫線(色・太さ・線種)を設定する方法を紹介します。



まず罫線の設定を表すスタイル属性は以下の3つです。



罫線のスタイル属性

  設定内容  指定   指定できる値
 罫線の色 

 BorderxxColor = 
  
 black, white …など 

 罫線の幅
(太さ)

 BorderxxWidth =
 

 1pt, 2pt …など


 罫線の線種  

 BorderxxStyle =

 dashed, dotted, double, hidden, solid …など 


xx部分にはセルのどの位置の罫線を設定するか指定
「Right」「Left」「Top」「Bottom」が入る




例① 特定の列に罫線を引く


工程1
まずは以下の記事で紹介している方法でざっくりとレポート全体の罫線の引き方を変更します。

(PROC TEMPLATEで全体の罫線やフォントをあらかじめ設定しといてもOK)

ods rtf file='出力するRTFファイルのフルパスを指定';

proc report data=sashelp.class nowd
           style(report)=[rules=groups frame=hsides]
           style(header)=[background=white];
   column name sex age ;
   define name / display;
   define sex / display;
   define age / display;
 run;

ods rtf close;


工程2
次に個別に細かい罫線を引いていきます。
たとえば、変数NAME の右に縦罫線を入れたいとします。

以下の記事も参考に罫線を入れてみます
REPORTプロシジャ入門8:書式の設定【STYLE=】

ods rtf file='出力するRTFファイルのフルパスを指定';

proc report data=sashelp.class nowd
           style(report)=[rules=groups frame=hsides]
           style(header)=[background=white];

   column name sex age ;
   define name / display style(header column)={BorderRightColor=black
                                                                BorderRightStyle=double
                                                                BorderRightWidth=1pt};
   define sex / display;
   define age / display;
run;

ods rtf close;




例② 特定の行に罫線を引く


以下の記事を参考に「AGE > 13」の行だけセルの下に罫線を引いてみます。
REPORTプロシジャ入門9:特定セルの書式設定【CALL DEFINE】

(例で使用しているCOMPUTEステートメントは、高頻度でうまく設定できないケースを見かけるので、上記記事と上記記事内にリンクしている記事も要確認)

ods rtf file='出力するRTFファイルのフルパスを指定';

proc report data=sashelp.class nowd
           style(report)=[rules=groups frame=hsides]
           style(header)=[background=white];

   column name sex age ;
   define name / display;
   define sex / display;
   define age / display;
   compute age;
        if age > 13 then
                call define(_row_,"style","style(calldef)={BorderBottomColor=black
                                                                                BorderBottomStyle=solid
                                                                                BorderBottomWidth=1pt}");
   endcomp;
run;

ods rtf close;




例③ 2段以上のヘッダーの罫線

以下をご覧ください。

ods rtf file='出力するRTFファイルのフルパスを指定';

proc report data=sashelp.class nowd
           style(report)=[rules=groups frame=hsides]
           style(header)=[background=white];
   column name ("Height and Weight" height weight);
   define name / display;
   define height / display;
   define weight / display;
run;

ods rtf close;


上のレポートで、ヘッダーが2段になっている「Height and Weight」の下に罫線を引きたいとします。


以下記事を参考に罫線を入れてみます。
ODS出力時に文字の書式設定をする【インラインフォーマット】

ods rtf file='出力するRTFファイルのフルパスを指定';

proc report data=sashelp.class nowd
           style(report)=[rules=groups frame=hsides]
           style(header)=[background=white];

    column name ("(*ESC*)S={BorderBottomColor=black
                                             BorderBottomStyle=solid
                                             BorderBottomWidth=1pt}Height and Weight"  height weight);
   define name / display;
   define height / display;
   define weight / display;
run;

ods rtf close;



2017年9月22日金曜日

指定した1文字を削除する【COMPRESS/KCOMPRESS関数編】



指定した1文字を削除する方法として、今回は「COMPRESS関数」または「KCOMPRESS関数」を使ってみます。

これら関数は空白を削除するためのものと思われがちですが、実はそれだけじゃなく非常に奥が深い関数です。





*** Sample data ;
data DT1;
   length X $20.;
   X="<10"; output;
   X=">10"; output;
   X="10 to 20"; output;
run;

たとえば、上の変数X から「<」または「>」の1文字をそれぞれ削除したいとします。



まず以下の通り、取り扱う変数値・文字値の種類によって、関数を使い分けます。

・COMPRESS ・・・ シングルバイトのみの場合(半角英数など)
・KCOMPRESS ・・・ マルチバイトを含む場合(日本語などの全角文字など)


*** 変数値から「<」と「>」を削除する ;
data DT2;
   set DT1;
   length Y $20.;
   Y = compress( X , "<>" );
run;


使い方
① 削除したい1文字を第2引数に列挙します。
例えば「a」または「b」という1文字を削除したい場合は「"ab"」と指定します。

例)compress( 変数 , "ab" )


② 以下のリンク記事で解説してる通り、日本語などのマルチバイト文字が含まれる場合は結果がバグってしまう事があるので、KCOMPRESS関数を使用しましょう。

関数の落とし穴




指定した文字列を削除するには?

連続する文字列を削除したい場合、
たとえば「a」または「b」を削除する、、のではなく「ab」を削除する。みたいな事をしたい場合は、以下記事の最後の方で紹介してるので、参考までに。

文字列の置換をするTRANWRD vs TRANSTRN関数

2017年9月19日火曜日

【LENGTH/LENGTHN関数】文字値が何バイトか返す




「LENGTH関数」「LENGTHN関数」の構文と、挙動の違いを紹介します。



構文


    LENGTH(  文字値または文字変数   )
    LENGTHN(  文字値または文字変数   )


      文字値の指定例: length( "abc" )
      文字変数の指定例: length( VAR1 )



  • 「文字値」または「文字変数の格納値」から末尾の半角スペースを除いたうえで、値が何バイトかを返す
  • 「文字値」または「文字変数の格納値」が欠損値の場合、返される値は以下の通り
    • LENGTH関数 = 1
    • LENGTHN関数 = 0




*** Sample data ;
data DT1;
    length X $10.;
    X="abc"; output;
    X=""; output;
run;







*** length, lengthn関数を使う ;
data DT2;
    set DT1;
    LEN   = length(X);
    LENN = lengthn(X);
run;

上の例では、Xが欠損値の時に関数の結果が異なっています。
この挙動を理解していないと、プログラマが意図しない結果になってしまいます。


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)

2017年9月8日金曜日

【KCOUNT関数】変数値にマルチバイト文字が含まれているか確認する方法




KCOUNT関数で「変数値にマルチバイト文字が含まれているか」確認することが出来ます。


(最初に注意点。日本語は全部マルチバイトだと思いがちですが、Shift_JIS環境においては「アイウエオ」などの半角カタカナは「1バイト」なので、KCOUNT関数では検出できない点に注意!
「バ」など半角カタカナにつく濁点は、「ハ」と「゙」でそれぞれ1バイトの文字が2つある感じです。「パ」などの半濁点も同様)




構文

  KCOUNT( 変数 )


・指定した変数の中に含まれるマルチバイト文字の数を返す。




* Sample data ;
data DT1;
input X:$20.;
cards;
ABCD
ABCD
あいうえお
123五
;
run;










* マルチバイト文字があるか確認する ;
data DT2;
 set DT1;
 Y = kcount(X);
run;



2017年9月7日木曜日

関数内での配列の使い方



「定義した配列を関数の中で使うにはどのように書けばよいですか?」

という質問を頂くことがあるので、簡単に紹介します。



* Sample data ;
data DT1;
    X=1;
    Y=2;
    Z=3;
run;






* 関数内で配列を使う例 ;
data DT2;
    set DT1;
    array AR(*) X Y Z;

    * 例① ;
    TOTAL1 = sum( AR(1), AR(2) );

    * 例② ;
    TOTAL2 = sum( of AR(*) );
run;



「例①」では配列からAR(1)とAR(2)を指定しています。
「例②」では配列ARの全要素を指定しています。

ひとつだけ注意点。
配列には色々な種類がありますが、その配列の種類によっては、例②の書き方が通用しない関数があります(実行時にERRORになる)



2017年9月5日火曜日

HTML出力で、カーソルを合わせた時にテキストをポップアップさせる




PROC REPORT や PROC PRINT でHTML出力する際 STYLE = [ FLYOVER="テキスト" ] という感じでポップアップテキストを仕込むことが出来ます。



以下の例は、HTML出力がONになっている前提です。

proc report data=SASHELP.CLASS;
    column NAME AGE HEIGHT;

    define NAME / display;
    define AGE / display;
    define HEIGHT / display;

    compute AGE;
       if AGE > 12 then
          call define("AGE","style","style(calldef)=[color=blue flyover='AGE > 12']");
    endcomp;
run;


STYLE=オプション内で、
  • AGE>12 のレコードの年齢を青く表示するよう指定
  • 同時に 「FLYOVER=」を使ってAGE>12のレコードの年齢にカーソルを合わせると "AGE > 12" というテキストがポップアップするよう指定

📝 ただし

STYLE=オプションは色々な場所に設定することができますが、それら全てについて正しく動作するか検証できていないので、ご利用時には動作確認を行ってください


あと話それるけど、高頻度で「うまくいかない!」と質問いただくので、こちらも参照下さい

そもそも「STYLE=オプション」って何?って方は以下記事も参照下さい
REPORTプロシジャ入門8:書式の設定【STYLE=】