SAS

SASプログラム高速化テクニック

最近SASはyoutubeに解説動画を積極的にアップしているらしく、私もいくつか視聴しています。

正直動画よりもPDFで公開してくれた方がうれしいのですが・・・

SAS Viyaだけでなく各種統計手法に関する解説もあるようなので、意外とやくにたつかもしれませんね。

今回はその中でSASコードを早くするテクニックが紹介されていたので
実際に試してみました。内容自体はベテラン勢なら当たり前かと思いますが、意識しておくのは重要です。

解説動画

www.youtube.com

動画で使用しているコードはgithubにて公開しています。

動画ではSASデータセットとlibname oracleをつかって外部データベースに接続した場合を検証しています。

で試しに実行してみたのですが、、、Academic OnDemandでは動作しません。動画ではSAS Viya上で動作していたので
仕方がないのでSAS VIyaの試用版に登録して試したのですが、libname oracle とcasは動作しませんでした。

試用版だと制限があるのか正しく動作しないですね。実際に試したい方はSAS Viyaのライセンスを持っていることが前提なのでお気を付けください。

とはいえ解説されている内容はSAS baseにおいても適用できるものです。実際に試さなくても知識として憶えておくだけで十分でしょう。

高速化テクニック

keepステートメントではなくkeepオプションを使用する

*keepオプションを使用; 
data test; 
set db(keep=id); 
run; 

*keepステートメントを使用; 

data test; 
set db; 
keep id; 
run;

特定の変数を抽出する場合keepオプションかkeepステートメントを使いますが、前者のほうが早いです。
keepオプションはsetステートメントでデータセットを読み込む段階で変数を抽出しますが、keepステートメントはデータセットをすべて読み込んだ後に変数を抽出します。
余計な変数を読み込まない分前者のほうが早いです。

SASデータセットだと顕著な差が出てこないのであまり意識している人は少ない印象ですが、レコード数が多くなると影響は大きくなるかもしれません。
またデータベースからデータを読み込むときは影響が大きいようです。動画では10倍以上実行速度に差が出ているので、外部データベースに接続する場合は特に注意です。

サブセットIFではなくwhereステートメントを使用する。

*whereステートメント; 
data test; 
set db; 
where id=4; 
run; 

*サブセットif; 
data test; 
set db; 
if id=4; 
run;

特定条件に合致するレコードを抽出するときはwhereステートメントかサブセットifを使用しますが、できるだけ前者を使ったほうが早いです。

whereステートメントはデータセットを読み込む段階でレコードが抽出されますが、サブセットifは一連の処理が終わった後にレコードが抽出されます。
サブセットifは抽出の対象ではないレコードに対しても処理を実施することになるので遅くなります。ただし一連の処理を実施した結果に基づいてレコード
を抽出する場合はサブセットifを使うことになります。

こちらもSASデータセットではそこまで差は出ないですが、データベースからデータを取得する場合はその差は顕著に表れるようです。動画ですと最大で30倍近く差が出るようです。

SAS関数ではなくANSI演算子を使う

*Like演算子; 
data test; 
set db; 
where usubjid like "A%"; 
run; 

*SAS関数; 

data test; 
set db; 
where scan(usubjid,1,"-")="A"; 
run;

動画ではLike演算子とscan関数を使用して特定の文字列を有するレコードを抽出していますが、Like演算子のほうが高速に結果が得られました。これはデータステップとsqlどちらも同じ結果でした。
およそ5倍程度早くなるらしいですが、個人的には微妙かなと思いました。

Like演算子によるパターンマッチングはそこまで多機能ではないですから意図しない結果になりそうなので私は使ったことないです。
このようなケースでは実務上SAS関数か正規表現を使うのがセオリーなのではないでしょうか。
データのクリーニングが完了しているのであればlikeでもよさそうですけど、そうでないのなら私は推奨しないです。

データステップとSQLを使い分ける。

動画によりますと単純な要約統計量の算出であればSQLよりもデータステップのほうが早いらしいです。データステップの場合は合計ステートメントを使っているわけですね。
ただしこのような結果になるのはSASデータセットのときで、外部データベースだとSQLのほうが早くなります。

やはりデータステップはSASデータセットに対して最適化されており、SQLはデータベースに対して最適化されているのでしょうかね。

*合計ステートメントで要約統計量を算出する; 
data summary; 
keep Count Total Average; 
set sas.narrow end=last; 
Total+ru; 
if not missing (ru) 
then Count+1; 
if last then do; 
   average=Total/Count; 
   output; 
end; 
run; 

*SQLの関数で要約統計量を算出する。 
proc sql; 
create table summary_sql as 
select count(ru) as Count ,
       sum(ru) as Total ,
       mean(ru) as average 
from sas.narrow ; 
quit;

とはいえデータステップでこれをやるのはちょっと非現実的じゃないですかね・・・可読性悪すぎる。これだったらSQLを使ったほうがいいでしょう。SQLよりも40%ほど早くなるそうですが、こんなの保守したくありません。それにデータステップだと4分位数の算出は難しいですよね。

また一つのデータステップで複数のデータセットを吐き出すパターンはSQLよりもSASデータステップのほうが早くなるので、動画ではデータステップの使用を推奨しております。
SQLだとcreate tableを繰り返すことになるので、確かにこの場合はデータステップのほうがよさそうですね。

data group1 group2 group3; 
set db; select; 
when (result<=1) output group1; 
when (result<=2) output group2; 
when (result<=3) output group3; 
otherwise; 
end; 
run;

CASあるいはproc DS2を使う

CASとはCloud Analytic Serviceの略でSASを使用したデータ管理および分析のためのクラウドベースの実行環境を提供するサーバーです。CAS上でSASプログラムを実行することで処理と結果が高速化されます。

要はクラウドを使ってマシンリソースを拡張、最適化しているわけですから、処理が早くなるのは当たり前でしょう。SAS viyaのライセンスがないと使えませんからほとんどのユーザーには関係ない話です。

CASが使えない場合はproc ds2のマルチスレッドを使うという手もありますが・・・CASを導入するメリットの乏しいユーザーがproc ds2に乗り換える動機があるのかは怪しいと思います。

まとめ

大量データを処理するのでなければ最初の2つくらいを押さえておけば十分でしょう。高速化にこだわりすぎて可読性、保守のしやすさを失わないようにしましょう。