--- id: cramers_v_py_datascience name: cramers_v kind: function lang: py domain: datascience version: "1.0.0" purity: pure signature: "def cramers_v(a: list, b: list) -> float" description: "Cramer's V del grupo eda: asociacion simetrica entre dos columnas categoricas pareadas (0=independientes, 1=asociacion perfecta), con correccion de sesgo Bergsma-Wicher. Descarta pares con None y devuelve 0.0 si hay <2 categorias o <2 pares. Funcion pura, sin pandas." tags: [eda, correlation, association, categorical, statistics, datascience] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "" imports: [] example: | from datascience import cramers_v a = ["red", "green", "blue", "red", "green", "blue"] b = ["hot", "cool", "cool", "hot", "cool", "cool"] # derivada de a cramers_v(a, b) # -> ~1.0 (asociacion perfecta) tested: true tests: - "test_perfect_association_is_near_one" - "test_independent_columns_low_value" - "test_single_category_returns_zero" - "test_fewer_than_two_pairs_returns_zero" - "test_none_pairs_are_discarded" - "test_always_returns_float_never_none" - "test_derived_column_high_association" test_file_path: "python/functions/datascience/cramers_v_test.py" file_path: "python/functions/datascience/cramers_v.py" params: - name: a desc: > Lista de valores categoricos hashables. Se empareja posicion a posicion con `b`. Los pares donde `a[i]` sea None se descartan. - name: b desc: > Lista de valores categoricos hashables pareada con `a` (idealmente misma longitud). Los pares donde `b[i]` sea None se descartan. zip recorta a la longitud minima de ambas listas. output: > float en [0, 1]. 0.0 = variables independientes, 1.0 = asociacion perfecta. Devuelve 0.0 cuando hay menos de 2 pares validos o menos de 2 categorias distintas en alguna de las dos variables. Nunca devuelve None ni lanza excepcion. --- ## Ejemplo ```python from datascience import cramers_v # Dos categoricas asociadas: b se deriva de a con un mapeo fijo. a = ["red", "green", "blue", "red", "green", "blue", "red", "green", "blue"] mapping = {"red": "hot", "green": "cool", "blue": "cool"} b = [mapping[x] for x in a] cramers_v(a, b) # -> ~1.0 (saber el color predice perfectamente la temperatura) # Dos categoricas independientes (aleatorias) -> V cercana a 0. import random rng = random.Random(42) cats = ["a", "b", "c", "d"] x = [rng.choice(cats) for _ in range(2000)] y = [rng.choice(cats) for _ in range(2000)] cramers_v(x, y) # -> < 0.5 (no hay asociacion) ``` ## Cuando usarla Cuando perfiles o exploras un dataset y necesites medir la **asociacion entre dos columnas categoricas** (no numericas): construir un heatmap de correlacion categorica, detectar columnas redundantes/derivadas una de otra, o decidir que features categoricas aportan informacion antes de modelar. Es el equivalente categorico de un coeficiente de correlacion: simetrica (`cramers_v(a, b) == cramers_v(b, a)`) y normalizada a [0, 1]. ## Notas Funcion pura, sin I/O, sin pandas y sin mutar los inputs. Construye la tabla de contingencia con `collections.Counter` sobre los pares `(a_i, b_i)` y calcula chi-cuadrado a mano (`sum((obs-exp)^2/exp)`), por lo que solo depende de la stdlib. Aplica la **correccion de sesgo de Bergsma-Wicher**, que reduce el inflado de V en tablas pequenas: `phi2corr = max(0, phi2 - (r-1)(k-1)/(n-1))`, con `r`/`k` filas/columnas corregidas y `n` el numero de pares validos. El resultado se clampa a [0, 1] por seguridad numerica. Casos borde resueltos sin excepcion: listas vacias, un solo par, columna con una sola categoria, o None en cualquiera de los dos lados (el par se descarta) -> todos devuelven `0.0` o una V bien definida sobre los pares que queden.