--- id: theils_u_py_datascience name: theils_u kind: function lang: py domain: datascience version: "1.0.0" purity: pure signature: "def theils_u(a: list, b: list) -> float" description: "Theil's U (coeficiente de incertidumbre) DIRECCIONAL entre dos columnas categoricas: U(a|b) = fraccion de la incertidumbre de `a` que se elimina conociendo `b`, en [0,1]. ASIMETRICO (theils_u(a,b) != theils_u(b,a)), a diferencia de Cramer's V, lo que permite detectar dependencias direccionales (p.ej. ciudad->pais). Funcion pura." tags: [eda, correlation, association, categorical, entropy, datascience] uses_functions: [] uses_types: [] returns: [] returns_optional: false error_type: "" imports: [] example: | from datascience import theils_u ciudad = ["Madrid", "Madrid", "Paris", "Paris", "Roma", "Roma"] pais = ["ES", "ES", "FR", "FR", "IT", "IT"] theils_u(ciudad, pais) # ~1.0 — saber el pais determina mucho la ciudad? no. theils_u(pais, ciudad) # 1.0 — la ciudad determina el pais (N:1) tested: true tests: - "test_b_determines_a_gives_one" - "test_asymmetry_n_to_one_relation" - "test_independence_gives_zero" - "test_fewer_than_two_pairs_returns_zero" - "test_constant_a_returns_zero" - "test_none_pairs_discarded" - "test_result_in_unit_interval" test_file_path: "python/functions/datascience/theils_u_test.py" file_path: "python/functions/datascience/theils_u.py" params: - name: a desc: > Columna categorica objetivo, cuya incertidumbre se mide. Lista de valores (str, int, etc.). Se empareja por indice con `b`. Los pares en los que `a` o `b` sean None se descartan antes de calcular. - name: b desc: > Columna categorica condicionante: el conocimiento cuyo poder explicativo sobre `a` se evalua. Misma longitud y emparejamiento por indice que `a`. output: > Theil's U(a|b) como float en [0.0, 1.0]. 1.0 = conocer `b` determina `a` por completo; 0.0 = `b` no aporta informacion sobre `a` (independencia). Devuelve 0.0 (nunca None ni excepcion) si hay menos de 2 pares validos o si `a` es constante (H(a)==0). El valor es DIRECCIONAL: U(a|b) generalmente difiere de U(b|a). --- ## Ejemplo ```python from datascience import theils_u # Relacion N:1 — varias ciudades por pais. La ciudad determina el pais, # pero el pais NO determina la ciudad. ciudad = ["Madrid", "Barcelona", "Paris", "Lyon", "Roma", "Milan"] pais = ["ES", "ES", "FR", "FR", "IT", "IT"] # Conocer la ciudad elimina TODA la incertidumbre del pais (cada ciudad # pertenece a un unico pais) -> ~1.0 theils_u(pais, ciudad) # 1.0 (U(pais | ciudad)) # Conocer el pais solo reduce parte de la incertidumbre de la ciudad # (cada pais tiene 2 ciudades posibles) -> < 1.0 theils_u(ciudad, pais) # ~0.5 (U(ciudad | pais)) # La ASIMETRIA es la gracia: theils_u(a, b) != theils_u(b, a). # Una medida simetrica como Cramer's V daria el mismo numero en ambos sentidos # y ocultaria la direccion de la dependencia. ``` ## Cuando usarla Cuando exploras un dataset (grupo `eda`) y quieres detectar **dependencias direccionales** entre columnas categoricas: que columna determina a otra, no solo si estan asociadas. Casos tipicos: jerarquias geograficas (ciudad -> pais, codigo_postal -> provincia), claves derivadas (sku -> categoria), o cualquier relacion N:1 donde te interesa saber el sentido. Usa Theil's U en vez de Cramer's V precisamente cuando la simetria de Cramer's V te impediria ver que `a` explica a `b` pero no al reves. Tambien sirve para construir una matriz de asociacion asimetrica que revele la estructura causal/jerarquica candidata antes de modelar. ## Gotchas Funcion pura, sin I/O ni dependencias externas (solo `math` y `collections`). Notas de uso: - Es **direccional**: `theils_u(a, b)` mide U(a|b) (incertidumbre de `a` explicada por `b`). No asumas simetria. - Pensada para columnas **categoricas**. Si pasas numerica continua de alta cardinalidad, cada valor sera casi unico y el resultado tendera a inflar la asociacion (cuidado al interpretar). - Empareja por indice y **descarta** pares con algun None; con menos de 2 pares validos devuelve 0.0. - Si `a` es constante (H(a)==0), devuelve 0.0: no hay incertidumbre que eliminar. - El resultado se clampa a [0, 1] para absorber error de coma flotante; nunca lanza excepcion ni devuelve None.