Skip to main content

Interactive graph visualization for Python notebooks using anywidget

Project description

anywidget-graph

Interactive graph visualization for Python notebooks.

Works with Marimo, Jupyter, VS Code, Colab, anywhere anywidget runs.

Features

  • Universal: One widget, every notebook environment
  • Backend-agnostic: Grafeo, Neo4j, NetworkX, pandas, or raw dicts
  • Interactive: Pan, zoom, click, drag, pin, expand neighbors, box select
  • Customizable: Colors, sizes, layouts, dark mode
  • Exportable: HTML, JSON

Installation

uv add anywidget-graph

Optional extras:

uv add "anywidget-graph[networkx]"   # NetworkX support
uv add "anywidget-graph[pandas]"     # pandas support
uv add "anywidget-graph[grafeo]"     # Grafeo backend
uv add "anywidget-graph[cosmosdb]"   # CosmosDB / Gremlin support

Quick Start

from anywidget_graph import Graph

graph = Graph.from_dict({
    "nodes": [
        {"id": "alice", "label": "Alice", "group": "person"},
        {"id": "bob", "label": "Bob", "group": "person"},
        {"id": "paper", "label": "Graph Theory", "group": "document"},
    ],
    "edges": [
        {"source": "alice", "target": "bob", "label": "knows"},
        {"source": "alice", "target": "paper", "label": "authored"},
    ]
})

graph

Data Sources

Dictionary

graph = Graph.from_dict({
    "nodes": [{"id": "a"}, {"id": "b"}],
    "edges": [{"source": "a", "target": "b"}]
})

Direct initialization

graph = Graph(
    nodes=[{"id": "a", "label": "Alice"}, {"id": "b", "label": "Bob"}],
    edges=[{"source": "a", "target": "b", "label": "KNOWS"}],
)

Cypher results (Neo4j)

from neo4j import GraphDatabase

driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "password"))

with driver.session() as session:
    result = session.run("MATCH (a)-[r]->(b) RETURN a, r, b LIMIT 100")
    graph = Graph.from_cypher(result)

GQL results

graph = Graph.from_gql(result)

SPARQL results

from rdflib import Graph as RDFGraph

g = RDFGraph()
g.parse("data.ttl")
result = g.query("SELECT ?s ?p ?o WHERE { ?s ?p ?o }")
graph = Graph.from_sparql(result)

Gremlin results (CosmosDB, TinkerPop)

graph = Graph.from_gremlin(result)

GraphQL results

graph = Graph.from_graphql(
    response.json(),
    nodes_path="data.characters.results",
    id_field="id",
    label_field="name",
)

NetworkX

import networkx as nx

G = nx.karate_club_graph()
graph = Graph.from_networkx(G)

pandas DataFrames

import pandas as pd

nodes_df = pd.DataFrame({"id": ["alice", "bob"], "group": ["person", "person"]})
edges_df = pd.DataFrame({"source": ["alice"], "target": ["bob"], "weight": [1.0]})

graph = Graph.from_dataframe(nodes_df, edges_df)

Interactivity

Events

graph = Graph.from_dict(data)

@graph.on_node_click
def handle_node(node_id, node_data):
    print(f"Clicked: {node_id}")

@graph.on_edge_click
def handle_edge(edge_data):
    print(f"Edge: {edge_data['label']}")

@graph.on_selection
def handle_selection(node_ids):
    print(f"Selected: {node_ids}")

Selection

graph.selected_nodes            # Current selection (list of IDs)
graph.selection_mode = "box"    # Switch to box-select mode

Node expansion

graph.expand_node("alice")      # Fetch and merge neighbors (requires backend)

Node pinning

graph.pin_nodes(["alice", "bob"])   # Pin at current positions
graph.unpin_nodes(["alice"])        # Release back to layout
graph.toggle_pin("bob")            # Toggle pin state
graph.unpin_all()                   # Unpin everything

Clear

