2026年5月4日月曜日

複数の文字変数の非欠損の数を数える話

SASで複数の変数の欠損の数を数えるにはcmiss関数があります。cmiss関数は便利な関数で、1obsの中で文字だろうが数字だろうが指定した変数の中の欠損の数を数えてくれます。非欠損の変数の数を数える際は、数字変数ならn関数というこれまた便利な関数があります。

一方文字変数の中で非欠損の数を数えたいときはいつも難儀していました。良い感じの関数が多分sasにはないです。あったらすみません。しょうがないので配列で数えたいうのが今回の記事です。文字変数と銘打っていますが欠測かどうかをmissing関数で調べているので、多分文字でも数字でも非欠損の数を出せると思います。文字でしか試してないのでerrorになるかもしれませんが。

対象の変数を指定している配列の部分をマクロ変数化すればいい感じのマクロになるんじゃないでしょうか?可変なのがその部分だけ、強いて言えばそこと結果を格納する変数名の部分だけなので。



data hoge ;
    a = "a" ; b = "" ; c = "" ; output ;
    a = "a" ; b = "b" ; c = "" ; output ;
run ;

data _null_ ;
    set hoge ;

    res = 0 ;
    array vars  a b c;
    do over vars ;
        if missing(vars) = 0 then res = res + 1;
    end;

    putlog res = ;

run;

/*>>>>>---------- 以下実行log ----------<<<<<*/

3956
3957  data _null_ ;
3958      set hoge ;
3959
3960      res = 0 ;
3961      array vars  a b c;
3962      do over vars ;
3963          if missing(vars) = 0 then res = res + 1;
3964      end;
3965
3966      putlog res = ;
3967
3968  run;

res=1
res=2

2026年3月1日日曜日

フォルダに格納したsasファイルを全部読み込む話

 先日紹介したsas公式のgithubの中に、指定したフォルダ内の全てのsasプログラムを読み込むマクロが含まれている。ある程度指定した順番に読み込んだりと高機能なものだ。

その辺の微調整が出来ないもっと単純な読み込みプログラムを私も持っているので、せっかくということで記事にする。以下の1行で指定したフォルダ、今回はhoge直下の.sasファイルを全部読み込んでいる。読み込む順番が調整できなかったりなどできないことが多いが、まあ気を付けていれば私には今の所コレで十分ではある

%include "%nrbquote(hoge\*.sas)";

2026年2月1日日曜日

0件のデータセット作るときに初期化が云々のメッセージって出ないように出来ませんでしたっけ?

変数ラベルや長さなどの変数情報だけを確定させるために0obsのデータセットを作ることがあるのですが、昔はstopステートメントを仕込めば初期化されていませんのメッセージを出さないように出来……ましたよね?0件のデータセット作るときのログに初期化されていませんのメッセージ出るとちょっと嫌だったので……

どうもちょっと前からstopを仕込んでもログに初期化されて~のメッセージが出てる気がするんですよね…そもそも昔からこのメッセージは出ていたのか?手癖で処理していたのでちゃんと覚えてない自分が恨めしい

以下の様に変数aの定義だけ持たせた0件のデータを作成すると、値が無いのでメッセージが出ます

17   data piyo ;

18   attrib  a length = $10 ;

19   run ;

NOTE: 変数aは初期化されていません。  <- これ

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

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

      処理時間           0.03 秒

      CPU時間            0.01 秒


ここに以下の様にデータステップ内にstopステートメントを仕込むと、このメッセージを出なく出来ていた……ような……あいまいな記憶の記事ですみません。気のせいですかね

21   data piyo ;

22   attrib  a length = $10 ;

23   stop ;

24   run ;

NOTE: 変数aは初期化されていません。 <- これが昔は出なかったような…

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

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

      処理時間           0.06 秒

      CPU時間            0.01 秒


2026年1月19日月曜日

データセットがキーとなる変数で重複が無いかを確認する

 キーとなる変数でデータセットが一意に定まるかを確認する時はproc sortを使っています。nouniquekeyオプションを使えば、重複してるレコードをだけをデータセットとして作れるので便利ですね。

