Summary
On the censored VPC path, the data argument is silently ignored: the observed data is always taken from the fit, whatever the caller passes. The uncensored path honours it, so the two disagree.
Reproducer
od <- fit$origData
half <- od[od$ID %in% unique(od$ID)[1:4], ] # 4 of 12 subjects
# uncensored path: honoured
nrow(vpcPlot(fit, n = 5, vpcdb = TRUE)$obs) #> 132
nrow(vpcPlot(fit, data = half, n = 5, vpcdb = TRUE)$obs) #> 44
# censored path: ignored
nrow(vpcCens(fit, cens = TRUE, n = 5, vpcdb = TRUE)$obs) #> 132
nrow(vpcCens(fit, data = half, cens = TRUE, n = 5, vpcdb = TRUE)$obs) #> 132 <-- expected 44
Root cause
vpcPlot() sets up the observed data honouring data:
.obsLst <- .vpcUiSetupObservationData(fit, data = data, idv = idv, cens = cens)
.obs <- .obsLst$obs
but the censored branch then overwrites it unconditionally:
if (cens & !tidyvpc) {
...
.obs <- as.data.frame(fit) # <-- discards the `data` the caller passed
(R/vpcPlot.R, censored branch.)
Note this also discards the EVID/MDV filtering applied just above the branch. That part is currently harmless because as.data.frame(fit) already contains only observation rows, but it means the filtering and the data handling are both dead on this path.
Suggested fix
Use the .obs prepared by .vpcUiSetupObservationData() rather than re-deriving it from the fit, or at minimum error rather than silently ignoring data when cens = TRUE.
Care is needed: as.data.frame(fit) and origData do not carry the same DV for censored rows. With censMethod = "cdf" the fit's DV is imputed (max DV among censored rows was 5.47 in my check, vs 1 in origData), so swapping the source changes what the observed "fraction below LOQ" is computed from. That choice should be made deliberately.
Context
Pre-existing on main; found while reviewing #54, which fixes a different bug on this path and does not change this behaviour.
Summary
On the censored VPC path, the
dataargument is silently ignored: the observed data is always taken from the fit, whatever the caller passes. The uncensored path honours it, so the two disagree.Reproducer
Root cause
vpcPlot()sets up the observed data honouringdata:but the censored branch then overwrites it unconditionally:
(
R/vpcPlot.R, censored branch.)Note this also discards the
EVID/MDVfiltering applied just above the branch. That part is currently harmless becauseas.data.frame(fit)already contains only observation rows, but it means the filtering and thedatahandling are both dead on this path.Suggested fix
Use the
.obsprepared by.vpcUiSetupObservationData()rather than re-deriving it from the fit, or at minimum error rather than silently ignoringdatawhencens = TRUE.Care is needed:
as.data.frame(fit)andorigDatado not carry the sameDVfor censored rows. WithcensMethod = "cdf"the fit'sDVis imputed (maxDVamong censored rows was 5.47 in my check, vs 1 inorigData), so swapping the source changes what the observed "fraction below LOQ" is computed from. That choice should be made deliberately.Context
Pre-existing on
main; found while reviewing #54, which fixes a different bug on this path and does not change this behaviour.