Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

Wald test applies lfc_null and alt_hypothesis to each coefficient instead of to the contrast

Abierto
#483 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
4/5
Tiempo estimado
3-5 días
Aptitud para principiantes
52/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
numpy, python

Línea de trabajo

Start by reading wald_test() in glm.py and trace how the contrast, lfc_null, and alternative hypotheses are used to compute the statistic. The report includes a reproducible synthetic-data example comparing ~condition with ~0 + condition, plus a proposed parametrized test covering all five alternatives. Done means equivalent designs produce matching statistics and p-values for the same contrast.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Report

When lfc_null != 0 or alt_hypothesis is set, wald_test() in glm.py subtracts lfc_null from (and clips) every coefficient and only then applies the contrast vector:

stat = contrast @ np.fmax((lfc - lfc_null) / wald_se, 0)   # "greater"
wald_statistic = float(contrast @ (lfc - lfc_null) / wald_se)  # default

That is only right when the contrast has a single non-zero entry. As soon as it has two (a comparison between two non-reference levels, a design without intercept, or a user-supplied numeric contrast) the result tests a different hypothesis: for a contrast whose entries sum to zero, lfc_null cancels out entirely (contrast @ (lfc - lfc_null) = contrast @ lfc), and for the thresholded alternatives each coefficient is clipped separately, so the statistic can be large even when the contrasted LFC is below the threshold.

The same model written in two equivalent ways then gives different p-values. With the bundled synthetic data, contrast B vs A, lfc_null=0.5:

from pydeseq2.dds import DeseqDataSet
from pydeseq2.ds import DeseqStats
from pydeseq2.datasets import load_example_data

counts = load_example_data(modality="raw_counts", dataset="synthetic")
metadata = load_example_data(modality="metadata", dataset="synthetic")
for design in ["~condition", "~0 + condition"]:
    dds = DeseqDataSet(counts=counts, metadata=metadata, design=design, quiet=True)
    dds.deseq2()
    ds = DeseqStats(dds, contrast=["condition", "B", "A"], lfc_null=0.5, quiet=True)
    ds.summary()
    r = ds.results_df.loc["gene1"]
    print(design, ds.contrast_vector, r[["log2FoldChange", "lfcSE", "stat", "pvalue"]].round(4).tolist())
~condition      [0. 1.]  [0.6328, 0.2891, 0.4594, 0.646]
~0 + condition  [-1  1]  [0.6328, 0.2891, 2.1889, 0.0286]

Same estimate, same standard error, but the second statistic is 0.6328 / 0.2891 (null value dropped) instead of (0.6328 - 0.5) / 0.2891 = 0.459. Over the 10 genes, stat differs by up to 4.21 and pvalue by up to 0.89 between the two designs. With alt_hypothesis="greaterAbs" the ~0 + condition fit reports 3 genes at padj < 0.05 against 0 for ~condition. In DESeq2 (R) results(dds, contrast=..., lfcThreshold=0.5) gives identical numbers for both designs, because the threshold is applied to the contrast estimate.

The fix is to compute the scalar contrast @ lfc first and apply lfc_null and the alternative hypothesis to it, keeping the existing contrast standard error (sqrt(Hc' M Hc)). I have a branch with that change and a parametrized test (~condition vs ~0 + condition must agree for all five alternatives) that fails on main and passes with the change; I can open a PR if that is welcome. (A similar PR, #468, was closed in September without discussion; I am opening this issue first to check how you would like it handled.)

Versions

pydeseq2 0.5.4 (main 2ca3b74), pandas 3.0.6, numpy 2.5.4, scipy (current), Python 3.13.14, macOS 26.

Lenguaje dominante
Python
Estrellas
768
Forks
93
Métricas de merge de PR
Sin PR fusionados en 30 d

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de scverse/PyDESeq2

Todos los issues de scverse/PyDESeq2

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.