Skip to content

Release 0.1.1 - #3

Merged
jaydu1 merged 16 commits into
mainfrom
release-0.1.1
Sep 30, 2026
Merged

jaydu1 merged 16 commits into
mainfrom
release-0.1.1

Conversation

@jaydu1

@jaydu1 jaydu1 commented Sep 30, 2026

Copy link
Copy Markdown
Member

No description provided.

jaydu1 and others added 16 commits September 25, 2026 22:55
LFC excluded every gene whose two arm means differed by less than
thres_diff before the BH correction. That filter selects on the effect
estimate: the pairs it removes are the ones with p-values near 1, so BH
over the remaining pairs was anti-conservative. On a Perturb-seq
negative control (fake perturbations drawn from controls) it removed
about 10% of the expressed pairs; keeping them flattened the null
p-value histogram and lowered the null SD of the statistics from 1.09
to 1.04. thres_diff is now only the floor before the logarithm. The
expression threshold (thres_min / min_counts) is unchanged: it looks
at expression level, not at the effect.

Docstrings and the LFC guide drop version-history prose and references
to an analysis that is not in the repository; the versionchanged
directives keep the history.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
3_run_batch.py no longer asserts version 0.1.0 and writes the nuisance
file that the propensity refit reads. The README lists the refit script
and which results file the notebook reads.

A local analysis folder that .gitignore already excluded was still
tracked; it is removed from the repository.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
comp_size_factor's median of ratios ran over every gene. In sparse
single-cell data most nonzero counts are 1 or 2, so those ratios caught
about a third of each cell's sequencing depth (SD of log size factors 0.12
against 0.35 for log total counts on Replogle controls). The leftover
depth sits in highly expressed genes, which scale fully with depth, and
when a group of cells differs from the controls in average depth by
chance, all of them shift together.

On a Replogle negative control (30 fake perturbations cut from the
controls, full pipeline) each fake arm's shift tracked its leftover depth
(correlation 0.98), the null SD for genes above 5 counts per cell was
1.32 and 114 pairs were called; Wilcoxon on the same genes called 2.
Swapping in total-count offsets removed the shift, while cross-fitting
and a Poisson outcome model changed nothing, which rules out the
in-sample fit and dispersion.

The median of ratios now uses genes with mean count >= min_mean (2), or
the 100 highest-expressed genes if fewer pass. That keeps its resistance
to composition changes, which total counts lack and which the GCATE
deconfounding tests depend on, while tracking depth: 14 false
discoveries on the same null, SD 1.08 for highly expressed genes, and no
change on the mouse-brain negative control. min_mean=0 restores the old
factors; method='libsize' uses total counts. Cells without counts now get
a size factor of zero instead of NaN.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Guides are assigned at random, so the propensity model only has to absorb
chance imbalance. Latent factors estimated from expression can instead
track a perturbation's own effect; the model then partly separates that
arm from the controls and the weights pile onto the treated cells that
look least affected.

select_propensity_factors, next to tune_penalty_factor and with the same
interface, starts from every covariate. For each treatment whose treated
ESS falls below 0.5, overlap below 0.3 or AUC above 0.9, it drops the
covariate most imbalanced between that treatment and the controls, one
at a time, until ESS and overlap recover; the intercept always stays. It
returns a drop_by_treatment mapping for refit_propensity_scores and a
per-arm report.

The Replogle and Perturb-seq tutorials leave log library size out of the
propensity model and choose the latent factors per arm with it: knocking
down transcription machinery shrinks total RNA, so library size separated
those arms from the controls almost completely. 4_refit_propensity.py
applies the rule to every Replogle batch (29 of 200 arms adjusted, one or
two factors dropped each); 4_cache_propensity_batch.py, which only served
the penalty sweep, is removed and the refit script takes its number.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fit_gcate took its offset from kwargs_glm['size_factor'], which was only
set for offset=True. A supplied offset array was therefore replaced by
ones everywhere except the dispersion estimate. The array is now used,
and it is checked for one finite value per row. offset=False still means
no offset.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
usevar='unequal' is the by-arm Welch variance again, with
Welch-Satterthwaite df. 'pooled' stays the default. In small case-control
designs Welch is the more conservative choice: on 85 SEA-AD donors,
permuted labels gave 0-1 false discoveries with it, against up to 3,654
under 'pooled'. The gcate docs note that with few units the factors can
stay nearly orthogonal to treatment, while under 'pooled' they still
shrink the standard errors. The SEA-AD tutorial README no longer lists
results/sea_ad_lfc.csv as a notebook input.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only results/replogle-r.csv, the JIC table on raw counts, is kept. The
older table computed on log-normalised values is removed, with its
mention in 2_estimate_r.py and the README; .gitignore no longer
exempts it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The 50 GB nuisance file only feeds the propensity walk-through, which the
notebook skips when the file is absent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The R tutorial was last rendered with 0.0.10. It still kept library size in
the propensity model and tuned a penalty on it. It now follows the Python
notebook: library size is left out, factors are selected per arm with
select_propensity_factors, then LFC runs with the selected scores. It is
re-knit with 0.1.1 and gives 8,161 hits, as the Python notebook does.

- The R discovery count no longer includes rows whose padj is NA.
- The unreferenced perturbseq_files/ figure is removed.
- LFC shows its per-treatment progress bar only with verbose=True, so the
  tutorial outputs no longer carry hundreds of progress lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Library size can confound (capture depth, cell quality) or reflect a
