---
name: fdr_correction
kind: function
lang: py
domain: datascience
version: "1.0.0"
purity: pure
signature: "def fdr_correction(pvalues: list, alpha: float = 0.05, method: str = \"bh\") -> dict"
description: "Correccion de comparaciones multiples (multiple-testing) sobre una lista de p-valores: Benjamini-Hochberg (FDR, 'bh') o Bonferroni (FWER, 'bonferroni'). Antidoto al sesgo de mineria de datos (data-mining bias): al evaluar muchas hipotesis a la vez (todos los pares de una matriz), el azar produce falsos positivos; esta funcion ajusta los p-valores y marca cuales siguen siendo significativos tras corregir. Pura, sin dependencias externas, alineada 1:1 con la entrada (admite None en posiciones sin test)."
tags: [eda, statistics, multiple-testing, fdr, benjamini-hochberg, bonferroni, p-value, data-mining-bias, python]
params:
  - name: pvalues
    desc: "lista de p-valores (floats en [0, 1]). Se admiten None u otros valores no validos en posiciones sin test disponible; se propagan como None en la salida y no cuentan como prueba (m)."
  - name: alpha
    desc: "nivel de significancia objetivo tras la correccion (default 0.05). Para BH es el umbral del FDR; para Bonferroni, del FWER (tasa de error por familia)."
  - name: method
    desc: "'bh' = Benjamini-Hochberg (controla FDR, menos conservador, mas potencia); 'bonferroni' = controla FWER (mas conservador). Cualquier otro valor devuelve un dict con note."
output: "dict {p_values_adjusted: lista alineada con pvalues (float ajustado o None), reject: lista de bool (True = significativo tras corregir), n_tests: nº de p-valores validos (m), n_rejected: nº de hipotesis rechazadas, alpha: float aplicado, method: str}. Casos degenerados (vacio, sin p validos, metodo desconocido) anaden clave note. Nunca None ni excepcion."
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
imports: [math]
tested: true
tests: ["test_bh_golden_rechaza_dos_de_tres", "test_bonferroni_mas_conservador_que_bh", "test_p_values_adjusted_alineados_y_en_rango", "test_none_se_propaga_alineado", "test_lista_vacia_devuelve_note", "test_solo_none_devuelve_note", "test_metodo_desconocido_devuelve_note", "test_todos_significativos"]
test_file_path: "python/functions/datascience/fdr_correction_test.py"
file_path: "python/functions/datascience/fdr_correction.py"
---

## Ejemplo

```python
from datascience import fdr_correction

# Tres pruebas: dos muy significativas, una claramente no.
pvalues = [0.01, 0.02, 0.5]

bh = fdr_correction(pvalues, alpha=0.05, method="bh")
print(bh["reject"])        # -> [True, True, False]
print(bh["n_rejected"])    # -> 2

# Bonferroni es mas conservador: solo sobrevive la mas fuerte.
bon = fdr_correction(pvalues, alpha=0.05, method="bonferroni")
print(bon["reject"])       # -> [True, False, False]
print(bon["p_values_adjusted"])  # -> [0.03, 0.06, 1.0]

# Posiciones sin test (None) se propagan alineadas: el llamador puede pasar la
# lista completa de pares y recuperar el mapeo 1:1.
mix = fdr_correction([0.001, None, 0.9])
print(mix["reject"])       # -> [True, False, False]
print(mix["n_tests"])      # -> 2  (el None no cuenta como prueba)
```

## Cuando usarla

Cuando evalues **muchas hipotesis a la vez** y vayas a declarar "significativos"
los resultados por debajo de un umbral de p-valor: matriz de asociacion entre
todas las columnas, barrido de reglas/senales, cualquier busqueda que pruebe N
combinaciones y se quede con las que "pasan". Sin corregir, con N pruebas y
alpha=0.05 esperas ~5% de falsos positivos *por azar*: cuantas mas pruebas, mas
correlaciones espurias. Llama a `fdr_correction` con todos los p-valores de la
familia y usa `reject` (no el umbral crudo) para decidir que es real. Usa `"bh"`
por defecto (mejor potencia); `"bonferroni"` cuando un falso positivo sea muy
costoso y prefieras maxima cautela.

## Gotchas

- Pura y sin dependencias externas (solo `math` de la stdlib).
- Corrige **dentro de una familia de pruebas**: pasa de una vez todos los
  p-valores que compiten, no los corrijas por separado o pierdes el control del
  sesgo.
- La salida esta **alineada 1:1** con la entrada. Las posiciones invalidas
  (`None`, `NaN`, fuera de `[0, 1]`, no numericas) se devuelven como
  `p_values_adjusted=None` y `reject=False`, y no cuentan en `n_tests` (m). Por
  eso puedes pasar la lista completa de pares aunque algunos no tengan test.
- `n_tests` es el numero de p-valores **validos** (m), que puede ser menor que
  `len(pvalues)` si hay `None`.
- BH y Bonferroni controlan cosas distintas: BH la tasa de falsos
  descubrimientos (FDR), Bonferroni la probabilidad de *cualquier* falso
  positivo (FWER). No son intercambiables; elige segun el coste de equivocarte.
- Metodo desconocido o lista vacia/sin p validos no lanzan: devuelven un dict
  con `note`.