仮にデータセットhogeが以下の様にあったとして、変数aの値が1obsと2obsで1で重複しています

後は以下の通り重複するobsをKEY_NO_UNIQUEデータセットに格納して、このデータセットにレコードがあればログにerrorを出す、とすれば重複があるかどうかはログを見るだけでokです。詳しい重複の内容はデータセットを見てください。

proc sort data = hoge out = KEY_NO_UNIQUE nouniquekey  ;

  by a  ;

run ;

data _null_ ;

    if 0 then set KEY_NO_UNIQUE nobs = _nobs ;

    if _nobs ^= 0 then putlog "E" "RROR NoUnique: Check KEY_NO_UNIQUE dataset" ;

    stop ;

run ;


2025年12月22日月曜日

sasの公式がgithubつくってるやん

 2025年のsasユーザー会には都合が合わなかったので参加していなかったのだが、資料を振り返ると面白そうなのがあった。SAS公式がお出ししたSASのパッケージだ。最近何かと流行っているSASPACの仕組みを利用したパッケージ群にSASが公式が乗り出す形になっている。SASToolboxとか言う公式にふさわしい名前だ。これについての発表資料はこちら

データセットのutf8変換、フォルダ内のファイルの一括sas7bdat化、フォルダ内のsasファイルを一括インクルードとどれも使う場面の多いマクロが公開されている。一括インクルードは私も自作のがあるが、読み込み順をある程度引数で調整できるようになっているのは画期的だ。私にはここまで実装するのは難しかったのでプログラムファイル名の工夫でこの順番を制御していた。先頭に数字付けたりとか……

こうやってみんなが公開されてる同じパッケージの関数を使いだすと、業界的に必須のダブルプログラムが形ばかりになるよなとは思っている。正副で同じ関数使うと必然的に同じ結果が出てくるので何の検証にもならない。まあこの問題は突き詰めると最近流行りのOSSでも同じことが言えるので、その内偉い人がこうしよう的な話を出してくるだろう。下請けの私は右向け言われたら右向きます。

みんなで色んなものを公開して修正していくのは嫌いではない。メンテナ誰がやるねんとか、いつまで更新されてるんや!?とかプルリクエスト対応ってもしかしてタダ働きですか!?とか付随する様々な問題は既に見えているが、今はこの甘美なバタバタに身を任せたい。

それは別にして会社のパソコンからgithubにアクセスしてアップロード/ダウンロードするの怖いよね。セキュリティが。これは情報セキュリティ的に問題ない通信です!ってどうやって説明しているのだろう。こんな古臭いことを考えてるのは私だけか?

2025年8月30日土曜日

複数のエクセルファイルを結合し、指定したシートの順番に並び替えるマクロ

 複数のエクセルを結合して一つのブックにまとめる際に、シートの順番もこちらで指定したいときがあるとします。ありますよね?あるとしましょう。例えば100でも200でも良いのですが結果の図表をエクセルに出す必要があるとき、二人で各50個作って最後にまとめる時…とかです。既にあるエクセルファイルに後からシートを指定した位置に追加する時もそうです。ブックの結合もめんどくさいですし、シート順の調整なんて手でやっていたら夜どころか朝になってしまいます。

以下に記載のVBAでそれなりに結合してシート順も指定できて便利です、との自作VBA自慢の記事です。この手の記事はLLMの登場以降ニーズが少ない気もしますが…LLMに聞けば出してくれますしね。そんなことは知らん。自慢すると言っておろうが。

1シート目に実行ボタンを付けて、結合前のファイルを格納したフォルダと結合後のファイルを出力するフォルダをそれぞれhogeとpiyoに指定します。sheetSortOrderシートには結合後のシートの順番を記載しています。後は画像下にあるvbaのコードを張り付けて実行すれば結合が出来るというわけです。


罫線を入れているA列に結合後のシート順をしてください。


複数ファイルを指定すると結合+シート順の並び替えになりますが、結合前フォルダに1つのエクセルファイルだけにするとシート順の並び替えだけになります。これだけでもとても便利。以下がVBAのソースコードになります。

