ラベル 関数紹介 の投稿を表示しています。 すべての投稿を表示
ラベル 関数紹介 の投稿を表示しています。 すべての投稿を表示

2023年12月1日金曜日

各種クオーテーションマークを含む文字列を持つマクロ変数を展開する話

 仕様を私がきちんと理解しているわけではないですが、クオーテーションマークを含んだ文字列を持つマクロ変数を展開するにはsymget関数を使うと便利ですよ、との紹介です。確かに展開は出来ているのですがどうしてこうなっているのかはわかっていないのであまり深く掘り下げないでください。

事の起こりは会社でよくできる後輩君から掲題のことを聞かれました。私のところでは紆余曲折あってマクロ変数を無事展開できたのですが、どうも後輩君のところではうまく展開できずにいました。手元ではできるのになんでだろうな…と思っていると、実際の展開箇所でsymget関数を使って展開するのと、マクロ変数を""で挟んで展開しているという違いに行きつきました。私はsymget関数を使っていましたが、正直この関数はcall symputxで作ったマクロ変数を同じdata stepで展開できる関数くらいにしか思っておらず、その他のマクロ変数の展開ではマクロ変数を""で挟むのもsymget関数を使うのも同じ挙動を示すと思っていました。ところが違うんですねえ…どうやら。

仮に以下のようなシングル、ダブルクオーテーションを両方含んだ文字列を持つマクロ変数hogeを準備して、データステップで展開するとします。

%let hoge = I'm a "sas" user' ;

このマクロ変数をダブルクオーテーションで挟んで展開すると、案の定errorが出ます。今の場合ではerrorが出るのでデータステップが完了していません。

  data aa ;

      hage = "&hoge" ;

NOTE: マクロ変数"HOGE"によって生成されたライン

1      "I'm a "sas" user ;'

       -----------

       49      388

               202

NOTE 49-169: 引用符で囲まれた文字列の後の識別子の意味は、将来のSASリリースで変わる可能性があります。

             引用符で囲まれた文字列と識別子の間にスペースを挿入することをお勧めします。

ERROR 388-185: 算術演算子を指定してください。

ERROR 202-322: オプションまたはパラメータを認識できません。無視します。

2238  run ;

NOTE: 以下の箇所で文字値を数値に変換しました。(行:カラム)

      1:2    1:13

NOTE: エラーが発生したため、このステップの処理を中止しました。

WARNING: データセットWORK.AAは未完成です。このステップは、0オブザベーション、2変数で停止しました。

WARNING: このステップを中止したため、データセットWORK.AAを置き換えていません。

NOTE: DATA ステートメント処理(合計処理時間):

      処理時間           0.03 秒

      CPU時間            0.03 秒


ところがマクロ変数hogeの展開をsymget関数で行うとerrorが出ず実行されます。

  data aa ;

      piyo = symget("hoge") ;

  /*    hage = "&hoge" ;*/

  run ;

NOTE: データセットWORK.AAは1オブザベーション、1変数です。

NOTE: DATA ステートメント処理(合計処理時間):

      処理時間           0.03 秒

      CPU時間            0.01 秒


出来上がったデータセットAAを見ると、不思議と格納されています。これなんでなんでしょうね。私は意識せずsymgetをなんでか使っていたので特に意識することなくここまで来てしまったのですが…






2021年6月10日木曜日

特定文字のunicodeが知りたい話

 βとかαなどギリシア文字に半角用フォントを充てるときや,≧や③といった文字を出力するときにunicodeを直接指定するときがあると思います.βなら^{unicode 2265}だったりする4文字のやつですね.毎度unicodeを検索しなくてもunicodec関数を使用すればsasである程度は表示できるので,その関数の紹介です.

例えば以下の実行結果ですが,上で取り上げたβと≧のunicodeをlogに出しています.

131    data _null_ ;
132      str1 = unicodec("β" ) ;
133      str2 = unicodec("≧" ) ;
134      putlog "β:" str1 "≧:" str2 ;
135    run ;

NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.00 秒

β:\u03B2 ≧:\u2267

他にも③だと以下の通りです.

136    data _null_ ;
137      str1 = unicodec("③" ) ;
138      putlog "③:" str1 ;
139    run ;

NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.00 秒

③:\u2462

分からなくて毎回調べるのが大変だったのでユニコードを出力できるこのunicodec関数は結構助かっています.sasのhelpを見ると「This function reads characters that are in the current SAS session encoding and converts them to Unicode encoding.」とあるので,現在のsasのセッションエンコーディングの文字を読み取って,unicodeに変換する関数のようです.なのでsjisの環境だと上付きの数字などのunicodeを出せないのが残念でならないです…上付きの数字は結構使うので惜しいところです.unicode環境で上付き文字を指定するとunicodeは出せると思います.試してないですが.

ちなみにunocodec関数には第二引数があり出力時のエスケープ文字を変えれるのですが,今回のようなユニコードを調べるだけならデフォルトのままで不都合がないのでそのままにしています.例えば第二引数に"paren"と指定すると<03B2>のようにカッコくくりになります

2021年1月1日金曜日

連続した複数のブランクを1つに削る話

 たまにミスか知らないですけど1つで良いところに2つくらいスペースが入ってるときを見かけます.え?そんなに見ない?
連続した空白の重複を取り除くのって知人のプログラムでは大層なことをやっていたのですが,関数一発で片付くのです…もちろん配列とか駆使すれば何とかなりますけど…

以下の実行ログを見てもらえればいいのですが,compbl関数の紹介です.
aの変数にはhoとgeの間にスペースが3つはいっていますが,compbl関数を通したbの変数ではスペースが1つになっています.あんまり使う機会無いのでいっつも使いたい時には忘れているのですが…
スペースは何個あっても1つになります.スペース自体を消す時はcompress関数ですね

79129   data _null_ ;

79130   a = "ho   ge" ;
79131   b = compbl(a) ;
79132
79133   putlog a b ;
79134   run ;

ho   ge ho ge

NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.00 秒


2020年2月1日土曜日

現在の作業ディレクトリの変更する関数の話

dlgcdir関数を使えば現在の作業ディレクトリが変更できます.
正しく実行できると戻り値は0になります.

415  /*現在の作業ディレクトリをlogに表示*/
416  data _null_;
417    rc=dlgcdir();  /*()を空にして実行すると現在のディレクトリを表示する*/
418    put rc=;         /*正しく実行できているかの確認_別に不要ではあるが見るのには便利*/
419  run;

NOTE: 現在の作業ディレクトリは"piyopiyo"です。
rc=0    /*正しく実行できているので戻り値が0*/
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.00 秒


420
421  /*現在の作業ディレクトリをhogeに変更*/
422  data _null_ ;
423  rc = dlgcdir("hoge") ;  /*()に指定したフォルダを現在のディレクトリとする*/
424  put rc= ;
425  run ;

NOTE: 現在の作業ディレクトリは"hoge"です。
rc=0
NOTE: DATAステートメント処理(合計処理時間):
      処理時間           0.00 秒
      CPU時間            0.00 秒


現在のディレクトリを変更することで,指定したフォルダ内のプログラムを%incで読み込む際にファイル名の指定だけで済むので記述が短くなって良いですね.もちろん現在のディレクトリを変更しているので,何が指定したフォルダに出力されるかを確認する必要はありますが.

例えば指定したフォルダの中のhoge.sasとhoge2.sasを実行するには%incにファイル名を書くだけでokになります.もちろん実行するプログラムの前にフォルダパスを記載すれば指定していないフォルダのプログラムも実行できますが,タラタラ前に書くのがめんどくさい時もありますし,フォルダパスをマクロ変数に格納して何かのはずみでマクロ変数の上書きが意図せず起きたりするのが結構ややこしい印象です.

%inc "hoge.sas" ;
%inc "hoge2.sas" ;

私は使ったことないですけど便利そうですよね,そのうち使うかもしれません.
 
後はファイルメニューの「プログラムを開く」で,最初に表示されるファイルの場所が現在のディレクトリになれば最高なんですが…今はsasの保存場所かなんかが最初に出るんですよね…最初と言わずとも現在のディレクトリにすぐアクセスできればokなんですが.

2019年6月1日土曜日

変数の型を調べる話

変数の型を調べる方法を紹介するお話です.

