Skip to content

[BUG] Error in the simulation outlier step #11

Open

Description

@jgarces02

Minimal reproducible example

library(SelectSim)

> str(m_selectsim)
List of 4
 $ M               :List of 2
  ..$ Nonsense: num [1:15236, 1:884] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..- attr(*, "dimnames")=List of 2
  .. .. ..$ : chr [1:15236] "A1BG" "A1CF" "A2M" "A2ML1" ...
  .. .. ..$ : chr [1:884] "0BM059818SC002" "0BM065793SC002" "0BM073778SC001" "10" ...
  ..$ Missense: num [1:15236, 1:884] 0 0 0 0 0 0 0 0 0 0 ...
  .. ..- attr(*, "dimnames")=List of 2
  .. .. ..$ : chr [1:15236] "A1BG" "A1CF" "A2M" "A2ML1" ...
  .. .. ..$ : chr [1:884] "0BM059818SC002" "0BM065793SC002" "0BM073778SC001" "10" ...
 $ tmb             :List of 2
  ..$ Nonsense:'data.frame':	775 obs. of  2 variables:
  .. ..$ sample  : Factor w/ 884 levels "0BM059818SC002",..: 1 2 3 6 8 9 11 13 14 15 ...
  .. ..$ mutation: int [1:775] 13 2 2 1 5 1 2 10 9 2 ...
  ..$ Missense:'data.frame':	75 obs. of  2 variables:
  .. ..$ sample  : Factor w/ 884 levels "0BM059818SC002",..: 10 14 63 64 66 70 74 81 82 90 ...
  .. ..$ mutation: int [1:75] 1 1 1 2 2 1 1 1 1 1 ...
 $ sample.class    : Factor w/ 2 levels "BM","PCT": 1 1 1 1 1 1 1 1 1 1 ...
  ..- attr(*, "names")= chr [1:884] "0BM059818SC002" "0BM065793SC002" "0BM073778SC001" "10" ...
 $ alteration.class: Named chr [1:15236] "mut" "mut" "mut" "mut" ...
  ..- attr(*, "names")= chr [1:15236] "PALMD" "FRRS1" "AGL" "HIAT1" ...

> aux <- SelectSim::selectX(
+   M = m_selectsim, min.freq = 2,
+   sample.class = m_selectsim$sample.class, 
+   alteration.class = m_selectsim$alteration.class)
[1] "#### Creating SelectSim object ####"
[1] "Step1-> Parsing and Filtering GAM..."
[1] "Number of features: 15236"
[1] "Number of features after filtering: 190"
[1] "-> Alteration Landscape object created"
[1] "Step2-> Generating Template object..."
[1] "-> Template object created"
[1] "Step3-> Generating sample weight matrix..."
[1] "-> Weight Matrix created"
[1] "Step4-> Generating null model..."
[1] "-> Removing the outliers matrix from null model..."
Error in snMut[, i] <- colSums(obj$null[[i]]) : 
  replacement has length zero
In addition: 
Warning messages:
1: In al$tmb[["total"]] + am$tmb[[i]][, c("mutation")] :
  longer object length is not a multiple of shorter object length
2: In al$tmb[["total"]] + am$tmb[[i]][, c("mutation")] :
  longer object length is not a multiple of shorter object length

Environment

  • SelectSim version: 0.1.6
  • R version: 4.5.3 (2026-03-11)
  • OS: macOS Tahoe 26.6.2
  • Installation method: devtools::install_github("CSOgroup/SelectSim")

