layout: modularity_weighted_layout silently DELETES edges with a dangling endpoint (inner merge), KeyErrors on an edge column named like _node, and emits no positions

Abierto
#1,963 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
4/5
Tiempo estimado
3-5 días
Aptitud para principiantes
48/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Tranquilo
Stack tecnológico
pandas, python

Línea de trabajo

Start in graphistry/layout/modularity_weighted/modularity_weighted.py, especially the merges at lines 101–104 and edge assignments at 107–116; compare sibling layout validation behavior. Reproduce the dangling-endpoint and edge-column collision cases in tests, and check the documented position contract in docs/source/gfql/spec/llm_guide.md. Done means the edge cases and layout contract are handled consistently, with tests covering the behavior.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

Found by the 2026-08 layouts/viz amplification probe. Repros on master @ 42b4f7f7d, pandas 2.3.3 / py3.12.

modularity_weighted_layout() rewrites the caller's edge frame through two unqualified .merge() calls. Because merge defaults to how='inner', edges are silently deleted; because the merge keys are not disambiguated, a benign column name in the edge frame turns into a KeyError.

graphistry/layout/modularity_weighted/modularity_weighted.py:101-104:

101:    edges = (g._edges
102:        .merge(g._nodes[[community_col, g._node]], left_on=g._source, right_on=g._node)
                 .rename(columns={community_col: 'source_community'}).drop(columns=[g._node])
103:        .merge(g._nodes[[community_col, g._node]], left_on=g._destination, right_on=g._node)
                 .rename(columns={community_col: 'destination_community'}).drop(columns=[g._node])
104:    )

and the result is bound as the graph's edges at :116.

Defect 1 (silent-wrong): edges whose endpoint is missing from _nodes are deleted without a word

import pandas as pd, graphistry
e = pd.DataFrame({'s': ['a','b','z'], 'd': ['b','a','a']})   # 'z' is not in _nodes
n = pd.DataFrame({'id': ['a','b'], 'comm': [0, 1]})
out = graphistry.edges(e,'s','d').nodes(n,'id').modularity_weighted_layout(community_col='comm')
print(len(e), '->', len(out._edges))

Observed: 3 -> 2 — the z->a edge is gone, no warning, no error.
Expected: either all 3 edges preserved (with a null/cross_community_weight community), or a typed decline naming the dangling endpoint.

This is the mirror image of the #1944 shape: there a node survived while its edges were dropped; here an edge is dropped while both frames look fine. Note the sibling layouts do reject the same graph loudly — layout_igraph('fr') and group_in_a_box_layout() both raise ValueError: Some vertices in the edge DataFrame are missing from vertices DataFrame. So this is also a surface divergence: three layouts, same input, one silently mutilates it.

The inverse also holds and is untested: duplicate ids in _nodes make the same inner merge fan out each edge into multiple rows.

Defect 2 (bare-crash): an edge column that shares the node-id column name

import pandas as pd, graphistry
e = pd.DataFrame({'s': ['a','b'], 'd': ['b','a'], 'id': [10, 11]})   # edges carry their own 'id'
n = pd.DataFrame({'id': ['a','b'], 'comm': [0, 1]})
graphistry.edges(e,'s','d').nodes(n,'id').modularity_weighted_layout(community_col='comm')

Observed: KeyError: "['id'] not found in axis" — the merge emitted id_x/id_y, so drop(columns=[g._node]) at :102 cannot find id.
Expected: a suffix-safe merge, or a typed error naming the collision.

An id column on edges is entirely ordinary (it is what g.edges(df, 's', 'd', 'id') binds), so this is a routine input.

Defect 3 (contract): the "layout" produces no positions

Despite the name and its listing under Layouts in docs/source/gfql/spec/llm_guide.md:548, the function only sets layout_settings(edge_influence=...) and an edge weight column (:96, :107-116). It emits no x/y at all:

out = g.modularity_weighted_layout()
'x' in out._nodes   # False

Every other entry in that list writes x/y. Either the docs/schema_effects should say "edge weights + settings only, positions come from the server", or the function should produce positions. As it stands, g.modularity_weighted_layout() followed by a local position read silently yields nothing.

Defect 4 (minor): silent overwrites and bare asserts on user data

  • :107-113 assign(weight=..., same_community=...) overwrites an existing user weight column and injects a permanent same_community boolean into the caller's edge table; :116 rebinds edge_weight='weight'.
  • :98-99 assert 'source_community' not in g._edges / 'destination_community' — a user edge frame that already has either column gets a bare AssertionError (and under python -O the check vanishes and :102-103 then produce duplicate column names).
  • :91 raise ValueError(f'Unsupported engine: {engine}') prints the abstract argument rather than engine_concrete, so engine='auto' reports Unsupported engine: EngineAbstract.AUTO.

Category

Defect 1: silent-wrong + surface-divergence. Defect 2: bare-crash on valid input. Defect 3: docs/contract divergence. Defect 4: bare asserts on user data.

Not exercised

cuDF arm not run (cugraph not installed here). By inspection :106-108 same_community.map({True: ..., False: ...}) is a pandas-dict-map form that cuDF handles unreliably for booleans and polars does not have at all — INDICATIVE, static only. Polars is covered separately in the engine-parity report.

Lenguaje dominante
Python
Estrellas
2.6k
Forks
229
Merge medio
1 d 5 h
PR fusionados (30 d)
100

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de graphistry/pygraphistry

Todos los issues de graphistry/pygraphistry

Issues similares

Más issues de Python

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.