---
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.
