Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@
^\.jules(/.*)?$
^\.trivyignore\.yaml$
^trivy\.yaml$
^\.semgrepignore$
^test_dummy\.R$
^test_validation\.R$
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) μ˜€λ²„ν—€λ“œλ₯Ό λ°©μ§€ν•΄μ•Ό ν•©λ‹ˆλ‹€.
## 2025-02-12 - R μ–Έμ–΄μ—μ„œ 데이터 ν”„λ ˆμž„ 2차원 μ„œλΈŒμ…‹νŒ… μ˜€λ²„ν—€λ“œ μ΅œμ ν™”
**Learning:** Rμ—μ„œ νŠΉμ • 쑰건을 λ§Œμ‘±ν•˜λŠ” 데이터 ν”„λ ˆμž„μ˜ ν•­λͺ©μ„ μ—…λ°μ΄νŠΈν•  λ•Œ, 2차원 μ„œλΈŒμ…‹νŒ… 방식(`df[idx, "col"] <- val`)을 μ‚¬μš©ν•˜λ©΄ 맀번 `[<-.data.frame` λ©”μ†Œλ“œ λ””μŠ€νŒ¨μΉ˜κ°€ λ°œμƒν•˜μ—¬ 차원 검사 및 데이터 ν”„λ ˆμž„μ˜ κΉŠμ€ 볡사가 μˆ˜λ°˜λ˜μ–΄ O(N) μ΄μƒμ˜ μ‹¬κ°ν•œ μ˜€λ²„ν—€λ“œκ°€ λ°œμƒν•©λ‹ˆλ‹€.
**Action:** 직접 벑터 ν• λ‹Ή 방식(`df$col[idx] <- val`)으둜 λ³€κ²½ν•˜μ—¬ λ©”μ†Œλ“œ λ””μŠ€νŒ¨μΉ˜ 및 데이터 ν”„λ ˆμž„ 볡사λ₯Ό ν”Όν•˜κ³  O(1)에 κ°€κΉŒμš΄ C-레벨 벑터 μˆ˜μ • μ†λ„λ‘œ μ„±λŠ₯을 크게 μ΅œμ ν™”ν•΄μ•Ό ν•©λ‹ˆλ‹€.
Comment on lines +19 to +21

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ“ Maintainability & Code Quality | 🟑 Minor | ⚑ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- target file ---'
cat -n .jules/bolt.md | sed -n '1,45p'
printf '%s\n' '--- R availability ---'
command -v R || true
command -v Rscript || true
printf '%s\n' '--- related assignment examples ---'
rg -n -C 3 '\[<-\]|\\$[A-Za-z_][A-Za-z0-9_]*\\[|2차원|μ„œλΈŒμ…‹|copy|볡사|O\\(1\\)|O\\(N\\)' . --glob '*.R' --glob '*.r' --glob '*.md' --glob '!node_modules' | head -250

Repository: ContextualWisdomLab/aFIPC

Length of output: 3527


🌐 Web query:

R language definition replacement function df$col[idx] <- value temporary copy-on-modify data.frame [<-.data.frame semantics

πŸ’‘ Result:

In R, assignment operations using replacement functionsβ€”such as df$col[idx] <- valueβ€”adhere to copy-on-modify semantics, which are driven by R's internal memory management to maintain the illusion of pass-by-value [1][2]. When you execute an expression like df$col[idx] <- value, R internally transforms it into a function call [3]. Conceptually, the evaluation follows this pattern [3]: tmp <- df df <- "[<-.data.frame"(tmp, i, j, value) rm(tmp) The core mechanisms governing this behavior are: 1. Copy-on-Modify (NAMED mechanism): R objects possess a NAMED field (0, 1, or 2) that tracks how many symbols are bound to the underlying data [2]. A value of 2 indicates that the object is shared and must be duplicated before modification to prevent side effects on other references [2][4]. Replacement functions for data frames frequently trigger this duplication because they are implemented as standard functions that increment reference counts [1][3]. 2. Data Frame Complexity: Data frames are lists of vectors [1]. Because modifying a data frame involves calling a generic replacement method (e.g., [<-.data.frame), multiple intermediate copies may be created during the function call process [1][3]. While R attempts to perform modifications in place whenever it can determine that an object is not shared (NAMED=0), data frame structure often necessitates copies [1][4]. For example, modifying a row typically requires copying every column in the data frame [1]. 3. Implementation Details: Methods like [[<-.data.frame and [<-.data.frame are responsible for the logic that ensures the data frame structure remains valid after assignment [5]. These methods check for the correct number of rows and handle type consistency [5]. Because they are standard R functions, they operate within the call-by-value paradigm, meaning the entire data frame object (or at least significant portions of it) is often duplicated to ensure that the original object remains unchanged if the operation were to fail or if other variables still reference the old data [1][3]. While modern versions of R (and the underlying ALTREP system) have introduced optimizations to reduce unnecessary copying in certain specific contexts, subassignment in data frames generally remains a high-overhead operation compared to modifications in environments or other specialized data structures [4][6].

Citations:


🌐 Web query:

R source [<-.data.frame $<-.data.frame replacement function copy-on-modify indexed assignment complexity

πŸ’‘ Result:

In R, data.frame replacement operationsβ€”such as [<-.data.frame, [[<-.data.frame, and $<-.data.frameβ€”are governed by R's copy-on-modify semantics [1][2][3]. Because data frames are S3 objects (specifically, lists with a class attribute and row names), these replacement methods are regular functions rather than primitives [4][5][6]. Key aspects of their behavior include: 1. Copy-on-Modify and Reference Counting: R objects track a reference count (the NAMED value) [2][3]. When you perform an assignment like df[i, j] <- value, R checks the reference count [3]. If the data frame is shared (i.e., referenced by other variables), R must copy it to preserve the copy-on-modify principle and avoid unintended side effects on other bindings [1][3]. 2. Method Implementation and Duplication: Unlike primitive replacement functions (e.g., for simple lists or vectors) that may optimize modify-in-place operations, [<-.data.frame and its counterparts are implemented in R code [5]. The standard R evaluator handles replacement expressions (e.g., x$a <- val) by transforming them into a call to the replacement function, often introducing intermediate copies (the tmp mechanism) before the function even executes [4][7]. Consequently, these methods often result in multiple duplications [1][4][6]. 3. Complexity and Efficiency: - Operations on a data frame typically have a complexity related to the size of the structure. For example, modifying a row requires copying every column [1]. - Because [<-.data.frame and [[<-.data.frame are non-primitive, they consistently increment the reference count of the input object [4][6]. This ensures that even when a data frame is not explicitly shared by another variable, the function's own reference to the input often forces a copy [4]. - Benchmarks frequently show that using $ or [[ for single-column modification can be more efficient than [ for row/matrix replacement, though all are significantly slower than in-place list modifications [5]. In summary, the observed performance cost is primarily due to the non-primitive nature of these S3 replacement methods, which triggers multiple duplications to ensure memory safety and maintain R's functional programming guarantees [1][4][6][7].

Citations:


🏁 Script executed:

#!/bin/bash
set -eu
tmpdir="$(mktemp -d)"
trap 'rm -rf "$tmpdir"' EXIT

curl -fsSL \
  https://raw.githubusercontent.com/wch/r-source/trunk/src/library/base/R/dataframe.R \
  -o "$tmpdir/dataframe.R"
curl -fsSL \
  https://cran.r-project.org/doc/manuals/r-release/R-lang.html \
  -o "$tmpdir/R-lang.html"

printf '%s\n' '--- data.frame replacement methods ---'
rg -n -A90 -B8 '^\`?\[<-\.`?data\.frame|^\`?\$<-\.`?data\.frame|^\`?\[\[<-\.`?data\.frame' \
  "$tmpdir/dataframe.R" | head -260

printf '%s\n' '--- replacement-function evaluation rules ---'
rg -n -i -A12 -B8 'replacement function|complex assignment|subassignment|left-hand side' \
  "$tmpdir/R-lang.html" | head -220

Repository: ContextualWisdomLab/aFIPC

Length of output: 21503


볡사 λΉ„μš©κ³Ό λ³΅μž‘λ„λ₯Ό λ‹¨μ •ν•˜μ§€ λ§ˆμ‹­μ‹œμ˜€.

df$col[idx] <- val도 idx 처리 λΉ„μš©μ΄ λ°œμƒν•©λ‹ˆλ‹€. λ˜ν•œ $<-.data.frame은 데이터 ν”„λ ˆμž„ 객체λ₯Ό 볡사할 수 있으며, copy-on-modify에 따라 μ—΄ 벑터도 볡사될 수 μžˆμŠ΅λ‹ˆλ‹€. λ”°λΌμ„œ 항상 O(1)에 가깝고 데이터 ν”„λ ˆμž„ 볡사λ₯Ό ν”Όν•œλ‹€κ³  λ¬Έμ„œν™”ν•˜μ§€ λ§ˆμ‹­μ‹œμ˜€. λŒ€ν‘œ μž…λ ₯의 μ‹œκ°„κ³Ό λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰μ„ μΈ‘μ •ν•œ 결과만 κΈ°μˆ ν•˜κ³ , μΈ‘μ • κ²°κ³Όκ°€ μ—†μœΌλ©΄ 데이터 ν”„λ ˆμž„ μˆ˜μ€€μ˜ μ„œλΈŒμ…‹ ν• λ‹Ή μ˜€λ²„ν—€λ“œλ₯Ό 쀄일 수 μžˆλ‹€κ³  μ™„ν™”ν•˜μ‹­μ‹œμ˜€.

πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.jules/bolt.md around lines 19 - 21, Update the optimization note in the R
data-frame subsetting section to avoid asserting that direct vector assignment
is always near O(1) or prevents data-frame and column copies. State only
measured time and memory results for representative inputs when available;
otherwise use qualified wording that it can reduce data-frame-level subsetting
overhead, while acknowledging index processing and copy-on-modify behavior.

40 changes: 20 additions & 20 deletions R/aFIPC.R
Original file line number Diff line number Diff line change
Expand Up @@ -598,15 +598,15 @@ autoFIPC <-
# Preserve mirt's structural estimability flags. Forcing every row TRUE
# frees boundary parameters such as 2PL g/u and makes the Hessian unstable.

NewScaleParms[NewScaleParms$item == 'GROUP', "est"] <- FALSE
OldScaleParms[OldScaleParms$item == 'GROUP', "est"] <- FALSE
NewScaleParms$est[NewScaleParms$item == 'GROUP'] <- FALSE
OldScaleParms$est[OldScaleParms$item == 'GROUP'] <- FALSE

NewScaleParms[NewScaleParms$name == "COV_11", "est"] <- TRUE
OldScaleParms[OldScaleParms$name == "COV_11", "est"] <- TRUE
NewScaleParms$est[NewScaleParms$name == "COV_11"] <- TRUE
OldScaleParms$est[OldScaleParms$name == "COV_11"] <- TRUE

if (itemtype == 'Rasch') {
NewScaleParms[NewScaleParms$name == "a1", "est"] <- FALSE
OldScaleParms[OldScaleParms$name == "a1", "est"] <- FALSE
NewScaleParms$est[NewScaleParms$name == "a1"] <- FALSE
OldScaleParms$est[OldScaleParms$name == "a1"] <- FALSE
}

#IPD
Expand Down Expand Up @@ -789,11 +789,11 @@ autoFIPC <-
message(' Newform Parms: ', paste(NewScaleParms[newIdx, "value"], collapse = ' '))
message(' Oldform Parms: ', paste(OldScaleParms[oldIdx, "value"], collapse = ' '))

NewScaleParms[newIdx, "value"] <-
OldScaleParms[oldIdx, "value"]
NewScaleParms$value[newIdx] <-
OldScaleParms$value[oldIdx]
message(' Linkedform Parms: ', paste(NewScaleParms[newIdx, "value"], collapse = ' '), '\n')

NewScaleParms[newIdx, "est"] <-
NewScaleParms$est[newIdx] <-
FALSE
} else {
message(
Expand All @@ -813,9 +813,9 @@ autoFIPC <-
newBetaIdx <- NewScaleParms$item == 'BETA'
oldBetaIdx <- OldScaleParms$item == 'BETA'

NewScaleParms[newBetaIdx, "value"] <-
OldScaleParms[oldBetaIdx, "value"]
NewScaleParms[newBetaIdx, "est"] <-
NewScaleParms$value[newBetaIdx] <-
OldScaleParms$value[oldBetaIdx]
NewScaleParms$est[newBetaIdx] <-
FALSE

message('applying BETA parameter as linking')
Expand Down Expand Up @@ -858,13 +858,13 @@ autoFIPC <-
new_mean11_idx <- NewScaleParms$name == "MEAN_11"
old_mean11_idx <- OldScaleParms$name == "MEAN_11"

NewScaleParms[new_cov11_idx, "est"] <- FALSE
OldScaleParms[old_cov11_idx, "est"] <- FALSE
NewScaleParms[new_mean11_idx, "est"] <- FALSE
OldScaleParms[old_mean11_idx, "est"] <- FALSE
NewScaleParms$est[new_cov11_idx] <- FALSE
OldScaleParms$est[old_cov11_idx] <- FALSE
NewScaleParms$est[new_mean11_idx] <- FALSE
OldScaleParms$est[old_mean11_idx] <- FALSE

NewScaleParms[new_cov11_idx, "value"] <- 1
OldScaleParms[old_mean11_idx, "value"] <- 0
NewScaleParms$value[new_cov11_idx] <- 1
OldScaleParms$value[old_mean11_idx] <- 0
}
if (freeMEAN == T) {
LinkedModelSyntax <-
Expand All @@ -875,8 +875,8 @@ autoFIPC <-
'MEAN = F1'
))

NewScaleParms[NewScaleParms$name == "MEAN_1", "est"] <- TRUE
OldScaleParms[OldScaleParms$name == "MEAN_1", "est"] <- TRUE
NewScaleParms$est[NewScaleParms$name == "MEAN_1"] <- TRUE
OldScaleParms$est[OldScaleParms$name == "MEAN_1"] <- TRUE
} else {
LinkedModelSyntax <-
mirt::mirt.model(paste0(
Expand Down
Loading