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

call sortc(sortn sorth)ルーチンで降順ソートする小技

call sortc(sortn sorth)ルーチンについては何度か取り上げています。

・横方向(列方向)にcall sortn,call sortcルーチンを使ってソート処理をかける
http://sas-tumesas.blogspot.jp/2013/10/call-sortncall-sortc.html

・CHOOSEN(C)関数とCALL SORTN(C)ルーチンで、横に可変的に増える変数をソートして指定した順番にくる変数を取得する
http://sas-tumesas.blogspot.jp/2014/02/choosenccall-sort.html

call sort系ルーチンには降順指定がないため、基本的には昇順しかできません。
それをどうしたら降順にできるかという話です。
※もしかしたら、既にどこかの記事で話したか、或いはamatsuさんからコメント等で教えて貰った内容かもしれません。最近、自分でも何を投稿したか忘れてて、もし重複してたらすみません。

今、

data Q1;
X1='S';
X2='Y';
X3='O';
X4='G';
X5='I';
run;



があって

data A1;
set Q1;
call sortc(of X1-X5);
run;

とすると






このように昇順になります。

これを降順にするには

data A2;
set Q1;
call sortc(of X5-X1);
run;

というように指定を逆向きにすれば






と、結果的に降順になります。

amatsuさんが
記事:変数指定は「V100-V1」のように逆にもできる。
http://sas-boubi.blogspot.jp/2014/06/v100-v1.html

で紹介されていたことと理屈は同じです。

引数に配列を使う場合は、配列に要素を指定する場合の、指定順番を逆にすれば
OKです。

CHOOSEN(C)関数とCALL SORTN(C)ルーチンで、横に可変的に増える変数をソートして指定した順番にくる変数を取得する

固定されたデータが手元に完全にある状態で、プログラムを書けることは幸せです。
実際のデータをみて取りうる範囲から固定した処理がかけるからです。

しかし往々にして、手元にこないと実際どんなデータかわからない、あるいは見てはいけない、という状況で、どんなデータが来ても可変的に対応できるプログラムを書くこともあると思います。



data Q1;
X_1='か';X_2='あ';X_3='さ';X_4='た';output;
X_1='B';X_2='C';X_3='A';X_4='Z';output;
run;






こんなデータがあったとします。

そして非常識なことに、なぜか、このデータは横にも増える可能性があるとします。
今プログラムを組んでも、次に実行する時のデータにはX_5やX_6があるかもしれません。

そして、求められている処理は、1行ごとに、横に辞書的に並び替えて、一番後ろから2番目に並ぶ
値を特定するという、これまたよくわからない処理だとします。

縦に増えていくデータは余裕ですが、横に変数が増えていくデータはやっかいです。
ありえないと思っていても、なぜか現実にそういうデータに当たることもあるものです。

転置して縦にしてから、降順ソートして、2番をとるような処理でもいいですが、できれば構造を
変えず単純にできないかと考え、以下のコードを考えました。


data A1;
set Q1;
call sortc(of X_:);
A=choosec(-2,of X_:);
run;






一行目は「さ」がお尻から2番目の文字で、2行目はCです。

横にいくら変数が追加されてもX_という接頭語が崩れない限りはX_100までいこうが動きます。
仮に変数がX_1の一つだけでもAがnullになるだけで、エラーにはなりません。

call sortcルーチンで変数間で値をソート順に交換させて、からchoosec関数で、指定した順番にいる変数を取得します。-2とすることで、右端から2個目(つまりソートされた最大の値の一つ前)をとることができます。

たった4行で書けたので自分的には大満足です。

もし対象が数値型なら

call sortcはcall sortn
choosec はchoosenに変えてください。


/*追記*/
てか、数値型なら、そもそも smallest関数とlargest関数で、何番目の最小値、最大値とれるからソートの必要ないですね、すみません。





横方向(列方向)にcall sortn,call sortcルーチンを使ってソート処理をかける

SASで並び替えというと、縦方向(行方向)、つまりオブザベーションの値を以て並び替える処理が通常で、あまり横にソートするということは多くないと思われます。
ARRAYで

たとえば以下のようなデータセット

data Q1;
 X=3;Y=2;Z=1;output;
 X=-1;Y=.;Z=10;output;
 X=1;Y=3;Z=2;output;
run;


があったとして
3つの変数の値の大小を比較して一番小さい値をX、2番目をY、、というように
値を並び替えたいとします。
つまり、欲しい結果は以下のようになります。


この場合


data A1;
 set Q1;
 call sortn(of X--Z);
run;

で詰みです。
CALL SORTNルーチンは数値変数を指定することで、値によって
変数の中身をソートしてくれます。


また次のデータセットのように値が文字型の場合はどうでしょうか

data Q2;
length X Y Z $2.;
 X='B';Y='A';Z='C';output;
 X='さ';Y='か';Z='あ';output;
 X='';Y='D';Z='-';output;
run;


この場合はCALL SORTCルーチンを利用して以下のように書けます。

data A2;
 set Q2;
 call sortc(of X--Z);
run;

すると実行結果は


となります。


これを知らないと、ARRAYを使ってループで大小比較したり、
TRANSPOSEしてSORTしてTRANSPOSEして戻すみたいな少し面倒な処理をやってしまいがちです。