--- id: normality_tests_py_datascience name: normality_tests kind: function lang: py domain: datascience version: "1.0.0" purity: pure signature: "def normality_tests(values: list, alpha: float = 0.05) -> dict" description: "Tests de normalidad (Jarque-Bera, D'Agostino-Pearson, Shapiro-Wilk) sobre una columna numerica para decidir si sigue una distribucion normal. Descarta None/NaN/no-numericos y reporta consenso de los tests aplicables." tags: [eda, models, statistics, normality, hypothesis-test, distribution, shapiro, jarque-bera] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "" imports: [math, scipy] example: | from normality_tests import normality_tests import numpy as np result = normality_tests(np.random.default_rng(0).normal(0, 1, 1000).tolist()) # result["is_normal"] == True tested: true tests: - "test_normal_large_sample_is_normal" - "test_skewed_sample_is_not_normal" - "test_small_sample_returns_note" - "test_drops_none_nan_and_non_numeric" - "test_shapiro_skipped_above_5000" - "test_normal_below_eight_after_cleaning_is_note" test_file_path: "python/functions/datascience/normality_tests_test.py" file_path: "python/functions/datascience/normality_tests.py" params: - name: values desc: "Lista de valores numericos (una columna). None, NaN, infinitos y no-numericos se descartan antes de testear. Los booleanos se excluyen." - name: alpha desc: "Nivel de significancia por test (default 0.05). normal = p > alpha (no se rechaza H0 de normalidad)." output: > dict. Si n < 8 (tras limpiar): {n, note: "muestra insuficiente", is_normal: None}. En otro caso: {n, jarque_bera:{stat,p,normal}, dagostino:{stat,p,normal}, shapiro:{stat,p,normal}|None (solo 3<=n<=5000), is_normal:bool}. En cada test normal = p > alpha. is_normal es el consenso (todos los tests aplicables coinciden en que los datos son normales). --- ## Ejemplo ```python from normality_tests import normality_tests import numpy as np # Muestra normal -> is_normal True normal = np.random.default_rng(0).normal(loc=10, scale=2, size=1000).tolist() r = normality_tests(normal) r["is_normal"] # True r["jarque_bera"]["normal"] # True r["shapiro"]["p"] > 0.05 # True # Muestra exponencial (sesgada) -> is_normal False expo = np.random.default_rng(7).exponential(scale=1.0, size=1000).tolist() normality_tests(expo)["is_normal"] # False # Muestra insuficiente normality_tests([1, 2, 3, 4, 5]) # {"n": 5, "note": "muestra insuficiente", "is_normal": None} ``` ## Cuando usarla Antes de aplicar un test parametrico o un estimador que asume normalidad (t-test, ANOVA, regresion OLS con intervalos de confianza, z-score para outliers): comprueba primero si la columna es realmente normal. Tambien en la fase EDA para decidir entre media (datos normales) y mediana/transformacion log (datos sesgados), y como gate barato antes de elegir un modelo que asuma errores gaussianos. ## Gotchas - Funcion pura y determinista para una entrada dada, pero los p-valores dependen del tamano de muestra: con n muy grande casi cualquier desviacion minuscula de la normalidad rechaza H0 (poder estadistico alto). Interpreta `is_normal` junto al tamano `n` y al contexto, no como verdad absoluta. - Shapiro-Wilk solo se ejecuta para `3 <= n <= 5000`; fuera de ese rango su clave es `None` y `is_normal` se decide solo con Jarque-Bera y D'Agostino. - Con `n < 8` no se ejecuta ningun test: devuelve `note` e `is_normal: None`. Cuenta el `n` tras limpiar (None/NaN/no-numericos descartados), no la longitud bruta de la lista. - D'Agostino-Pearson (`normaltest`) requiere internamente `n >= 8` para skew y kurtosis; por eso el umbral de muestra insuficiente es 8.