From 8695a2ca69608c111aec97b09e6d48e96cc79189 Mon Sep 17 00:00:00 2001 From: seonghobae <8172694+seonghobae@users.noreply.github.com> Date: Fri, 28 Aug 2026 16:33:56 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20R=20dataframe=20?= =?UTF-8?q?subsetting=20column=20name=20extraction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit πŸ’‘ What: Changed `colnames(df[cols])` to directly use `cols` in `R/aFIPC.R`. 🎯 Why: `colnames(df[cols])` evaluates the subsetting `df[cols]`, which performs a complete memory allocation and deep copy of all columns in the subset just to extract their names. By directly returning the `cols` character array, we bypass this expensive O(N) memory allocation entirely. πŸ“Š Impact: Eliminates O(N) deep copying operations during setup and IRT model building stages. Reduces memory overhead significantly when processing large scale psychometric test data. πŸ”¬ Measurement: Verified mathematically via test_manual.R using `all.equal(colnames(df[cols]), cols)` which proved equivalent results, but the new version evaluates in O(1) time without dataset copying. Tested syntax loading smoothly in local tests. --- .jules/bolt.md | 3 +++ R/aFIPC.R | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index 7d3c603f..7421d508 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -16,3 +16,6 @@ ## 2025-02-12 - R μ–Έμ–΄μ—μ„œ 반볡적인 mirt λͺ¨λΈ 생성 μ‹œ λΆˆν•„μš”ν•œ λ°μ΄ν„°ν”„λ ˆμž„ λΆ€λΆ„μ§‘ν•© μΆ”μΆœ μ΅œμ ν™” **Learning:** Rμ—μ„œ λ°μ΄ν„°ν”„λ ˆμž„μ˜ νŠΉμ • 열을 μΆ”μΆœν•˜λŠ” μž‘μ—…(`df[cols]`)은 O(N)의 λ©”λͺ¨λ¦¬ 볡사λ₯Ό μˆ˜λ°˜ν•©λ‹ˆλ‹€. `autoFIPC`μ—μ„œ `mirt` λͺ¨λΈμ˜ νŒŒλΌλ―Έν„°λ₯Ό μ„€μ •ν•˜κ±°λ‚˜ ν˜ΈμΆœν•˜λŠ” κ³Όμ • 쀑에 `newformXDataK[colnames(newFormModel@Data$data)]` μ½”λ“œκ°€ λ°˜λ³΅ν•΄μ„œ μ‚¬μš©λ˜μ—ˆκ³ , 심지어 `ncol()`을 μœ„ν•΄ λ‹¨μˆœνžˆ 개수λ₯Ό ꡬ할 λ•Œλ„ μ‚¬μš©λ˜μ–΄ λΆˆν•„μš”ν•œ λ©”λͺ¨λ¦¬ ν• λ‹Ήκ³Ό μ˜€λ²„ν—€λ“œλ₯Ό μ΄ˆλž˜ν–ˆμŠ΅λ‹ˆλ‹€. **Action:** μ‘°κ±΄λ¬Έμ΄λ‚˜ 반볡문 λ‚΄λΆ€μ—μ„œ λΆˆν•„μš”ν•˜κ²Œ λ°μ΄ν„°ν”„λ ˆμž„ λΆ€λΆ„μ§‘ν•© 연산이 λ°˜λ³΅λ˜μ§€ μ•Šλ„λ‘ μ™ΈλΆ€μ—μ„œ ν•œ 번만 `linkedFormData <- newformXDataK[colnames(newFormModel@Data$data)]`둜 캐싱(caching)ν•œ λ’€, `ncol(linkedFormData)`와 `data = linkedFormData` ν˜•νƒœλ‘œ μž¬μ‚¬μš©ν•˜μ—¬ λ©”λͺ¨λ¦¬ 볡사와 O(N) μ˜€λ²„ν—€λ“œλ₯Ό λ°©μ§€ν•΄μ•Ό ν•©λ‹ˆλ‹€. +## 2024-08-28 - R μ–Έμ–΄μ—μ„œ λ°μ΄ν„°ν”„λ ˆμž„ μ„œλΈŒμ…‹νŒ… 컬럼λͺ… μΆ”μΆœ μ‹œ O(N) 볡사 μ˜€λ²„ν—€λ“œ μ΅œμ ν™” +**Learning:** Rμ—μ„œ λ°μ΄ν„°ν”„λ ˆμž„μ˜ μ„œλΈŒμ…‹μ— λŒ€ν•΄ 컬럼λͺ…을 κ°€μ Έμ˜¬ λ•Œ `colnames(df[cols])`λ₯Ό μ‚¬μš©ν•˜λ©΄, 원본 λ°μ΄ν„°ν”„λ ˆμž„μ„ μ‹€μ œλ‘œ μ„œλΈŒμ…‹νŒ…ν•˜λŠ” κ³Όμ •μ—μ„œ O(N)의 데이터 볡사와 λ©”λͺ¨λ¦¬ 할당이 λ°œμƒν•©λ‹ˆλ‹€. μ΄λŠ” λ‹¨μˆœνžˆ 컬럼λͺ…을 κ°€μ Έμ˜€λŠ” λͺ©μ μ— λΉ„ν•΄ μ‹¬κ°ν•œ λΉ„νš¨μœ¨μ„ μ΄ˆλž˜ν•©λ‹ˆλ‹€. +**Action:** `colnames(df[cols])`의 κ²°κ³ΌλŠ” 항상 `cols` 벑터 μžμ‹ κ³Ό λ™μΌν•˜λ―€λ‘œ, λ°μ΄ν„°ν”„λ ˆμž„μ„ μ„œλΈŒμ…‹νŒ…ν•˜μ§€ 말고 이미 κ°€μ§€κ³  μžˆλŠ” `cols` 벑터 자체λ₯Ό 직접 μ‚¬μš©ν•˜μ—¬ μ„±λŠ₯ μ €ν•˜λ₯Ό λ°©μ§€ν•΄μ•Ό ν•©λ‹ˆλ‹€. diff --git a/R/aFIPC.R b/R/aFIPC.R index 62546519..fc1524a4 100644 --- a/R/aFIPC.R +++ b/R/aFIPC.R @@ -620,8 +620,8 @@ autoFIPC <- IPDItemCount <- 0 # IPD target item checking - newFormColNames <- colnames(newformXDataK[colnames(newFormModel@Data$data)]) - oldFormColNames <- colnames(oldformYDataK[colnames(oldFormModel@Data$data)]) + newFormColNames <- colnames(newFormModel@Data$data) + oldFormColNames <- colnames(oldFormModel@Data$data) # ⚑ Bolt: Vectorized match() to avoid dynamic array growth overhead inside a for loop idxNew <- match(newformCommonItemNames, newFormColNames) @@ -749,8 +749,8 @@ autoFIPC <- } } - newFormColNames <- colnames(newformXDataK[colnames(newFormModel@Data$data)]) - oldFormColNames <- colnames(oldformYDataK[colnames(oldFormModel@Data$data)]) + newFormColNames <- colnames(newFormModel@Data$data) + oldFormColNames <- colnames(oldFormModel@Data$data) # ⚑ Bolt: Cache parameter indices to avoid O(N) linear search inside loop newScaleParmsItemIdxCache <- split(seq_len(nrow(NewScaleParms)), NewScaleParms$item) @@ -848,7 +848,7 @@ autoFIPC <- message('\nestimating Linked Form Eq(X) parameters') # ⚑ Bolt: Cache subsetted dataframe to avoid repeated O(N) memory copies during mirt model setup - linkedFormData <- newformXDataK[colnames(newFormModel@Data$data)] + linkedFormData <- newformXDataK[newFormColNames] if (forceNormalZeroOne) { freeMEAN <- F