0014 — Semántica de proyección de redes: tipo de nodo, peso, scope y asortatividad por proxy¶
- Estado: Aceptada
- Fecha: 2026-06-15
- Decidido por: IA (Claude Opus 4.8), validado por el Product Owner proxy
(ver
registro-ia.md) - Relacionada con: 0006 (tabla Arrow +
NetworkSpec), 0007 (OpenAlex backbone; refs y citantes ya en el corpus), 0013 (identidad estable de papers). - Difiere a: 0007 §co-citación / Hito 8 (
OpenAlexEnricher, 2º nivel de fetch) la co-citación completa y el lookup ROR→país.
Contexto¶
El Hito 2 implementa el núcleo de redes (API.md §7–10): los cinco proyectores, el analizador,
los exportadores y la fachada Networks. Para que esas funciones puras tuvieran un contrato
sin ambigüedad hubo que fijar cuatro cuestiones de semántica que no estaban resueltas en
API.md y que cambian lo que un consumidor observa del grafo:
- Qué es el peso de una arista y cómo se filtra una red poco densa.
- Qué entidad es el nodo en cada proyección.
- Qué arma
Networks.quicky por qué la co-citación no entra en el camino de baja fricción. - Cómo se mide la asortatividad cuando el atributo real (país/región) no está en el corpus
y hay que usar un proxy. Esta tensión reaparece en el criterio geográfico del informe de
calidad de co-citación (
min_countries).
El estudio de semiconductores que originó el método (crítica #2 del sandbox IED) pedía que el
acoplamiento bibliográfico —barato, mira hacia adelante, no necesita 2º nivel de fetch—
fuera ciudadano de primera y operara sobre el corpus completo, no solo sobre las semillas.
La co-citación, en cambio, es la proyección más cara: depende de traer los citantes con sus
propias citas, lo que recién está disponible tras el OpenAlexEnricher del Hito 8
(ADR 0007).
Decisión¶
D1 — Peso = conteo crudo; filtro min_weight¶
El peso de una arista es el conteo crudo de ítems compartidos entre los dos extremos
(papers co-firmados, keywords co-ocurrentes, referencias o citantes compartidos). No se
normaliza (sin Jaccard, sin coseno de Salton) en este hito. El filtrado de redes hiperdensas se
hace con el parámetro min_weight (default 1 = sin filtro): se descarta toda arista con
weight < min_weight.
D2 — Tipo de nodo por proyección¶
| Proyección | Nodo | Columna |
|---|---|---|
| Co-autoría | entidad (autor) | authors_id |
| Colaboración institucional | entidad (institución) | institutions_id |
| Co-ocurrencia de keywords | entidad (keyword) | keywords_id |
| Acoplamiento bibliográfico | paper | id (sobre references_id) |
| Co-citación | paper | id (sobre cited_by_id) |
En co-autoría / instituciones / co-word el nodo es la entidad que co-ocurre; en acoplamiento y co-citación el nodo es el paper y la arista representa referencias/citantes compartidos.
D3 — Networks.quick no incluye co-citación¶
Networks.quick arma cuatro redes con configuración razonable: acoplamiento (scope full),
co-autoría, colaboración institucional y co-ocurrencia de keywords. No incluye co-citación,
porque ésta requiere el 2º nivel de fetch del OpenAlexEnricher (Hito 8); quick lo avisa por
log al construir. El CoCitationProjector sí existe y es invocable a propósito vía
Networks.build(corpus, NetworkSpec(kind="cocitation")), con scope por defecto seeds_only:
proyecta sobre los citantes ya presentes en cited_by_id, lo cual es válido para ese subset pero
no es la co-citación completa hasta el enriquecimiento del Hito 8.
D4 — Asortatividad por atributo configurable, con proxy explícito¶
assortativity(g, *, attribute=None, by_degree=True, proxy=None) mide asortatividad por un
atributo categórico configurable por el usuario (no se hardcodea ningún campo como region;
crítica #5) y/o por grado. Cuando se pasa proxy, el dict de salida incluye una clave
proxy_disclaimer que advierte que el atributo es un proxy del campo real, no el campo real
("fácil pero consciente"). El proxy de país se deriva de authors_affiliations (per-paper) en
el momento de asignar atributos a los nodos, fuera de la función pura.
Nota — proxy de país en min_countries (informe de calidad de co-citación)¶
El criterio geográfico del cocitation_quality_report (min_countries) usa, en Hito 2,
institutions_id como proxy de la diversidad de países: cuenta ids de institución únicos como
aproximación al número de países. El output marca el proxy con un disclaimer. El refinamiento real
(lookup ROR→país) queda para el Hito 8, junto con la co-citación completa.
Consecuencias¶
- El acoplamiento es ciudadano de primera y reproducible sin red: opera sobre el corpus completo desde el Hito 2, sin depender de ningún Enricher (las refs ya vienen de OpenAlex, ADR 0007). Cierra la crítica #2.
- La semántica de cada red es inequívoca: quien consume un
NetworkArtifactsabe qué es un nodo y qué significa un peso, y puede ralear conmin_weightsin sorpresas. quickes honesto sobre lo que omite: no promete co-citación que aún no puede sostener (lección 5 de v0: no prometer de más); avisa por log y deja elCoCitationProjectordisponible para el uso consciente.- La asortatividad y el
min_countriesson honestos sobre el proxy: el disclaimer evita leer un proxy de país como dato duro. El costo es que el lookup ROR→país real queda diferido al Hito 8; hasta entonces, estos números son orientativos. - Peso crudo, no normalizado: simple y predecible, pero comparar densidades entre redes de distinto tamaño exige cuidado. Migrar a un peso normalizado (Salton/Jaccard) sería aditivo y reversible (parámetro extra del proyector), no rompe el contrato actual.
AS-BUILT — capa decorate separada de los proyectores (2026-06-16, #25)¶
Al implementar el label legible en los nodos (#25; síntoma en la Nota 09 B3: las redes salían con
id crudo —oa:…, I185261750, un ORCID— ilegibles en Gephi/VOSviewer/Cytoscape) se confirmó
la pureza de los proyectores de D2: los proyectores (networks/projectors.py) siguen siendo
funciones puras sobre pa.Table y NO setean ningún atributo de nodo (devuelven el grafo con
ids crudos, sin label).
La legibilidad y los atributos para export/GUI viven en una capa de frontera separada,
bib2graph.networks.decorate (decorate_graph/decorate), aplicada en facade.py:_build_artifact
—no en los proyectores—. Inyecta en los nodos: label (mapeo por NetworkKind: paper →
"título (año)" truncado a LABEL_MAX_CHARS=60; autor/institución → nombre *_raw correlativo;
keyword → la keyword), degree_centrality (todos) y, para paper, year/is_seed/curation_status;
community opcional desde artifact.communities. Así Networks.quick/build devuelven artefactos
decorados sin contaminar el núcleo puro de proyección. Contrato en API.md §7.1.
Esta separación es coherente con D2 (el proyector solo decide qué entidad es el nodo) y con la convención del repo "núcleo puro vs frontera": la proyección no sabe de presentación. (Nota aditiva; no modifica la decisión original.)