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.7.tar.gz (142.4 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.7-py3-none-any.whl (65.2 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: anywidget_graph-0.2.7.tar.gz
  • Upload date:
  • Size: 142.4 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.7.tar.gz
Algorithm Hash digest
SHA256 2f26b4304259ba255212f7f5381bc83210096b7bd447b89fc86db15d2006d645
MD5 2c0f14520989d2ced483faa9b1b781bd
BLAKE2b-256 c1fb84ecc8f074600297d8054bdfd6683f73a96b26a1ab57085ed8eaf1da10ca

See more details on using hashes here.

Provenance

The following attestation bundles were made for anywidget_graph-0.2.7.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.7-py3-none-any.whl.

File metadata

File hashes

Hashes for anywidget_graph-0.2.7-py3-none-any.whl
Algorithm Hash digest
SHA256 b9b741455b49276a56e3c41576278f7a6c6233ad0b648b7b7fc4ddcd8972d050
MD5 3f851cc86653ae824c3aee7dca03d6cf
BLAKE2b-256 1e6e0f7af6e6fa6cce2de49c85e634e880a20278cb3be71542d302b5821acb05

See more details on using hashes here.

Provenance

The following attestation bundles were made for anywidget_graph-0.2.7-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