Activity

  1. self-assigned this
    on Sep 1, 2026
  2. changed the title [-][BUG][/-] [+][BUG] Error in the simulation outlier step[/+] on Sep 1, 2026
  3. added theissue type on Sep 1, 2026
  4. Arvindiyer commented on Sep 1, 2026

    @Arvindiyer
    Collaborator

    @jgarces02

    Thanks for the detailed report — the full str() dump plus the exact error and warning text made this easy to track down. I reproduced it with a synthetic dataset shaped like yours, and it turns out there are two separate bugs bundled together here:

    1. The crash itself comes from sample.class being a factor (Factor w/ 2 levels "BM", "PCT"). Internally, get.blocks() builds per-block sample lists using which(sample.class == x), and R silently drops the names attribute when you do this comparison on a named factor (it keeps the names fine for a named character vector — that inconsistency is the actual bug). Once the names are gone, the code that slices the mutation/weight matrices by sample name ends up indexing with NULL, which produces empty matrices a few steps downstream, and that's what finally surfaces as replacement has length zero in retrieveOutliers(). I confirmed this by running your workflow on synthetic data with sample.class as a factor (crashes identically) vs. as a character vector (runs to completion) with nothing else changed.

    2. Your two warnings are a separate issue: it's a row-count mismatch. Nonsense/Missense in your tmb list only include samples with ≥1 mutation of that type (775 and 75 rows respectively, out of 884 samples total), but the code that builds the total TMB per sample just does plain vector addition assuming every tmb table already has one row per sample in matching order. When the lengths don't match, R recycles instead of erroring, so it adds positionally rather than matching by sample. That's the "longer object length is not a multiple of shorter object length" warning. This one doesn't crash, but it does silently produce incorrect per-sample TMB totals and sample weights, so it's worth fixing even though it's not what threw your error.

    Workaround for now, no package changes needed on your end — just two small tweaks to m_selectsim before calling selectX():

    # 1. Convert sample.class from factor to character, keeping the sample names
    m_selectsim$sample.class <- setNames(as.character(m_selectsim$sample.class),
                                          names(m_selectsim$sample.class))
    
    # 2. Give each tmb table one row per sample (0 where a sample has none of
    #    that mutation type), matching the column order of M
    all_samples <- colnames(m_selectsim$M[[1]])
    for (nm in names(m_selectsim$tmb)) {
      tb <- m_selectsim$tmb[[nm]]
      full <- data.frame(sample = all_samples,
                          mutation = 0L,
                          row.names = all_samples)
      full[as.character(tb$sample), "mutation"] <- tb$mutation
      m_selectsim$tmb[[nm]] <- full
    }
    
    aux <- SelectSim::selectX(
      M = m_selectsim, min.freq = 2,
      sample.class = m_selectsim$sample.class,
      alteration.class = m_selectsim$alteration.class)

    That should let you run selectX() on your data today without hitting either issue. Let me know if it doesn't behave as expected on your full dataset.

    I've tracked down the root cause in get.blocks() and new.AL.general() and I will be putting a patch together for both.

    I'm happy to help 👍🏽. Thanks again for the clear write-up; it made this a quick diagnosis.

  5. Arvindiyer commented on Sep 1, 2026

    @Arvindiyer
    Collaborator

    TODO (tracking checklist to close it)

    ### Fix tracking
    
    - [X] Fix `get.blocks()` (`R/selectX_create.r`) so a `factor` `sample.class`/`alteration.class` doesn't silently lose sample names via `which(sample.class == x)` (coerce to `character`, or use a name-safe split).
    - [X] Fix `new.AL.general()` (`R/selectX_create.r`) so `al$tmb$total` is computed by matching each `tmb` table to samples **by name**, not by raw vector position/recycling.
    - [X] Add validation in `selectX()` / `new.AL.general()` that errors clearly (instead of warning + silently corrupting/crashing) when:
      - [X] `sample.class` or `alteration.class` is a factor
      - [X] any `tmb` table doesn't have exactly one row per sample matching `colnames(M[[i]])`
    - [X] Add regression tests covering both cases above (factor `sample.class`; partial/sparse `tmb` tables)
    - [X] Update docs (`@param` in `selectX_run.R`) to state the required shape for `sample.class`, `alteration.class`, and `tmb`
    - [X] Verify fix against the reporter's original reproduction (factor `sample.class` + sparse `tmb`) — should run cleanly or fail with a clear error
  6. jgarces02 commented on Sep 1, 2026

    @jgarces02
    Author

    Nice. All checked the factor type in all tables except in that variable 😓. Now it works perfectly.
    And good catch with the different sizes, I didn't pay too much attention while the error was happening.
    Thanks a lot for your help, @Arvindiyer!

    (I won't close it as I see you're still doing things in regard this issue)

  7. Arvindiyer commented on Sep 1, 2026

    @Arvindiyer
    Collaborator

    @jgarces02 Thanks again for bringing this to my attention. I’ll work on it and fix the bug. Glad to hear the suggested workaround worked well 👍🏽😊

    No need to close the issue on your end—I’ll take care of that once the fix is in. And if anything else comes up, feel free to open another issue anytime. Happy to help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions