先日紹介したsas公式のgithubの中に、指定したフォルダ内の全てのsasプログラムを読み込むマクロが含まれている。ある程度指定した順番に読み込んだりと高機能なものだ。
その辺の微調整が出来ないもっと単純な読み込みプログラムを私も持っているので、せっかくということで記事にする。以下の1行で指定したフォルダ、今回はhoge直下の.sasファイルを全部読み込んでいる。読み込む順番が調整できなかったりなどできないことが多いが、まあ気を付けていれば私には今の所コレで十分ではある
sasの個人用メモみたいなもの
先日紹介したsas公式のgithubの中に、指定したフォルダ内の全てのsasプログラムを読み込むマクロが含まれている。ある程度指定した順番に読み込んだりと高機能なものだ。
その辺の微調整が出来ないもっと単純な読み込みプログラムを私も持っているので、せっかくということで記事にする。以下の1行で指定したフォルダ、今回はhoge直下の.sasファイルを全部読み込んでいる。読み込む順番が調整できなかったりなどできないことが多いが、まあ気を付けていれば私には今の所コレで十分ではある
変数ラベルや長さなどの変数情報だけを確定させるために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 秒
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 秒
キーとなる変数でデータセットが一意に定まるかを確認する時は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年のsasユーザー会には都合が合わなかったので参加していなかったのだが、資料を振り返ると面白そうなのがあった。SAS公式がお出ししたSASのパッケージだ。最近何かと流行っているSASPACの仕組みを利用したパッケージ群にSASが公式が乗り出す形になっている。SASToolboxとか言う公式にふさわしい名前だ。これについての発表資料はこちら
データセットのutf8変換、フォルダ内のファイルの一括sas7bdat化、フォルダ内のsasファイルを一括インクルードとどれも使う場面の多いマクロが公開されている。一括インクルードは私も自作のがあるが、読み込み順をある程度引数で調整できるようになっているのは画期的だ。私にはここまで実装するのは難しかったのでプログラムファイル名の工夫でこの順番を制御していた。先頭に数字付けたりとか……
こうやってみんなが公開されてる同じパッケージの関数を使いだすと、業界的に必須のダブルプログラムが形ばかりになるよなとは思っている。正副で同じ関数使うと必然的に同じ結果が出てくるので何の検証にもならない。まあこの問題は突き詰めると最近流行りのOSSでも同じことが言えるので、その内偉い人がこうしよう的な話を出してくるだろう。下請けの私は右向け言われたら右向きます。
みんなで色んなものを公開して修正していくのは嫌いではない。メンテナ誰がやるねんとか、いつまで更新されてるんや!?とかプルリクエスト対応ってもしかしてタダ働きですか!?とか付随する様々な問題は既に見えているが、今はこの甘美なバタバタに身を任せたい。
それは別にして会社のパソコンからgithubにアクセスしてアップロード/ダウンロードするの怖いよね。セキュリティが。これは情報セキュリティ的に問題ない通信です!ってどうやって説明しているのだろう。こんな古臭いことを考えてるのは私だけか?
複数のエクセルを結合して一つのブックにまとめる際に、シートの順番もこちらで指定したいときがあるとします。ありますよね?あるとしましょう。例えば100でも200でも良いのですが結果の図表をエクセルに出す必要があるとき、二人で各50個作って最後にまとめる時…とかです。既にあるエクセルファイルに後からシートを指定した位置に追加する時もそうです。ブックの結合もめんどくさいですし、シート順の調整なんて手でやっていたら夜どころか朝になってしまいます。
以下に記載のVBAでそれなりに結合してシート順も指定できて便利です、との自作VBA自慢の記事です。この手の記事はLLMの登場以降ニーズが少ない気もしますが…LLMに聞けば出してくれますしね。そんなことは知らん。自慢すると言っておろうが。
1シート目に実行ボタンを付けて、結合前のファイルを格納したフォルダと結合後のファイルを出力するフォルダをそれぞれhogeとpiyoに指定します。sheetSortOrderシートには結合後のシートの順番を記載しています。後は画像下にある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
最近は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より
二つの期間の間の日数を求めたい時ありますよね?ADaM的にはADYの変数で定義されているようなやつです。せっかくなので流行りの{admiral}のお試しがてら期間の変数を算出します。別に{admiral}である必要はないのですが、せっかくよさげなパッケージがあるなら使ってみるのがOSSの醍醐味でしょう。
なにはともあれまずはパッケージのインポートですね。なかったらinstall.packages()してください。諸々実行して最後のオブジェクトHOGEにxxdyの変数が作成されています。便利な関数だなあ…!(ダイマ)