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

Uploaded Python 3

File details

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

File metadata

  • Download URL: anywidget_graph-0.2.6.tar.gz
  • Upload date:
  • Size: 141.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.6.tar.gz
Algorithm Hash digest
SHA256 5c04a6def311f5c219aba72fd5d36de3e1b45265b3610c26feb897162961e765
MD5 27b9eb6d8b243fc0b05b319628f8fe1e
BLAKE2b-256 9612170993e16d5a5d1cf8ac9f21762c450ecdcd082c5d9499095667dd406d69

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for anywidget_graph-0.2.6-py3-none-any.whl
Algorithm Hash digest
SHA256 ae346fa40ac1852e66bca8cff34db165aed4585d05445003c2f69bba825fa977
MD5 659ccbd3a3b32967c220856cfb69ab76
BLAKE2b-256 f43c5b43748c0614dacdca919f49fe57a13462f8c0caa7e318ababaabcfe875e

See more details on using hashes here.

Provenance

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