proc contentsとかいろいろありますが,今回はvtype関数を取り上げます.この関数でデータセット内の変数を指定すると,その変数が文字か数値かが返ってきます.複数の変数を調べるときはcontentsプロシジャ使うほうがいいですが,こちらの関数も小回りが利いて使うときがたまにあります.

試しに動かしたときのログが以下です.データセットhogeに数値変数aと文字変数bを作ります.
変数aの型をatypeに,bの型をbtypeに取得しているのが次のデータステップです.結果もログに表示させていますが,数値変数ならNが,文字変数ならCが得られます.

そんなに変数の型が変わるのはそもそもよくないのですが,知ってるとデータステップ内で取得できるので便利です.
72 data hoge ; 73 a = 1 ; b = "TST" ; 74 run ; NOTE: データセットWORK.HOGEは1オブザベーション、2変数です。 NOTE: DATAステートメント処理(合計処理時間): 処理時間 0.00 秒 CPU時間 0.00 秒 75 76 data hoge2 ; 77 set HOGE ; 78 79 ATYPE = vtype(a) ; 80 BTYPE = vtype(b) ; 81 82 putlog "ATYPE= " ATYPE ; 83 putlog "BTYPE= " BTYPE ; 84 run ; ATYPE= N BTYPE= C NOTE: データセットWORK.HOGEから1オブザベーションを読み込みました。 NOTE: データセットWORK.HOGE2は1オブザベーション、4変数です。 NOTE: DATAステートメント処理(合計処理時間): 処理時間 0.00 秒 CPU時間 0.01 秒

2018年6月15日金曜日

call is8601_convertルーチンの話

is8601_convertコールルーチンは,日付や2つの日付の間の間隔をiso8601フォーマットに変換します.
この日付の間隔のことを,デュレーション値と呼ぶようです.
たとえば始点が4月1日,終点が4月2日のとき,デュレーション値は1日です.
第一引数に変換前の値に何が入るかを指定し,第二引数に変換後の値が何であるかを指定します.
第三引数に変換前の値,第四引数に変換に使う値,第五引数に変換後の値を格納する変数を指定します.

第一引数に'du/dt'と指定すると,第三引数にデュレーション値,第四引数に日付を指定します.
これが逆になるとエラーが返ります.
同様に'dt/du'と指定すると,日付→デュレーション値の順に指定をしないといけません.

とてつもなくわかりにくいので以下にいくつか具体例を示します.

data _null_ ;
  format hoge e8601dn. ;
  /*変換前の値はデュレーション値と日付,変換後に期間の開始日を出力,期間は8週間,期間の終点は2018年1月1日*/
  call is8601_convert('du/dt' , 'start' ,"p8w" ,"2018-01-01" ,  hoge ) ;
  putlog hoge = ;
run ;

hoge=2017-11-06


data _null_ ;
  format hoge e8601dn. ;
  /*変換前の値は日付とデュレーション値,変換後に期間の終了日を出力,期間の開始日は2018年1月30日,期間は8週間*/
  call is8601_convert('dt/du' , 'end' ,"2018-01-30" ,"p8w" , hoge ) ;
putlog hoge = ;
run ;

hoge=2018-03-27


data _null_ ;
  length hoge $20 ;
  format hoge $n861b. ;
  /*変換前の値に日付と日付,変換後はデュレーション値,期間の始点は1月30日,終点は3月14日*/
  call is8601_convert('dt/dt' , 'du' ,"2018-01-30T12:30" ,"2018-03-14T14:45" , hoge ) ;
  putlog hoge = ;
run ;

hoge=P1M15DT2H15M

2016年8月11日木曜日

repeat関数

指定した文字を指定した回数出力する関数。
構文は repeat("文字",n) です。
n+1回、指定した文字を出力します。
nには0以上の数しか入らないので、最低でも1回出力することになります。

x = repeat("hoge" , 2);
put x;

とすると
hogehogehogeと出力されます。


文字のところに" "とスペースも入れられます。
スペースの数をこの関数で制御して、
6 (  50.0)
7 (100.0)
2 (    2.0)
のように、めんどくさい桁そろえするときに重宝します。
あまり機会がないですが。

小数点の位置を揃える桁そろえのめんどくさいことめんどくさいこと