graph.clear()                   # Remove all nodes, edges, pins, and selection

Styling

Property-based coloring

graph = Graph.from_dict(
    data,
    color_field="group",               # Color nodes by field
    color_scale="viridis",             # Scale: viridis, plasma, inferno, magma, cividis, turbo
    size_field="score",                # Size nodes by field
    size_range=[5, 30],                # Min/max node size
)

Edge styling

graph.edge_color_field = "type"
graph.edge_color_scale = "plasma"
graph.edge_size_field = "weight"
graph.edge_size_range = [1, 8]

Layouts

Graph.from_dict(data, layout="force")      # ForceAtlas2 (default)
Graph.from_dict(data, layout="circular")
Graph.from_dict(data, layout="random")

Options

graph = Graph(
    nodes=nodes,
    edges=edges,
    width=800,                  # Widget width (px)
    height=600,                 # Widget height (px)
    background="#fafafa",       # Background color
    show_labels=True,           # Node labels
    show_edge_labels=False,     # Edge labels
    show_toolbar=True,          # Toolbar visibility
    show_settings=True,         # Settings panel
    show_query_input=True,      # Query input box
    dark_mode=True,             # Dark theme
    show_tooltip=True,          # Hover tooltips
    tooltip_fields=["label", "id"],
    max_nodes=300,              # Limit for node expansion
)

Database Backends

Grafeo (default)

import grafeo
db = grafeo.GrafeoDB()
graph = Graph(database_backend="grafeo", grafeo_db=db)

Neo4j (browser-side)

graph = Graph(
    database_backend="neo4j",
    connection_uri="neo4j+s://demo.neo4jlabs.com",
    connection_username="neo4j",
    connection_password="password",
)

Generic backend

graph = Graph(backend=my_backend)  # Any object implementing DatabaseBackend protocol

Export

graph.to_json()                         # JSON string with nodes and edges
graph.to_html()                         # Self-contained HTML string
graph.to_html(title="My Graph")         # Custom title
graph.save_html("graph.html")           # Write HTML to file

Environment Support

Environment Supported
Marimo Yes
JupyterLab Yes
Jupyter Notebook Yes
VS Code Yes
Google Colab Yes
Databricks Yes

Related

License

Apache-2.0

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

anywidget_graph-0.2.8.tar.gz (146.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

anywidget_graph-0.2.8-py3-none-any.whl (68.8 kB view details)

Uploaded Python 3

File details

Details for the file anywidget_graph-0.2.8.tar.gz.

File metadata

  • Download URL: anywidget_graph-0.2.8.tar.gz
  • Upload date:
  • Size: 146.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for anywidget_graph-0.2.8.tar.gz
Algorithm Hash digest
SHA256 a0f51e9fb2739d88ad1336554f6aab98980865375b51c9a3eb4c0452a062071a
MD5 a5d7dc94607a6f801b9e0bf60803d7ac
BLAKE2b-256 d2fa6f3348972aac8510cd7ebdcfaaf59a7af96f1acadfb7c10ed55bfe474e16

See more details on using hashes here.

Provenance

The following attestation bundles were made for anywidget_graph-0.2.8.tar.gz:

Publisher: pypi.yml on GrafeoDB/anywidget-graph

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file anywidget_graph-0.2.8-py3-none-any.whl.

File metadata

File hashes

Hashes for anywidget_graph-0.2.8-py3-none-any.whl
Algorithm Hash digest
SHA256 b77b22f97ca0b2e61343167c7248ab725f52b037cbaadc3fa557290c9d71fcda
MD5 b4dffa77268561a6bf56897ce933f2e9
BLAKE2b-256 64076436bdcb7a32e30a0c23b5ce9b8a4bf188bbdfc7e5f193639947f507f4e9

See more details on using hashes here.

Provenance

The following attestation bundles were made for anywidget_graph-0.2.8-py3-none-any.whl:

Publisher: pypi.yml on GrafeoDB/anywidget-graph

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page