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

Aperta
#1,963 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
48/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Tranquilla
Stack tecnologico
pandas, python

Direzione di ricerca

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.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

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.

Lingua principale
Python
Stelle
2.6k
Fork
229
Merge medio
1g 5h
PR unite (30g)
100

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di graphistry/pygraphistry

Tutte le issue di graphistry/pygraphistry

Issue simili

Altre issue su Python

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.