32c7336bf6
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7.4 KiB
7.4 KiB
name, kind, lang, domain, version, purity, signature, description, tags, params, output, uses_functions, uses_types, returns, returns_optional, error_type, imports, tested, tests, test_file_path, file_path
| name | kind | lang | domain | version | purity | signature | description | tags | params | output | uses_functions | uses_types | returns | returns_optional | error_type | imports | tested | tests | test_file_path | file_path | |||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| summarize_table_pg | function | py | datascience | 1.0.0 | impure | def summarize_table_pg(dsn: str, table: str, schema: str = "public", high_card_ratio: float = 0.9) -> dict | Adaptador PostgreSQL del perfilado base del grupo eda: espejo de summarize_table_duckdb. Perfila una tabla PostgreSQL con SQL push-down (count, count(DISTINCT), min/max/avg/stddev_samp, percentile_cont) sin traer filas a RAM, y devuelve EXACTAMENTE el mismo esqueleto TableProfile (mismas claves) para que el resto del grupo eda lo consuma igual con fuente PostgreSQL. dict-no-throw. |
|
|
dict dict-no-throw. En exito {status:'ok', profile: TableProfile} con source='postgres' y el MISMO shape que summarize_table_duckdb (n_rows/n_cols, type_breakdown, constant_cols, all_null_cols, null_cell_pct y columns[] de ColumnProfile con name/physical_type/inferred_type/semantic_type/count/null_count/null_pct/distinct_count/unique_pct/flags y sub-dict numeric con min,max,mean,std,p25,p50,p75 y el resto en None). En error {status:'error', error:str}. Claves estadisticas finas (skew, kurtosis, histograma, percentiles finos, moda, outliers, correlaciones, key_candidates, quality_score) quedan en None/[] para que otras funciones del grupo eda las completen. |
|
false | error_go_core | true |
|
python/functions/datascience/summarize_table_pg_test.py | python/functions/datascience/summarize_table_pg.py |
Ejemplo
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from datascience import summarize_table_pg
# Perfila la tabla `trends` del PostgreSQL del proyecto captacion_clientes
# (la misma base que alimenta Metabase).
res = summarize_table_pg(
dsn="postgresql://captacion:secret@localhost:5433/trends",
table="amazon_bestsellers",
schema="public",
high_card_ratio=0.9,
)
if res["status"] == "ok":
p = res["profile"]
print(f"{p['table']}: {p['n_rows']} filas x {p['n_cols']} cols (source={p['source']})")
print("type_breakdown:", p["type_breakdown"])
for col in p["columns"]:
print(col["name"], col["inferred_type"], "nulls=", col["null_pct"], col["flags"])
else:
print("error:", res["error"])
Cuando usarla
- Cuando hagas EDA de una tabla PostgreSQL que no conoces y necesites el esqueleto barato de su perfil (tipos inferidos, nulos, cardinalidad, flags) antes de gastar en estadistica fina. Tipico: las bases PostgreSQL conectadas a Metabase (trends, captacion_clientes, etc.).
- Como adaptador PostgreSQL del grupo
eda: produce el mismo TableProfile quesummarize_table_duckdb, de modo queprofile_tabley el resto del grupo funcionan igual cambiando solo la fuente. - Cuando quieras perfilar tablas grandes sin traer filas a RAM: todo se calcula con agregados (count, count(DISTINCT), min/max/avg/stddev_samp, percentile_cont) que hacen push-down en el motor de PostgreSQL.
Gotchas
- Impura: lee de un servidor PostgreSQL via
pg_query(transaccion read-only, nunca escribe). Requierepsycopg2(ya enpython/.venv) y un DSN valido; un servidor inalcanzable devuelve{status:'error'}sin lanzar. distinct_countexacto solo hasta 200000 filas: paran_rows <= 200000se calculacount(DISTINCT col)EXACTO en la query agregada por columna. Por encima de ese umbral NO se estima (PostgreSQL no trae HyperLogLog de serie sin extension) ydistinct_countse capa de forma conservadora amin(count_no_nulo, n_rows). En ambos casosunique_pct = min(distinct_count / n_rows, 1.0), asi que nunca excede 1.0. Por encima de 200k filas los flagspossible_id/high_cardinalityderivan de esa cota conservadora, no de un distinct real.- El shape es identico a
summarize_table_duckdb(mismas claves de TableProfile y ColumnProfile, mismo sub-dictnumeric) para queprofile_tabley el grupoedalo consuman sin distinguir la fuente.sourcees"postgres"(vs"duckdb"). NO calcula skew, kurtosis, histograma, percentiles finos (p1/p5/p95/p99), moda, outliers, correlaciones, key_candidates ni quality_score: esas claves quedan enNone/[]para otras funciones del grupo. El sub-dictnumericsolo trae min, max, mean, std, p25, p50, p75 (estos tres ultimos viapercentile_cont WITHIN GROUP). - Identificadores (schema/tabla/columna) se interpolan citados, no son parametrizables: por eso
tableyschemase validan contra^[A-Za-z_][A-Za-z0-9_]*$antes de citarlos con comillas dobles. Un nombre invalido (con;, espacios, etc.) devuelve{status:'error'}sin tocar la base. Los valores (schema/table de la query ainformation_schema) si van por parametros posicionales%s. countdel ColumnProfile es el no-nulo (count(col));null_count = n_rows - count. Una tabla con 0 filas devuelve perfiles connull_pct=0.0ydistinct_count=0.
Notas
Contrato compartido por todo el grupo eda (identico a summarize_table_duckdb,
mantener estable):
TableProfile = {
table, source, profiled_at, n_rows, n_cols, size_bytes, duplicate_rows,
duplicate_pct, constant_cols:[str], all_null_cols:[str], null_cell_pct,
type_breakdown:{numeric, categorical, datetime, text, boolean},
columns:[ColumnProfile], correlations, key_candidates:[str], quality_score,
llm, models
}
ColumnProfile = {
name, physical_type, inferred_type, semantic_type, count, n_rows, null_count,
null_pct, empty_count, empty_pct, distinct_count, unique_pct, flags:[str],
quality_score, numeric:<sub>|None, categorical:<sub>|None, datetime:<sub>|None
}
numeric_sub = {
min, max, mean, median, mode, std, variance, cv, p1, p5, p25, p50, p75, p95,
p99, iqr, skew, kurtosis, n_outliers, outlier_pct, zero_pct, negative_pct,
distribution_type, histogram
}
Mapeo de data_type (information_schema) PostgreSQL a inferred_type:
smallint/integer/bigint/numeric/decimal/real/double precision/serial* -> numeric;
date/time*/timestamp* -> datetime; boolean -> boolean; text/varchar/character* ->
categorical si distinct_count <= 50 o distinct_count/n_rows < 0.5, si no text;
el resto (json, jsonb, uuid, array, bytea, ...) -> text.
Flags por columna: constant (distinct_count<=1), possible_id (unique_pct>=0.99
y null_pct==0), high_cardinality (categorical con unique_pct>=high_card_ratio),
mostly_null (null_pct>0.5).