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関数で、何番目の最小値、最大値とれるからソートの必要ないですね、すみません。





4 件のコメント:

  1. こんにちは。
    いままでchoosec関数の使い道を見つけられなかったんですが、変数の数が可変の場合はとても有効ですね!勉強になりました!

    ちなみにご存じかもしれないですが、CALL SORTHというのが評価版ながらあるみたいですね。
    全て数値か、全て文字の場合は、どちらでもSORTHでいけるようです。

    data DT1;
    N1=2;
    N2=1;
    C1="b";
    C2="a";

    call sorth(of N:);
    call sorth(of C:);
    run;

    使えそうだけどSASオンラインドキュメント見ても、いまいち載ってない気がするんですよね。。

    ちなみに話がそれてしまいますが、SASって他にも未知の関数が結構あって、
    DMIN、DMAX、DMEAN、DSUM関数とか、たまたま見つけたんですがご存じでしょうか?
    色々プログラム作って挙動を試してみたら使えそうな関数な気がするんですが、
    検索しても引っかからないし、マニュアルにも載ってないっぽいので、使いづらいんですよね。。。

    返信削除
    返信
    1. こんにちわ。
      おおっ!そんな便利ルーチンが!知りませんでした!てか、そういう文字数値両方いけるやつを後で追加するなら、最初からそれ作っとけよって感じですね。

      D系、存在は知ってました。SASHELP.VFUNC(ディクショナリーテーブル)に、その環境で使用可能な関数が全てデータセットとして参照できますが、ここの数とリファレンスやヘルプに載っている関数の数が合わない気がしたので差異を試してた時期がありましたが、VFUNCを調べると800個以上関数があって、すぐ挫折しました。
      せめてVFUNCに備考欄があって簡単な説明入れてくれればよかったのに。
      D系確かその時みかけていじりましたが、よく挙動がわかんなかったです。引数の数が全部で奇数だとキレてくるやつですよね?引数を一つ飛ばしで関数の対象にする?とかって適当に理解しましたが、多分違いますよね。教えて欲しいです。。


      そういう隠し技みたいなの、凄い使いづらいです。deleteプロシジャとかも9.4から追加されたことになってるけど、実はずっと前からどこにも載ってないけど、データセットを消すのに最速の裏シージャでしたもんね。

      削除
  2. こんにちは。

    存在するプロシージャや関数はすべてヘルプ等に載せておくべきだと思うのになんででしょうかねー。。
    D系は動かした感じ多分なんですが、以下のような挙動でした。

    DMAX関数の挙動…
    ①変数2つずつを1つのグループとする。
    ②まず各グループの第2引数の値が一番大きいグループの第1引数の値が返される。
    ③MAXになった第2引数が複数グループある場合は、そのグループの中で一番最初に指定されているグループが採用される。
    ④第2引数がNULLの場合、そのグループは無視される。

    DMIN関数も同様の挙動でした。
    DSUMとDMEANは第2引数の意味がよくわかりません。。④のルールは全関数共通みたいですが。


    data TEST;

    A1=50; A2=1;
    B1=20; B2=3;
    C1=30; C2=3;
    D1=40; D2=.;

    ANSWER1 = dmax(A1,A2, B1,B2, C1,C2, D1,D2);
    ANSWER2 = dmin(A1,A2, B1,B2, C1,C2, D1,D2);
    ANSWER3 = dsum(A1,A2, B1,B2, C1,C2, D1,D2);
    ANSWER4 = dmean(A1,A2, B1,B2, C1,C2, D1,D2);

    put (ANSWER1-ANSWER4) (=);
    run;

    結果ログ
    ANSWER1=20 ANSWER2=50 ANSWER3=100 ANSWER4=33.333333333

    返信削除
    返信
    1. 凄いわかりやすい例をくださって有難うございます!!
      というか、よくこんな複雑な挙動を読み解きましたね!!ペアをつくっているから、引数の合計が奇数だとエラーになったのか、なるほど、、。
      確かにこれはうまく状況にハマれば、複数ステップかかるような処理を一撃必殺できそうですね!にしても、リファレンスがないと想像の域になってしまうから使いづらいですね、、。

      削除