Sub combineMacro()

    Dim folderPath As String

    Dim filename As String

    Dim targetBook As Workbook

    Dim afterBook As Workbook

    Dim targetSheet As Worksheet

    Dim sheetSordOrder As Range

    Dim sheetName As String

    Dim sortFlag As Boolean

    Dim runTime As String

    Dim savePath As String

        

    ' 結合するExcelブックが保存されているフォルダのパスを指定

    folderPath = ThisWorkbook.Sheets("Sheet1").Range("D12").Value & "\"

    

    ' 新しいブックを作成

    Set afterBook = Workbooks.Add

    

    ' 結合マクロが保存されているブックのSheetSortOrderに記載されているシート順を取得

    Set sheetSordOrder = ThisWorkbook.Sheets("SheetSortOrder").Range("A1").CurrentRegion

    

    ' フォルダ内のExcelブックを結合

    filename = Dir(folderPath & "*.xlsx")

    Do While filename <> ""

        Set targetBook = Workbooks.Open(folderPath & filename)

        

        ' シートを結合ブックに追加

        For Each targetSheet In targetBook.Sheets

            targetSheet.Copy After:=afterBook.Sheets(afterBook.Sheets.Count)

        Next targetSheet

        

        targetBook.Close False

        filename = Dir

    Loop

    

    ' シートを並び替える

    For Each targetSheet In afterBook.Sheets

        sheetName = targetSheet.Name

        sortFlag = False

        

        ' シート名がシート順に含まれているかチェック

        For Each V_CELL In sheetSordOrder

            If V_CELL.Value = sheetName Then

                targetSheet.Move Before:=afterBook.Sheets(V_CELL.Row)

                sortFlag = True

                Exit For

            End If

        Next V_CELL

        

        ' シート名がシート順に含まれていない場合は削除

        If Not sortFlag Then

            Sheets(sheetName).Delete

        End If

    Next targetSheet

    

    ' 結合ブックを保存

    ' 実行日時を取得

    runTime = Format(Now, "yyyymmdd_hhmmss")

    

    ' 保存パスを取得

    savePath = ThisWorkbook.Sheets("Sheet1").Range("D14").Value & "\"

    afterBook.SaveAs savePath & "結合後" & runTime & ".xlsx"

    ' ブックを閉じる

    afterBook.Close   

    ' メッセージを表示

    MsgBox "結合が完了しました。"

End Sub



2025年7月1日火曜日

OSSのライセンスについての話

 最近はRやpythonなどのOSSの利用が進んでいる。COREなんかはMITライセンスなのであまり細かいことは気にしなくても良いが、利用するパッケージによってはAGPL>3.0やapach>2.0等のライセンスが設定されている。apach>2.0はそのライセンスのOSS利用した場合は特許や許諾の表示を入れなければならないし*1、AGPL>3.0は成果物が公表された場合は利用したソースコードを公開しなければならない*2

…等と定められているが、例えばAGPL>3.0は2.Basic Permissionsに「The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work.」…実行された対象からの出力が、内容から判断してその対象物に該当する限りライセンスの対象にする…とある。製薬業界ではライセンス対象であるソースコード…往々にしてこれは当局への説明資料…が、最終成果物である医薬品には直接使用されていないので、ライセンス対象にはならないのではないか。もちろんこれは1ペーペーのおっさんが垂れ流している駄文なので正しいかはわからない。法務に詳しい方が見れば何を当たり前のことを…?/何を的外れなことを…?となるかもしれないが、現時点で何となく私の考えは上述の通り。

これまでSASという大きな傘の下にいたがOSSという大海原に漕ぎ出すにあたって、ライセンスなどの基本的な事項について一度立ち止まって考える機会はこれからも大切にしていきたい。


*1

https://licenses.opensource.jp/Apache-2.0/Apache-2.0.html

4.再頒布より

*2

https://www.gnu.org/licenses/agpl-3.0.html

2.Basic Permissionsより