treatment effect (total RNA), and the data cannot tell which. The LFC docs
now say this in one place, with a rule of thumb: keep library size when
every arm stays supported with it, and leave it out when arms separate.
tune_penalty_factor is the middle ground.

The Perturb-seq, Replogle and SEA-AD tutorials use the same wording and
point to the docs, instead of calling library size a pure treatment
effect. Only markdown changed; the results are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With latent factors in the outcome model, crispyx 0.1.5's structured GLM
fitter was about 2.5 times slower than 0.1.6's, for the same estimates:
159 s against 62 s for LFC on 5,000 cells, 3,000 genes and 10 treatments
(mouse-brain subsample). The full test suite passes with 0.1.6.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An Intel (x86_64) Python on an Apple Silicon Mac runs translated by Rosetta
2, which makes causarray's numerical code several times slower. On a
5,000-cell subsample LFC took 159 s translated against 32-57 s natively,
and the test suite 270 s against 64 s. pip installs into whatever Python
it is given, so packaging cannot prevent this. Instead, `import causarray`
now issues one RuntimeWarning that explains how to create a native
environment, and the README says how to check for one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The causarray and causarray-r environments must share an architecture, since
reticulate cannot load an arm64 Python into an Intel R. The README now says
to create both natively on Apple Silicon. environment-r.yaml lists dplyr and
ggplot2, which the R tutorial loads directly.

The R tutorial is re-knit with native R 4.4 and Python. Results are
unchanged (8,161 hits; U9 and U7 dropped for Satb2). Seurat now loads
quietly, and the newer pandoc changes heading style only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
L45Glut-py.ipynb is a causarray 0.1.1 tutorial on one cell type of a
mouse-brain CRISPR screen (L4-5 IT Glut, 58 perturbations). It covers the
per-cell-type pipeline, the propensity and calibration checks, the
biological readouts and a comparison with Wilcoxon, and ends with the loop
for all cell types. It is rendered in the docs as an orphan page, not
listed in the index. pipeline.py, which the notebook imports, and the
folder README are committed with it. The data, the results, the archived
investigation notebook and its scripts stay local.

.gitignore now ignores prepared SEA-AD data other than the committed
excitatory-neuron pseudo-bulk, such as the subclass pseudo-bulks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- comp_size_factor: a cell with no counts in the well-expressed genes fell
  back to ratios against all-gene geometric means, which sit near one for
  sparse genes, so an almost-empty cell got a size factor near the median
  (0.91 for a cell with 0.6% of the median library). It now gets its total
  count, rescaled by the other cells' median ratio of size factor to total.
  method='scale' returns one factor per row instead of one per column. The
  unreachable old implementation and two unused helpers are removed.
- select_propensity_factors: a triggered treatment that already meets the
  target keeps every covariate; on_infeasible ('best', 'all', 'none')
  decides what an arm that never reaches the target keeps, instead of
  always dropping every candidate; with mask, covariates are ranked on the
  eligible cells that the support metrics score. The tutorial selections
  are unchanged: every adjusted arm failed the target before and met it
  after.
- _platform: look up sysctlbyname in the running process instead of a
  hard-coded library path.
- Docstrings drop numbers tied to particular analyses (SEA-AD permutations,
  Replogle dispersion, Perturb-seq timings, the Rosetta benchmark).
- The Welch test states the 2x ratio for equal arms and widens its bound.
- lfc.rst points up to the library-size section; the mouse-brain README
  no longer lists local-only files; the L45Glut notebook's text now
  matches its output for Ppp2r1a and Ap2s1 and no longer says the null
  check shows the extra hits are real.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The three notebooks are re-executed with the 0.1.1 changes: no mean-
difference filter before BH, size factors from well-expressed genes, and
propensity factors chosen per arm.

- SEA-AD uses usevar='unequal' and says it is the more conservative choice
  in a sample this small (2,683 genes). The GCATE cache is refitted.
- Perturb-seq leaves library size out of the propensity model and selects
  factors per arm; only Satb2 is adjusted (U9 and U7 dropped, treated ESS
  0.50 -> 0.68), and its section shows what the selection changed in one
  paragraph instead of hand-picked filtering variants. 8,161 hits.
- Replogle reads the per-arm refit (145,340 discoveries; SUPT5H 1,591
  hits, its down-regulated genes enriched for translation and ribosome
  biogenesis). Section 5 walks through the batch with SUPT5H and SUPT6H
  and skips the walk-through when the local nuisance file is absent. The
  fallback to the legacy rank table is removed, and a heading that skipped
  from level 2 to level 4 is fixed.
- All three describe library size in the propensity model as a judgement
  call and point to the LFC docs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Code review skipped — your organization has no extra usage available to pay for this review.

If your organization's extra usage balance is empty, an organization admin can add extra usage credits at claude.ai/admin-settings/usage. If its monthly spend limit was reached, an admin can raise it on the same page. If neither applies, contact Anthropic support.

Once extra usage is available, comment @claude review on this pull request, or push a new commit, to trigger a review.

@jaydu1
jaydu1 merged commit 6770a39 into main Sep 30, 2026
3 checks passed
@jaydu1
jaydu1 deleted the release-0.1.1 branch September 30, 2026 06:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant