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より

2025年6月4日水曜日

Rで二つの日付の間の期間の日数を求める

 二つの期間の間の日数を求めたい時ありますよね?ADaM的にはADYの変数で定義されているようなやつです。せっかくなので流行りの{admiral}のお試しがてら期間の変数を算出します。別に{admiral}である必要はないのですが、せっかくよさげなパッケージがあるなら使ってみるのがOSSの醍醐味でしょう。

なにはともあれまずはパッケージのインポートですね。なかったらinstall.packages()してください。諸々実行して最後のオブジェクトHOGEにxxdyの変数が作成されています。便利な関数だなあ…!(ダイマ)


library(admiral)
library(dplyr)
library(lubridate) # 日付を操作するパッケージ
library(tibble) # テストデータを作るのに使用(tribble関数)

# データ作成
datain <- tribble(
  ~TRTSDTM,              ~ASTDTM,               ~AENDT,
  "2014-01-17T23:59:59", "2014-01-18T13:09:09", "2014-01-20"
) %>%
  mutate(
    TRTSDTM = as_datetime(TRTSDTM),
    ASTDTM = as_datetime(ASTDTM),
    AENDT = ymd(AENDT)
  )

head(datain)

'''
> head(datain)
# A tibble: 1 × 3
  TRTSDTM             ASTDTM              AENDT
  <dttm>              <dttm>              <date>
1 2014-01-17 23:59:59 2014-01-18 13:09:09 2014-01-20
'''

# 相対日を追加
# source_varsに期間の後端を示す変数を格納する。
# HOGEDY = TRTSDTMのようにすると、出力する期間の変数名を指定できる
# 変数名のみ指定すると期間の変数名はxxDYの形に指定される
# (個人的にはプログラム中に出力変数名が出てこないのは嫌なので変数名は指定したい)
hoge <- derive_vars_dy(
  datain,
  reference_date = TRTSDTM,
  source_vars = exprs(HOGEDY = TRTSDTM, ASTDTM, AENDT)
)

head(hoge)
'''
> head(hoge)
# A tibble: 1 × 6
  TRTSDTM             ASTDTM              AENDT      HOGEDY ASTDY AENDY
  <dttm>              <dttm>              <date>      <dbl> <dbl> <dbl>
1 2014-01-17 23:59:59 2014-01-18 13:09:09 2014-01-20      1     2     4
'''

2025年5月1日木曜日

指定したフォルダに格納されているファイルの、ファイル名と更新日時を取得する

vbscriptが廃止になると聞いて…(略。powershell5で指定したフォルダの直下にあるファイルのファイル名と最終更新日を取得するものを作成した。powershellなので実行には癖があるため実行用exeを作っても良い。こいつまたpowershellで変なもの作ってるな。

変数_ipathに対象のフォルダを指定して実行すればok。powershell本体のps1ファイルがある場所に結果となるoutput.txtが保存され、そのtxtを自動で開く形にしている。ps1ファイルの場所を自動で取得する$PSScriptRoot変数は結構便利。ちなみにソース中の#はコメントだ。

今回のは指定フォルダ内のサブフォルダは取得対象外にしているが、サブフォルダも含めたい場合はget-childitemに-recurseオプションを付ければよい。また最終更新日はこのままだと時分まで取得するので、日付までで良い場合はToStringの箇所をyyyy/mm/ddとする。

オサレポイントは出力時に`tと、ファイル名と日時をタブ区切りにしているところか。丸ごとエクセルにコピペするとファイル名セルと日時セルに分かれるのでちょっと便利。そのほかにもGet-childitemはいろいろ取得できるので必要に応じて修正できる。

最後は取得したGet-childitemをパイプでout-fileで外部ファイルのoutput.txtに出力している。パイプは便利だが結構何が送られているのかを見逃しがちになるので使いどころには気を付けたい。と言いつつもGet-childitemで取得したフォルダの中身をパイプでForEach-Objectに送って全件ループ、みたいな書き方が結構便利。わざわざforでループ用変数作って…みたいなことをしなくて良いのが助かる。


以下ソースコード

$_ipath = ""

$_opath = $PSScriptRoot

Write-Host "$_ipath 直下のファイルの更新日時を取得します"


# ファイルの更新日時を取得してテキストの書き込み

# サブフォルダの中身を含めて取得するにはget-childitemに-recurseを付ける

# 更新日時が日付までで良い場合はToString以下をyyyy/mm/ddにする


Get-childitem -Path $_ipath -File | ForEach-Object {

    $format_date = $_.LastWriteTime.ToString("yyyy/MM/dd HH:mm")

    "$($_.Name)`t$format_date" 

} | Out-File -filepath $_opath\output.txt


#結果ファイルを開く

invoke-item $_opath\output.txt


Write-Host "end"

2025年4月1日火曜日

複数の変数に同じ処理をかける時は配列が使えるの話

 例えばA、B、Cの3つの変数に同じ処理…今回はそれぞれに+1する…を実行する時、arrayが使えることもある。同じ処理をピコピコと繰り返し書くのが面倒な時は配列に記載してやればいっぱい似たようなものを書かなくて良い。具体例は以下の通り。

配列hogeを定義して、do overで全部にdo以下の処理を当てはめている。dimで要素数取って、とかをやっても良いのだが、今の場合のように全部に処理をすることが明らかな場合はわざわざdimで要素数を取らなくても良い。

data _null_ ;

    a = 1 ;

    b = 2 ;

    c = 3 ;

    array hoge a b c ;

    do over hoge ;

        hoge = hoge + 1 ;

    end ;

    putlog a b c ;

run ;

2025年3月1日土曜日

rtfの表に上付きの🄬を出力する

rtfの表内に上付き文字を出したいときはインラインフォーマットで^{super A}とすると上付き文字のAが出せますし、ギリシャ文字なんかを出すときは^{unicode 03B2}で出したりします。

環境依存文字の🄬も似たように^{unicode 24C7}でrtf上に出せますが、上付きの🄬…ユニコード指定の文字を上付き…で出すには工夫が要るのか…?という話です。特段の工夫とかは要らず、hoge^{super ^{unicode 24C7}}とやるとhogeの右肩に🄬を出すことができます。便利ですね。


インラインフォーマット指定前の^はods escapecharacterで指定しています。試してないのですが、defaultの(*esc*)だと重ねれない気がするので何かしらの文字をエスケープ文字として設定してください