diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 903f56c14..451f8d8f1 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -45,7 +45,7 @@
"ghcr.io/prulloac/devcontainer-features/latex:1": {
"scheme": "minimal",
"mirror": "https://mirror.ctan.org/systems/texlive/tlnet/",
- "packages": "tikz-network,standalone,xcolor,xifthen,tools,ifmtarg,pgf,datatool,etoolbox,tracklang,amsmath,trimspaces,epstopdf-pkg,dvisvgm"
+ "packages": "tikz-network,standalone,xcolor,xifthen,tools,ifmtarg,pgf,datatool,etoolbox,tracklang,amsmath,trimspaces,epstopdf-pkg,dvisvgm,preview,babel-english"
}
}
}
diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml
index 6d5a1becb..ad9992bd7 100644
--- a/.github/actions/setup/action.yml
+++ b/.github/actions/setup/action.yml
@@ -1,6 +1,6 @@
name: Setup # Inspired by https://github.com/pyg-team/pytorch_geometric/blob/737707c37fc2bd712a2289b683ec14549926ff49/.github/actions/setup/action.yml
-description: Set up Python, PyTorch and PyTorch Geometric.
+description: Set up environment with dependencies
inputs: # defaults are set to the version used in the dev container
python-version:
@@ -26,6 +26,12 @@ runs:
python-version: ${{ inputs.python-version }}
activate-environment: true
+ - name: Cache uv virtual environment
+ uses: actions/cache@v4
+ with:
+ path: .venv
+ key: uv-venv-${{ runner.os }}-py-${{ inputs.python-version }}-cuda-${{ inputs.cuda-version }}
+
- name: Install pathpyG
run: |
uv sync --frozen --extra ${{ inputs.cuda-version }}
@@ -38,9 +44,48 @@ runs:
uv run python -c "import torch; print('CUDA:', torch.version.cuda)"
shell: bash
+ - name: Set up TeX Live
+ if: ${{ inputs.full_install == 'true' }}
+ uses: TeX-Live/setup-texlive-action@v3
+ with:
+ cache: true
+ packages: |
+ scheme-basic
+ latexmk
+ tikz-network
+ standalone
+ xcolor
+ xifthen
+ tools
+ ifmtarg
+ pgf
+ datatool
+ etoolbox
+ tracklang
+ amsmath
+ trimspaces
+ epstopdf-pkg
+ dvisvgm
+ preview
+ babel-english
+
- name: Install extension packages
if: ${{ inputs.full_install == 'true' }}
- run: | # ToDo: Add LaTeX installation
- sudo apt-get update && sudo apt-get install -y build-essential python3-dev libcairo2-dev libpango1.0-dev ffmpeg
+ uses: awalsh128/cache-apt-pkgs-action@v1
+ with:
+ execute_install_scripts: true
+ packages: |
+ build-essential
+ python3-dev
+ libcairo2-dev
+ libpango1.0-dev
+
+ - name: Install ffmpeg
+ if: ${{ inputs.full_install == 'true' }}
+ uses: FedericoCarboni/setup-ffmpeg@v3
+
+ - name: Install optional Python dependencies
+ if: ${{ inputs.full_install == 'true' }}
+ run: |
uv sync --frozen --extra vis --extra ${{ inputs.cuda-version }}
shell: bash
diff --git a/docs/gen_ref_pages.py b/docs/gen_ref_pages.py
index f2059e22c..8055deda8 100644
--- a/docs/gen_ref_pages.py
+++ b/docs/gen_ref_pages.py
@@ -12,7 +12,7 @@
ignored_modules = yaml.safe_load(ignored_modules_path.read_text("utf-8"))
for path in sorted(Path("src").rglob("*.py")):
- if str(path.relative_to(".")) in ignored_modules:
+ if ignored_modules and str(path.relative_to(".")) in ignored_modules:
print(f"Skipping {path} as it is in the ignored modules list.")
continue
module_path = path.relative_to("src").with_suffix("")
@@ -28,7 +28,14 @@
elif parts[-1] == "__main__":
continue
- nav[(part.split("_")[-1] for part in parts)] = doc_path.as_posix()
+ parts_list = []
+ for part in parts:
+ if part.startswith("_"):
+ parts_list.append(part.split("_")[-1])
+ else:
+ parts_list.append(part)
+
+ nav[tuple(parts_list)] = doc_path.as_posix()
print(f"Checking {full_doc_path}")
if not (Path("docs") / full_doc_path).exists():
diff --git a/docs/getting_started.md b/docs/getting_started.md
index 912a99037..b4a8ebdc3 100644
--- a/docs/getting_started.md
+++ b/docs/getting_started.md
@@ -47,10 +47,22 @@ pip install git+https://github.com/pathpy/pathpyG.git
### Optional Visualisation Backends
-We provide multiple visualisation backends for PathpyG. The default backend [D3.js](https://d3js.org/) does not require any additional dependencies. We further provide a [Matplotlib](https://matplotlib.org/) backend that is installed by default. Additionally, we implemented a [Manim](https://www.manim.community/) backend that is not installed by default due to its dependencies that are required for installation. Please refer to the [Manim installation instructions](https://docs.manim.community/en/stable/installation/uv.html) for more information. Once installed, you can use the Manim backend for visualisation by setting the `backend` in the `PathpyG.plot` function to `manim`:
-```python
-import pathpyg as pp
+We provide multiple visualisation backends for PathpyG. The default backend [D3.js](https://d3js.org/) does not require any additional dependencies. We further provide a [Matplotlib](https://matplotlib.org/) backend that is installed by default. Additionally, we implemented a [tikz](https://tikz.dev/) and a [Manim](https://www.manim.community/) backend that are not installed by default due to their dependencies that are required for installation. You can use the tikz backend if you have a LaTeX distribution installed on your system.
-t_graph = TemporalGraph.from_edge_list([('a', 'b', 1),('b', 'a', 3), ('b', 'c', 3)])
-pp.plot(t_graph, backend='manim')
-```
\ No newline at end of file
+To use the Manim backend, please refer to the [Manim installation instructions](https://docs.manim.community/en/stable/installation/uv.html) for more information. Once installed, you can use the backends for visualisation by setting the `backend` in the `PathpyG.plot` function to `tikz` or `manim`:
+
+??? example "Using the TikZ Backend"
+ ```python
+ import pathpyg as pp
+
+ g = pp.Graph.from_edge_list([('a', 'b'),('b', 'c'),('c', 'a')])
+ pp.plot(g, backend='tikz')
+ ```
+
+??? example "Using the Manim Backend"
+ ```python
+ import pathpyg as pp
+
+ t_graph = TemporalGraph.from_edge_list([('a', 'b', 1),('b', 'a', 3), ('b', 'c', 3)])
+ pp.plot(t_graph, backend='manim')
+ ```
\ No newline at end of file
diff --git a/docs/plot_tutorial.md b/docs/plot_tutorial.md
index bfff91c8b..cbc946f64 100644
--- a/docs/plot_tutorial.md
+++ b/docs/plot_tutorial.md
@@ -1,238 +1,238 @@
-# Develop Custom Plot Functions
+# Developing your own Plots
-This tutorial guides you through the process of creating your own plotting functions in pathpyG.
+!!! abstract "Overview"
+ Add a new histogram plot to pathpyG’s visualisation stack, wire it into [`pp.plot(...)`][pathpyG.plot], and render it with Matplotlib. This guide explains the data-prep vs. rendering split and shows the minimal pieces to implement.
-The visualization framework of pathpyg is designed in such a way that is easy to extend it according your own needs.
+This tutorial shows how to add a new plotting capability to pathpyG’s visualisation backend by implementing a histogram plot. You’ll learn how plot types, backends, and configuration work together, and how to add a new plot into the public [`pp.plot(...)`][pathpyG.plot] entry point.
-For this tutorial we want to implement capabilities to plot histograms.
+**What you’ll do**
-You will learn:
+- :material-family-tree: Understand the new visualisation architecture
+- :material-chart-bar: Implement a new `HistogramPlot` that prepares data
+- :material-vector-link: Wire it into the plot orchestrator and select backends
+- :material-image-multiple: Add Matplotlib rendering support for the new type
+- :material-test-tube: Use and (optionally) test your new plot
-- How to set up a generic plot function
-- How to convert `pathpyG` data to plot data
-- How to plot with `d3js`
-- How to plot with `tikz`
-- How to plot with `matplotlib`
+!!! tip "Scope"
+ This guide focuses on Matplotlib for rendering histograms (a natural fit). You can add other backends later following the same pattern.
-## Structure
+## Visualisation architecture at a glance
-Plotting commands and functions are located under `/src/pathpyG/visualisation/`
+The visualisation module is built around two core abstractions and a single entry point:
-```tree
-visualisation
- __init__.py
- _d3js
- ...
- _matplotlib
- ...
- _tikz
- ...
- layout.py
- network_plots.py
- plot.py
- utils.py
-```
-
-Folders with `_...` indicate the supported backends. We will have a look at them later.
-
-The `layout.py` file includes algorithms to calculate the positions of the nodes.
+- :material-database-cog: [`PathPyPlot`][pathpyG.visualisations.pathpy_plot.PathPyPlot] prepares data/config for rendering. Subclass it for each plot type.
+- :material-cog: [`PlotBackend`][pathpyG.visualisations.plot_backend.PlotBackend] renders a given [`PathPyPlot`][pathpyG.visualisations.pathpy_plot.PathPyPlot] using a concrete engine ([Matplotlib][pathpyG.visualisations._matplotlib], [TikZ][pathpyG.visualisations._tikz], [d3.js][pathpyG.visualisations._d3js], [Manim][pathpyG.visualisations._manim]).
+- :material-play-circle: [`plot(...)`][pathpyG.plot] is the public API. It chooses a plot class (kind) and a backend (by argument or filename extension), instantiates both, then saves or shows.
-In the `utils.py` file are useful helper functions collected. E.g. among others a function that converts `hex_to_rgb`, `rgb_to_hex`, or a simple [`Colormap`][pathpyG.visualisations.utils.Colormap] class. If your plot needs generic functions which might be helpful for other plots as well, this would be a good place to store them.
+!!! info "Reference"
+ See the [module overview](/reference/pathpyG/visualisations) for supported backends, formats, and styling options. For existing plot types, see [`NetworkPlot`][pathpyG.visualisations.network_plot.NetworkPlot] (static) and [`TemporalNetworkPlot`][pathpyG.visualisations.temporal_network_plot.TemporalNetworkPlot] (temporal). For existing backends, see e.g. [`MatplotlibBackend`][pathpyG.visualisations._matplotlib.backend.MatplotlibBackend] which we will be using.
-The `network_plots.py` file includes all plots related to network visualization. We will create in this tutorial a similar collection for histograms.
+## Define a new plot type: HistogramPlot
-Finally, the `plot.py` file contains our generic [`PathPyPlot`][pathpyG.visualisations.plot.PathPyPlot] class which we will use to build our own class.
+Start by creating a new subclass of [`PathPyPlot`][pathpyG.visualisations.pathpy_plot.PathPyPlot] (e.g., in `src/pathpyG/visualisations/histogram_plot.py`). Its job is to:
-This abstract class has a property `_kind` which will specify the type of plot for the generic plot function. Similar to `pandas` we should be able to call:
-
-```python
-pp.plot(graph, kind="hist")
-```
+- Accept the input object(s) (typically a [`Graph`][pathpyG.core.graph.Graph]) and user options
+- Compute or collect the values to be binned
+- Populate `self.data` with a clean, backend-agnostic structure
+- Update `self.config` with plot configuration (bins, labels, etc.)
-This abstract class has two dict variables `self.data` and `self.config`. The `self.data` variable is used to store the data needed for the plot, while the `self.config` stores all the configurations passed to the plot.
+!!! info "Minimal class attributes"
+ Inputs: `graph: Graph`, `key: str` (what to measure), `bins: int | sequence`, plus style options via `**kwargs`.
-Furthermore this class has three abstract methods we have to define later for our supported backends: `generate` to generate the plot, `save` to save the plot to a file, `show` to show the current plot.
-
-
-## Let's get started
-
-In order to get started, we have to create a new python file where we will store our histogram plots. So let's generate a new file `hist_plots.py`
-
-```
-touch hist_plots.py
-```
+ Data format (suggested):
+ - `self.data["hist_values"]: list[float | int]` — the values to bin
+ - optionally precomputed bins/edges (if you want backend-agnostic binning)
+ - `self.config` should include `title`, `xlabel`, `ylabel`, and `bins`
-We start with creating a function which allows us later to plot a histogram.
-
-This function will take a `Graph` object as input and has the parameters `key` and `bins` as well as a dict of `kwargs` for furthermore specifications.
-
-We will use the `key` variable to define the data type of the histogram e.g. `by='betweenes'` to get the betweenes centrality plotted. With the `bins` parameters we will change the amount of bins in the histogram. all other options will by passed to the function as keyword arguments and can be backend specific.
+### Example outline:
```python
-"""Histogram plot classes."""
+# src/pathpyG/visualisations/histogram_plot.py
from __future__ import annotations
-
import logging
+from typing import Any
+from pathpyG.visualisations.pathpy_plot import PathPyPlot
+from pathpyG.core.graph import Graph
-from typing import TYPE_CHECKING, Any
-
-# pseudo load class for type checking
-if TYPE_CHECKING:
- from pathpyG.core.graph import Graph
-
-# create logger
-logger = logging.getLogger("pathpyG")
-
-
-def hist(network: Graph, key: str = 'degree', bins: int = 10, **kwargs: Any) -> HistogramPlot:
- """Plot a histogram."""
- return HistogramPlot(network, key, bins, **kwargs)
-
-```
-
-pathpyG is using logging to print out messages and errors. It's a good habit to use it also for your plotting function.
-
-Our `hist` function will be callable via the package. e.g. `pp.hist(...)`. Itself it will return a plotting class which we have to create.
-
+logger = logging.getLogger("root")
-```python
-from pathpyG.visualisations.plot import PathPyPlot
class HistogramPlot(PathPyPlot):
- """Histogram plot class for a network properties."""
+ """Prepare data for histogram visualisation.
- _kind = "hist"
+ Collects values from a Graph according to `key` and exposes them in
+ `self.data["hist_values"]` for backends to render.
+ """
- def __init__(self, network: Graph, key: str = 'degree', bins: int = 10, **kwargs: Any) -> None:
- """Initialize network plot class."""
+ _kind = "histogram"
+
+ def __init__(self, graph: Graph, key: str = "degree", bins: int | list[int] = 10, **kwargs: Any) -> None:
super().__init__()
- self.network = network
- self.config = kwargs
- self.config['bins'] = bins
- self.config['key'] = key
+ self.graph = graph
+ # merge kwargs into config; ensure required fields are present
+ self.config.update({
+ "bins": bins,
+ "title": kwargs.pop("title", f"{key.title()} distribution"),
+ "xlabel": kwargs.pop("xlabel", key),
+ "ylabel": kwargs.pop("ylabel", "count"),
+ })
+ self.key = key
+ self.config.update(kwargs)
self.generate()
def generate(self) -> None:
- """Generate the plot."""
- logger.debug("Generate histogram.")
-```
+ # Compute values to bin based on `key`
+ if self.key in ("degree", "degrees"):
+ values = list(self.graph.degrees().values())
+ elif self.key in ("in_degree", "indegree", "in-degrees"):
+ values = list(self.graph.degrees(mode="in").values())
+ elif self.key in ("out_degree", "outdegree", "out-degrees"):
+ values = list(self.graph.degrees(mode="out").values())
+ else:
+ logger.error(f"Histogram key '{self.key}' not supported.")
+ raise KeyError(self.key)
-The `HistogramPlot` plotting class is a child from our abstract `PathPyPlot` function. We will overwrite the abstract `generate()` function in order to get the data needed for our plot.
-
-By convention we assume `d3js` will be the default plot backend, hence the final data generated by this function should provide the necessary data structure for this backend.
-
-For other backends, this data might be needed to be converted e.g. keywords might be different. We will address this later in our tutorial.
+ self.data["hist_values"] = values
+```
+!!! note
+ - Keep the class small: gather values and fill `self.data`/`self.config`.
+ - Choose names that are clear for backends (`hist_values`, `bins`, labels).
-## Testing, Testing, Testing
+## Add the new plot to the public API
-Before we start developing our histogram plot, we should set up a test environment so that we can directly develop the unit test next to our plot function.
+[`plot(...)`][pathpyG.plot] uses the `PLOT_CLASSES` mapping to instantiate the right plot class for a given `kind`. Extend it with your new class:
-Therefore we are going to our testing folder an create a new test file.
+```python
+# src/pathpyG/visualisations/plot_function.py
+from pathpyG.visualisations.histogram_plot import HistogramPlot
-```
-cd ../../../tests/
-touch test_hist.py
+PLOT_CLASSES: dict = {
+ "static": NetworkPlot,
+ "temporal": TemporalNetworkPlot,
+ "histogram": HistogramPlot, # add this line
+}
```
-Now we can create a simple test environment with a simple graph and call our `hist(...)` function.
+??? example "Usage"
-```python
-from pathpyG.core.graph import Graph
-from pathpyG.visualisations.hist_plots import hist
+ ```python
+ import pathpyG as pp
+ g = pp.Graph.from_edge_list([("a", "b"), ("b", "c"), ("a", "c")])
+ # Matplotlib is the natural backend for histograms
+ pp.plot(g, kind="histogram", backend="matplotlib", key="degree", bins=10, filename="degree_hist.png")
+ ```
-def test_hist_plot() -> None:
- """Test to plot a histogram."""
- net = Graph.from_edge_list([["a", "b"], ["b", "c"], ["a", "c"]])
- hist(net)
-```
+!!! tip "Backend selection"
+ [`plot(...)`][pathpyG.plot] auto-selects a backend from the filename extension if you omit `backend`. For histograms, prefer PNG via Matplotlib by passing `filename="...png"` or `backend="matplotlib"`.
-Note: If you only want to run this function and not all other test you can use:
+## Add Matplotlib support for HistogramPlot
-```
-pytest -s -k 'test_hist_plot'
-```
+Backends validate supported plot types. The [Matplotlib backend][pathpyG.visualisations._matplotlib] currently supports `NetworkPlot` and renders nodes/edges. We’ll extend it to also support `HistogramPlot`.
+
+Implementation approach:
-## Generating the plot data
+1. Add `HistogramPlot` to `SUPPORTED_KINDS` so the backend accepts the plot type.
+2. Branch in [`to_fig()`](/reference/pathpyG/visualisations/_matplotlib/backend/#pathpyG.visualisations._matplotlib.backend.MatplotlibBackend.to_fig) (or factor out into a helper) to draw a histogram when the plot is a `HistogramPlot`.
-To plot our histogram we first have to generate the required data from our graph.
+Sketch of the required changes (condensed for illustration):
-In the future we might want to add more options for histograms, hence we use the `match`-`case` function form python.
-
```python
- def generate(self) -> None:
- """Generate the plot."""
- logger.debug("Generate histogram.")
-
- data: dict = {}
-
- match self.config["key"]:
- case "indegrees":
- logger.debug("Generate data for in-degrees")
- data["values"] = list(self.network.degrees(mode="in").values())
- case "outdegrees":
- logger.debug("Generate data for out-degrees")
- data["values"] = list(self.network.degrees(mode="out").values())
- case _:
- logger.error(
- f"The <{self.config['key']}> property",
- "is currently not supported for hist plots.",
- )
- raise KeyError
-
- data["title"] = self.config["key"]
- self.data["data"] = data
+# src/pathpyG/visualisations/_matplotlib/backend.py
+from pathpyG.visualisations.histogram_plot import HistogramPlot
+
+SUPPORTED_KINDS = {
+ NetworkPlot: "static",
+ HistogramPlot: "histogram", # add support
+}
+
+class MatplotlibBackend(PlotBackend):
+ ...
+ def to_fig(self) -> tuple[plt.Figure, plt.Axes]:
+ # If histogram: render using ax.hist
+ if self._kind == "histogram":
+ return self._to_fig_histogram()
+ # Else: existing network rendering
+ return self._to_fig_network()
+
+ def _to_fig_histogram(self) -> tuple[plt.Figure, plt.Axes]:
+ fig, ax = plt.subplots(
+ figsize=(unit_str_to_float(self.config["width"], "in"), unit_str_to_float(self.config["height"], "in")),
+ dpi=150,
+ )
+ ax.set_axis_on()
+ ax.hist(self.data["hist_values"], bins=self.config.get("bins", 10), color=rgb_to_hex(self.config["node"]["color"]), alpha=0.9)
+ ax.set_title(self.config.get("title", "Histogram"))
+ ax.set_xlabel(self.config.get("xlabel", "value"))
+ ax.set_ylabel(self.config.get("ylabel", "count"))
+ return fig, ax
+
+ def _to_fig_network(self) -> tuple[plt.Figure, plt.Axes]:
+ # move existing implementation of `to_fig` here
+ ...
```
-First we initialize a dictionary `data` to store our values. In this case we are interested in the in and out-degrees of our graph, which are already implemented in `pathpyG` (state 2023-11-26).
+!!! tip "Tips"
+ - Reuse `unit_str_to_float` so sizing behaves like other plots.
+ - Use a default color from `self.config["node"]["color"]` for consistency.
+ - Keep the new code path fully separate from the network drawing code to avoid regressions.
-If the keyword is not supported the function will raise a `KeyError`.
+??? info "If you want web or LaTeX histograms"
+ The current d3.js and TikZ backends are tailored to network visualisation (they expect `nodes`/`edges` in `self.data`). To add histogram support there, you would:
-To provide a default title for our plot we also store the keyword in the data dict. If further data is required for the plot it can be stored here.
+ - Create a new JS or TeX template for histograms
+ - Extend the backend to accept `HistogramPlot` and dispatch to the new template
-Finally, we add the data dict to our `self.data` variable of the plotting class. This variable will be used later in the backend classes.
+ Start with Matplotlib first — it's a good starting point.
-With this our basic histogram plot function is finished. We are now able to call the plot function, get the data from our graph and create a data-set which can be passed down to the backend for visualization.
+## Try it out
-## The matplotlib backend
+Once you’ve added the `HistogramPlot`, updated `PLOT_CLASSES`, and extended the Matplotlib backend as shown, you can create and save a histogram in a single call:
-Let's open the `_matplotlib` folder located under `/src/pathpyG/visualisation/_matplotlib`, where all matplotlib functions are stored.
+```python
+import pathpyG as pp
-```tree
-_matplotlib
- __init__.py
- core.py
- network_plots.py
+g = pp.Graph.from_edge_list([("a", "b"), ("b", "c"), ("a", "c"), ("c", "d")])
+pp.plot(
+ g,
+ kind="histogram",
+ backend="matplotlib", # or infer via filename extension
+ key="degree",
+ bins=5,
+ title="Node Degree Distribution",
+ filename="degree_hist.png",
+)
```
-The `_init_.py` holds the configuration for the plot function, which we will modify later. The `core.py` file contains the generic `MatplotlibPlot` class, which provides `save` and `show` functionalities for our plots. We do not need to modify these functions. Instead, we have to generate a translation function from our generic data dict (see above) to a histogram in matplotlib. To do so, lets create first a new python file named `hist_plots.py`
+In notebooks, omit `filename` to show inline.
-```
-cd _matplotlib
-touch hist_plots.py
-```
+## Testing (optional but recommended)
-Here we will add our missing piece for a functional matplotlib plot.
+Create a small unit test to exercise the new path end-to-end:
```python
-"""Histogram plot classes."""
-from __future__ import annotations
-
-import logging
+# tests/visualisations/test_histogram.py
+import pathpyG as pp
-from typing import TYPE_CHECKING, Any
+def test_histogram_plot_matplotlib(tmp_path):
+ g = pp.Graph.from_edge_list([("a", "b"), ("b", "c"), ("a", "c")])
+ out = tmp_path / "deg_hist.png"
+ pp.plot(g, kind="histogram", backend="matplotlib", key="degree", bins=3, filename=str(out))
+ assert out.exists()
+```
-# pseudo load class for type checking
-if TYPE_CHECKING:
- from pathpyG.core.graph import Graph
+## Where to look for guidance and consistency
-# create logger
-logger = logging.getLogger("pathpyG")
+- :material-cog-outline: Backends: see other backends like [`Matplotlib`][pathpyG.visualisations._matplotlib] and [`d3.js`][pathpyG.visualisations._d3js] for how plot instances are validated and rendered.
+- :material-database-outline: Plot classes: study [`NetworkPlot`][pathpyG.visualisations.network_plot.NetworkPlot] and [`TemporalNetworkPlot`][pathpyG.visualisations.temporal_network_plot.TemporalNetworkPlot] to understand how [`PathPyPlot`][pathpyG.visualisations.pathpy_plot.PathPyPlot] subclasses fill `self.data` and `self.config`.
+- :material-file-document: The [module overview](/reference/pathpyG/visualisations) explains backend selection, saving, and common styling options.
+## Recap
-def hist(network: Graph, key: str = 'degree', bins: int = 10, **kwargs: Any) -> HistogramPlot:
- """Plot a histogram."""
- return HistogramPlot(network, key, bins, **kwargs)
+- :material-plus-circle: New plots are [`PathPyPlot`][pathpyG.visualisations.pathpy_plot.PathPyPlot] subclasses that prepare data and config.
+- :material-merge: Register your plot in `PLOT_CLASSES` so [`pp.plot(..., kind=...)`][pathpyG.plot] can instantiate it.
+- :material-image-multiple: Extend at least one backend to render your plot type. For histograms, Matplotlib is a clean first target.
+- :material-link-variant: Keep a small, clear data contract between your plot class and backend rendering.
-```
+With this, you have a clean, maintainable path to add new visualisations to pathpyG while leveraging the unified [`pp.plot(...)`][pathpyG.plot] API and existing backend infrastructure.
diff --git a/docs/reference/ignored_modules.yaml b/docs/reference/ignored_modules.yaml
index d54a85b16..e69de29bb 100644
--- a/docs/reference/ignored_modules.yaml
+++ b/docs/reference/ignored_modules.yaml
@@ -1,16 +0,0 @@
-- src/pathpyG/visualisations/_matplotlib/__init__.py
-- src/pathpyG/visualisations/_matplotlib/network_plots.py
-- src/pathpyG/visualisations/_matplotlib/core.py
-- src/pathpyG/visualisations/_tikz/__init__.py
-- src/pathpyG/visualisations/_tikz/network_plots.py
-- src/pathpyG/visualisations/_tikz/core.py
-- src/pathpyG/visualisations/_d3js/__init__.py
-- src/pathpyG/visualisations/_d3js/network_plots.py
-- src/pathpyG/visualisations/_d3js/core.py
-- src/pathpyG/visualisations/_manim/network_plots.py
-- src/pathpyG/visualisations/_manim/core.py
-- src/pathpyG/visualisations/network_plots.py
-- src/pathpyG/visualisations/hist_plots.py
-- src/pathpyG/visualisations/utils.py
-- src/pathpyG/visualisations/layout.py
-
diff --git a/docs/reference/pathpyG/visualisations/_manim/index.md b/docs/reference/pathpyG/visualisations/_manim/index.md
deleted file mode 100644
index 7f9c6d813..000000000
--- a/docs/reference/pathpyG/visualisations/_manim/index.md
+++ /dev/null
@@ -1,122 +0,0 @@
-# Visualisation using Manim
-
-The `_manim` submodule provides plotting tools for creating Manim-based visualisations of temporal networks.
-Designed for use in Jupyter notebooks or standalone rendering, it allows animated plots of temporal graphs
-while offering a wide range of customizable styling options.
-
----
-
-## Classes
-
-### `ManimPlot`
-
-Base class for Manim visualisations integrated with Jupyter notebooks. Defines the interface for rendering and exporting animations from data.
-
-**Methods**
-
-- `show(**kwargs)`: Render and display inline in Jupyter Notebook
-- `save(filename: str, **kwargs)`: Save animation to disk
-
-**kwargs** for saving Manim plots:
-
-- `filename` (`str`): Name the rendered file should be given. This keyword is necessary for saving.
-- `save_as` {`gif`,`mp4`}: Saving format options. Default is `mp4`
-- `save_dir` (`str`): Directory path to save the Output to. Default is current working directory.
-
-For rendering and inline display use the `show()` method instead of `save()`.
-
-
-### `TemporalNetworkPlot`
-
-Animation class for temporal graphs. Supports dynamic layout, time-based color changes, and further customized styling options.
-
-#### Keyword Arguments Overview
-
-| Argument | Type | Default | Short Description |
-|------------------------|------------------|----------|-------------------------------------------------|
-| **General** | | | |
-| `delta` | int | 1000 | Duration of timestep (ms) |
-| `start` | int | 0 | Animation start timestep |
-| `end` | int / None | None | Animation end timestep (last edge by default) |
-| `intervals` | int | None | Number of animation intervals |
-| `dynamic_layout_interval` | int | None | Steps between layout recalculations |
-| `background_color` | str | WHITE | Background color (name, hex, RGB, or Manim) |
-| **Nodes** | | | |
-| `node_size` | float / dict | 0.4 | Radius of nodes (uniform or per-node) |
-| `node_color` | str / list / dict / float / tuple | BLUE | Node fill color or list of colors |
-| `node_cmap` | Colormap | None | Colormap for scalar node values |
-| `node_opacity` | float / dict | 1 | Node fill opacity (0 transparent, 1 solid) |
-| `node_color_timed` | list | None | Color Changes for Nodes at timestep
-| **Edges** | | | |
-| `edge_size` | float / dict | 0.4 | Edge width (uniform or per-edge) |
-| `edge_color` | str / list / dict / float / tuple | GRAY | Edge line color or list of colors |
-| `edge_cmap` | Colormap | None | Colormap for scalar edge values |
-| `edge_opacity` | float / dict | 1 | Edge line opacity (0 transparent, 1 solid) |
-
----
-#### Detailed Descriptions
-
-##### General
-
-- `delta`: Duration (in milliseconds) of each animation timestep.
-- `start`: Starting timestep of the animation sequence.
-- `end`: Ending timestep; defaults to the last timestamp of the input data.
-- `intervals`: Number of discrete animation steps.
-- `dynamic_layout_interval`: How often (in timesteps) the layout recomputes.
-- `background_color`: Background color of the plot, accepts color names, hex codes, RGB tuples, or Manim color constants.
-
-
-##### Nodes
-
-- `node_size`: Node radius; either a single float applied to all nodes or a dictionary with sizes per node ID.
-- `node_color`: Fill color(s) for nodes. Can be a single color string referred to by name (`"blue"`), HEX (`"#ff0000"`), RGB(`(255,0,0)`), float, a list of colors cycling through nodes or a dictionary with color per node in one of the given formats
-- `node_cmap`: Colormap used when node colors are numeric.
-- `node_opacity`: Opacity level for nodes, either uniform or per node.
-- `node_color_timed`: List containing color changes at certain time steps for a certain node. Tuples in the list follow `('node_id',(t, color))` format to indicate for a node with node_id a change in color at time t. Color can be a single color string referred to by name, HEX, RGB or float.
-
-##### Edges
-
-- `edge_size`: Width of edges, can be uniform or specified per edge in a dictionary with size per edge ID.
-- `edge_color`: Color(s) of edges; supports single or multiple colors (see `node_color` above).
-- `edge_cmap`: Colormap used when edge colors are numeric.
-- `edge_opacity`: Opacity for edges, uniform or per edge.
-
----
-
-
-#### Notable Methods
-
-- `construct`: Core method for generating the Manim animation
-- `get_layout()`: Computed node 3D positions based on temporal windows
-- `get_color_at_time()`: Determines a node´s color at a given timestep
-- `compute_edge_index()`: converts input data into `(source, target, time)` tuples
-
-## Usage Example
-```python
-import pathpyG as pp
-
-# Example network data
-tedges = [('a', 'b', 1),('a', 'b', 2), ('b', 'a', 3), ('b', 'c', 3), ('d', 'c', 4), ('a', 'b', 4), ('c', 'b', 4)]
-t = pp.TemporalGraph.from_edge_list(tedges)
-
-# Create temporal plot with custom settings and display inline
-pp.plot(
- t,
- backend= 'manim',
- delta = 5,
- start= 1,
- end = 10,
- background_color = '#f0f0f0',
- node_size = {"a": 0.6, "b": 0.3},
- node_color = ["red", "blue"],
- edge_color = {'a-b-1.0':0.6, 'd-c-4.0':'green'},
- edge_opacity = 0.7,
- node_color_timed = [('a', (1, 'yellow')), ('b', (2, 'blue')), ('c', (4, 0.1)), ('b', 4, (255,0,0))]
-)
-```
-
-## Notes
-
-- The Manim config is adjusted internally for resolution, framerate and format
-
-
diff --git a/docs/reference/pathpyG/visualisations/index.md b/docs/reference/pathpyG/visualisations/index.md
new file mode 100644
index 000000000..5aeaf7cd1
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/index.md
@@ -0,0 +1,480 @@
+# PathpyG Visualisations
+
+This page provides an overview of the available visualisations and the supported backends.
+It also describes which displaying and saving options are available as well as the supported keyword arguments for customized plot styling.
+
+---
+
+## Overview
+
+The main plotting function is `pathpyG.plot()`, which can be used to create visualisations of both static and temporal networks.
+The function supports multiple backends, each with its own capabilities and output formats.
+The backend will be automatically chosen depending on the input data and the specified options.
+
+The default backend is `d3.js`, which is suitable for both static and temporal networks and produces interactive visualisations that can be viewed in a web browser.
+
+!!! example "Interactive Temporal Graph Visualisation with d3.js"
+
+ ```python
+ import pathpyG as pp
+
+ # Example temporal network data
+ tedges = [
+ ("a", "b", 1),
+ ("a", "b", 2),
+ ("b", "a", 3),
+ ("b", "c", 3),
+ ("d", "c", 4),
+ ("a", "b", 4),
+ ("c", "b", 4),
+ ("c", "d", 5),
+ ("b", "a", 5),
+ ("c", "b", 6),
+ ]
+ t = pp.TemporalGraph.from_edge_list(tedges)
+
+ # Create temporal plot and display inline
+ pp.plot(t)
+ ```
+
+
+ ??? example "Interactive Static Graph Visualisation with d3.js"
+
+ ```python
+ import pathpyG as pp
+
+ # Example network data
+ edges = [
+ ("a", "b"),
+ ("a", "c"),
+ ("b", "c"),
+ ("c", "d"),
+ ("d", "e"),
+ ("e", "a"),
+ ]
+ g = pp.Graph.from_edge_list(edges)
+ pp.plot(g)
+ ```
+
+
+### Backends
+
+We currently support a total of four plotting backends, each with different capabilities making them suitable for different use cases.
+The table below provides an overview of the supported backends and their available file formats:
+
+| Backend | Static Networks | Temporal Networks | Available File Formats|
+|---------------|------------|-------------|--------------|
+| **d3.js** | ✔️ | ✔️ | `html` |
+| **manim** | ❌ | ✔️ | `mp4`, `gif` |
+| **matplotlib**| ✔️ | ❌ | `png`, `jpg` |
+| **tikz** | ✔️ | ❌ | `svg`, `pdf`, `tex`|
+
+#### Details
+
+- **d3.js**: The default backend, suitable for both static and temporal networks. It produces interactive visualisations that can be viewed in a web browser.
+- **matplotlib**: A widely used plotting library in Python. It is suitable for static networks and produces raster graphics files.
+- **manim**: A backend specifically designed for creating animations of temporal graphs, producing high-quality video files.
+- **tikz**: A backend for creating publication-quality vector graphics with LaTeX-compatible output or directly compiled output as PDF or SVG.
+
+!!! note
+ The `manim` and the `tikz` backends require additional dependencies to be installed.
+ Please refer to the respective sections in the [Installation Guide](/getting_started/#optional-visualisation-backends) for more information.
+
+## Saving a Plot
+
+You can save plots to files by specifying the `filename` argument in the `pp.plot()` function call.
+The file format will be automatically determined based on the file extension.
+If no filename is provided, the plot will be displayed inline (in a Jupyter notebook or similar environment).
+
+## Customisation
+
+For more advanced visualisations, `PathpyG` offers customisation options for node and edge properties (like `color`, `size`, and `opacity`), as well as support for additional backends, including `manim`, `matplotlib`, and `tikz`.
+We provide some usage examples below, and a detailed overview of the supported keyword arguments for each backend in section [Customisation Options](#customisation-options).
+
+### Visualising Undirected Networks
+
+We provide support for directed and undirected static networks.
+Directed networks are visualised with arrows, while undirected networks use simple lines in all backends.
+We provide an example using `matplotlib` below.
+
+!!! example "Undirected Static Graph Visualisation with `matplotlib`"
+
+ You will see below that compared to the examples above, the nodes do not have arrows indicating directionality.
+ ```python
+ import torch
+ import pathpyG as pp
+
+ # Example undirected network data
+ edge_index = torch.tensor([[0, 1, 3, 3], [1, 2, 1, 0]])
+ g = pp.Graph.from_edge_index(edge_index).to_undirected()
+
+ # Create static plot and display inline
+ pp.plot(g, backend="matplotlib")
+ ```
+
+
+ !!! tip "Node Labels"
+ In the above picture, the nodes do not have labels.
+ This is because labels are automatically generated based on the node IDs provided in `g.mapping.node_ids`.
+ When we created the graph using the `from_edge_index()` method, we did not provide any specific node IDs, so no IDs were assigned and no labels were generated.
+ You can override the default behaviour by specifying `show_labels=True` in the `pp.plot()` function call.
+
+### Node and Edge Customisation
+
+You can customise the appearance of nodes and edges in both static and temporal networks.
+We describe the different options below.
+
+#### Static Networks
+
+
+In all backends, you can customise the `size`, `color`, and `opacity` of nodes and edges.
+You can specify these properties either as attributes of the `PyG` graph object `PathpyG.Graph.data` (as `torch.Tensor` or `numpy.ndarray` with one value per node/edge) or as arguments in the `pp.plot()` function call in three different ways: (1)
+
+- A single value (applied uniformly to all nodes/edges)
+- A list of values with length equal to the number of nodes/edges (values are applied in order)
+- A dictionary mapping node/edge IDs to values (values are applied based on the IDs)
+
+For `color`, you can use color names (e.g., `"blue"`), HEX codes (e.g., `"#ff0000"`), or RGB tuples (e.g., `(255, 0, 0)`).
+You can also pass numeric values, which will be mapped to colors using a `matplotlib` colormap (specified via `cmap`).
+
+
+1. If both the graph attribute and the function argument are provided, the function argument takes precedence.
+
+!!! example "Custom Node and Edge Properties"
+
+ In the example below, we set custom properties for nodes and edges using all three methods.
+ ```python
+ import torch
+ import pathpyG as pp
+
+ # Example network data
+ edges = [
+ ("a", "b"),
+ ("a", "c"),
+ ("b", "d"),
+ ("c", "d"),
+ ("d", "a"),
+ ]
+ g = pp.Graph.from_edge_list(edges)
+
+ # Add properties as attributes to the graph
+ g.data["node_size"] = torch.tensor([10, 15, 20, 15])
+ g.data["edge_color"] = torch.tensor([0, 1, 2, 1, 0])
+ g.data["node_opacity"] = torch.zeros(g.n)
+
+ # Create static plot with custom settings and display inline
+ pp.plot(
+ g,
+ backend="tikz",
+ node_color={"a": "red", "b": "#00FF00"},
+ edge_opacity={("a", "b"): 0.1, ("a", "c"): 0.5, ("b", "d"): 1.0},
+ node_opacity=1.0, # override graph attribute
+ edge_size=torch.tensor([1, 2, 3, 2, 1]),
+ )
+ ```
+
+
+ ??? tip "Display Images inside your Nodes"
+ `d3.js` additionally supports images as node representations.
+ You can specify the image source using the `node_image` argument.
+ The image source can be a URL or a local file path.
+ ```python
+ import torch
+ import pathpyG as pp
+
+ # Example network data
+ edges = [
+ ("b", "a"),
+ ("c", "a"),
+ ]
+ mapping = pp.IndexMap(["a", "b", "c", "d"])
+ g = pp.Graph.from_edge_list(edges, mapping=mapping)
+ g.data["node_size"] = torch.tensor([25]*4)
+ pp.plot(
+ g,
+ node_size={"d": 50},
+ edge_size=5,
+ node_image={
+ "a": "https://avatars.githubusercontent.com/u/52822508?s=48&v=4",
+ "b": "https://raw.githubusercontent.com/pyg-team/pyg_sphinx_theme/master/pyg_sphinx_theme/static/img/pyg_logo.png",
+ "c": "https://pytorch-geometric.readthedocs.io/en/latest/_static/img/pytorch_logo.svg",
+ "d": "docs/img/pathpy_logo_new.png",
+ },
+ show_labels=False,
+ )
+ ```
+
+
+#### Temporal Networks
+
+For temporal networks, you can also customise the `size`, `color`, and `opacity` of nodes and edges at each timestep.
+In our understanding, a temporal network has a fixed set of nodes, but edges appear at different timesteps.
+Thus, all nodes exist at all times, but edges may only exist at certain timesteps.
+Therefore, edge properties can be specified for each timestep where the edge exists.
+In contrast, node properties can change at specified points in time, but will remain the same for all subsequent timesteps until they are changed again.
+
+The customisation options work similarly to static networks, with the exception that passing a dictionary for node/edge properties requires adding the timestep to the key:
+
+!!! example "Custom Node and Edge Properties in Temporal Networks"
+ In the example below, we set the starting `node_color` and `node_size` for all nodes using graph attributes.
+ We further customise the `edge_color` for each edge at each timestep using a graph attribute.
+ Next, we override the `node_color` for node `"b"` at timestep `2` and for node `"a"` from the start using function arguments.
+ Finally, we use a dictionary with a tuple consisting of the source node, target node, and timestep to set the `edge_size` for two specific edges at specific timesteps.
+
+ ```python
+ import torch
+ import numpy as np
+ import pathpyG as pp
+
+ # Example temporal network data
+ tedges = [
+ ("a", "b", 1),
+ ("a", "b", 2),
+ ("b", "a", 3),
+ ("b", "c", 3),
+ ]
+ t = pp.TemporalGraph.from_edge_list(tedges)
+ t.data["node_size"] = torch.tensor([15, 8, 19])
+ t.data["node_color"] = np.array(["blue", "green", "orange"])
+ t.data["edge_color"] = torch.tensor([0, 1, 2, 1])
+
+ # Create temporal plot and display inline
+ pp.plot(
+ t,
+ backend="manim",
+ node_opacity=0.5,
+ edge_size={("a", "b", 1): 10, ("a", "b", 2): 1},
+ node_color={("b", 2): "red", "a": "purple"}, # node_color for node 'a' is set to 'purple' from the start
+ )
+ ```
+
+
+
+
+## Layouts
+
+By default, `PathpyG` uses the Fruchterman-Reingold force-directed algorithm to compute node positions for static networks.
+For temporal networks, the layout is computed dynamically at each timestep using the `d3.js` backend, while the `manim` backend uses a Fruchterman-Reingold layout computed on the aggregated static network by default.
+
+### Static Networks
+You can change the layout algorithm for static networks using the `layout` argument in the `pp.plot()` function call.
+
+**networkx layouts:**
+
+We currently support most layouts via the `networkx` library.
+See the examples below for usage.
+
+=== "Random"
+
+ Use `"random"`, `"rand"` or `None` to specify a random layout.
+ ```python
+ import pathpyG as pp
+ from torch_geometric import seed_everything
+ seed_everything(42)
+
+ g = pp.algorithms.generative_models.watts_strogatz(30, 2, 0.25)
+ pp.plot(g, backend="tikz", layout="random")
+ ```
+
+
+=== "Circular"
+
+ Use `"circular"`, `"circle"`, `"ring"`, `"1d-lattice"`, or `"lattice-1d"` to specify a circular layout.
+ ```python
+ import pathpyG as pp
+ from torch_geometric import seed_everything
+ seed_everything(42)
+
+ g = pp.algorithms.generative_models.watts_strogatz(30, 2, 0.25)
+ pp.plot(g, backend="tikz", layout="circular")
+ ```
+
+
+=== "Shell"
+
+ Use `"shell"`, `"concentric"`, `"concentric-circles"`, or `"shell layout"` to specify a shell layout.
+ ```python
+ import pathpyG as pp
+ from torch_geometric import seed_everything
+ seed_everything(42)
+
+ g = pp.algorithms.generative_models.watts_strogatz(30, 2, 0.25)
+ pp.plot(g, backend="tikz", layout="shell")
+ ```
+
+
+=== "Spectral"
+
+ Use `"spectral"`, `"eigen"`, or `"spectral layout"` to specify a spectral layout.
+ ```python
+ import pathpyG as pp
+ from torch_geometric import seed_everything
+ seed_everything(42)
+
+ g = pp.algorithms.generative_models.watts_strogatz(30, 2, 0.25)
+ pp.plot(g, backend="tikz", layout="spectral")
+ ```
+
+
+=== "Kamada-Kawai"
+
+ Use `"kamada-kawai"`, `"kamada_kawai"`, `"kk"`, `"kamada"`, or `"kamada layout"` to specify a Kamada-Kawai layout.
+ ```python
+ import pathpyG as pp
+ from torch_geometric import seed_everything
+ seed_everything(42)
+
+ g = pp.algorithms.generative_models.watts_strogatz(30, 2, 0.25)
+ pp.plot(g, backend="tikz", layout="kamada-kawai")
+ ```
+
+
+=== "Fruchterman-Reingold"
+
+ Use `"fruchterman-reingold"`, `"fruchterman_reingold"`, `"fr"`, `"spring_layout"`, `"spring layout"`, or `"spring"` to specify a Fruchterman-Reingold layout.
+ ```python
+ import pathpyG as pp
+ from torch_geometric import seed_everything
+ seed_everything(42)
+
+ g = pp.algorithms.generative_models.watts_strogatz(30, 2, 0.25)
+ pp.plot(g, backend="tikz", layout="fruchterman-reingold")
+ ```
+
+
+=== "ForceAtlas2"
+
+ Use `"forceatlas2"`, `"fa2"`, `"forceatlas"`, `"force-atlas"`, `"force-atlas2"`, or `"fa 2"` to specify a ForceAtlas2 layout.
+ ```python
+ import pathpyG as pp
+ from torch_geometric import seed_everything
+ seed_everything(42)
+
+ g = pp.algorithms.generative_models.watts_strogatz(30, 2, 0.25)
+ pp.plot(g, backend="tikz", layout="forceatlas2")
+ ```
+
+
+**Other layouts:**
+In addition to the `networkx` layouts, we also support:
+
+- Grid layout
+
+ ??? example
+
+ ```python
+ import pathpyG as pp
+ from torch_geometric import seed_everything
+ seed_everything(42)
+
+ g = pp.algorithms.generative_models.watts_strogatz(30, 2, 0.25)
+ pp.plot(g, backend="tikz", layout="grid", filename="tikz_grid_layout.svg")
+ ```
+
+
+- Custom layout (by providing a dictionary mapping node IDs to positions)
+
+ ??? example
+
+ ```python
+
+ import pathpyG as pp
+
+ g = pp.Graph.from_edge_list([("a", "b"), ("a", "c"), ("b", "d"), ("c", "d"), ("d", "a")])
+ # Provide custom x and y coordinates for a layout
+ layout = {
+ "a": (0, 0),
+ "b": (1, 0),
+ "c": (0, 1),
+ "d": (1, 1)
+ }
+ pp.plot(g, backend="tikz", layout=layout, filename="tikz_layout.svg")
+ ```
+
+
+### Temporal Networks
+
+We apply a sliding window approach to compute layouts for temporal networks.
+At each timestep, we consider a window of past and future timesteps (controlled via the `layout_window_size` argument) and aggregate all edges inside this window to a static graph to compute the layout.
+You can either pass a fixed integer value, which will then be split equally into past and future timesteps, or a tuple specifying the number of past and future timesteps separately.
+The layout algorithm can be any of the supported static layout algorithms described above.
+
+!!! example "Custom Layout for Temporal Networks"
+
+ In the example below, we use a sliding window of `2`, meaning that we aggregate the current and one previous timestep to compute the layout at each timestep.
+ ```python
+ import pathpyG as pp
+
+ # Example temporal network data
+ tedges = [
+ ("a", "b", 1),
+ ("a", "b", 2),
+ ("b", "a", 3),
+ ("b", "c", 3),
+ ("d", "c", 4),
+ ("a", "b", 4),
+ ("c", "b", 4),
+ ("c", "d", 5),
+ ("b", "a", 5),
+ ("c", "b", 6),
+ ]
+ t = pp.TemporalGraph.from_edge_list(tedges)
+
+ # Create temporal plot and display inline
+ pp.plot(t, backend="manim", layout_window_size=2, layout="fa2")
+ ```
+
+
+
+
+## Customisation Options
+
+Below is full list of supported keyword arguments for each backend and their descriptions.
+
+| Argument | d3.js | manim | matplotlib | tikz | Short Description |
+| ------------------------- | :-----: | :-----: | :-----: | :-----: | --------------------------------------------- |
+| **General** | | | | | |
+| `default_backend` | ✔️ | ✔️ | ✔️ | ✔️ | Backend to use when none is specified |
+| `cmap` | ✔️ | ✔️ | ✔️ | ✔️ | Colormap (string that refers to matplotlib cmap) for scalar node/edge values |
+| `layout` | ✔️ | ✔️ | ✔️ | ✔️ | Layout algorithm for static networks (see [Layouts](#layouts)) |
+| `width` | ✔️ | ❌ | ✔️ | ✔️ | Width of the output |
+| `height` | ✔️ | ❌ | ✔️ | ✔️ | Height of the output |
+| `latex_class_options` | ❌ | ❌ | ❌ | ✔️ | LaTeX document class options (e.g., `"border=2mm"`) for `tikz` backend |
+| `margin` | ✔️ | ❌ | ✔️ | ✔️ | Margin around the plot area (in pixels for `d3.js`, in points for `matplotlib` and `tikz`) |
+| `curvature` | ✔️ | ❌ | ❌ | ✔️ | Curvature of edges (0: straight, >0: curved) |
+| `layout_window_size` | ✔️ | ✔️ | ❌ | ❌ | Size of sliding window for temporal network layouts (int or tuple of int) |
+| `delta` | ✔️ | ✔️ | ❌ | ❌ | Duration of timestep in milliseconds (ms) |
+| `separator` | ✔️ | ✔️ | ✔️ | ✔️ | Separator for higher-order node labels |
+| **Nodes** | | | | | |
+| `size` | ✔️ | ✔️ | ✔️ | ✔️ | Radius of nodes (uniform or per-node) |
+| `color` | ✔️ | ✔️ | ✔️ | ✔️ | Node fill color |
+| `opacity` | ✔️ | ✔️ | ✔️ | ✔️ | Node fill opacity (0 transparent, 1 solid) |
+| `image_padding` | ✔️ | ❌ | ❌ | ❌ | Padding around node images (in pixels) |
+| **Edges** | | | | | |
+| `size` | ✔️ | ✔️ | ✔️ | ✔️ | Edge width (uniform or per-edge) |
+| `color` | ✔️ | ✔️ | ✔️ | ✔️ | Edge line color |
+| `opacity` | ✔️ | ✔️ | ✔️ | ✔️ | Edge line opacity (0 transparent, 1 solid) |
+
+**Legend:** ✔️ Supported ❌ Not Supported
+
+You can find the default values for each argument in `pathpyG.toml` located in the `pathpyG` installation directory.
+
+!!! note "Node and Edge Keyword Arguments"
+
+ The node and edge keyword arguments listed above represent the default options that are specified via the `pathpyG.toml` configuration file.
+ You can change these defaults using keyword arguments in the `pp.plot()` function call as follows:
+ ```python
+ import pathpyG as pp
+
+ # Example network data
+ g = pp.Graph.from_edge_list([("a", "b"), ("a", "c")])
+
+ # Create network plot and display inline
+ pp.plot(g, node={"opacity": 0.2}, filename="d3js_node_opacity.html")
+ ```
+
+
+ However, if you want to change either `color`, `size`, or `opacity` for nodes or edges, the preferred way is to use the dedicated keyword arguments described in the previous sections.
+
+---
+For more details and usage examples, see [Manim Visualisation Tutorial](/tutorial/manim_tutorial),[Visualisation Tutorial](/tutorial/visualisation) and [Develop your own plot Functions](/plot_tutorial)
diff --git a/docs/reference/pathpyG/visualisations/plot.md b/docs/reference/pathpyG/visualisations/plot.md
deleted file mode 100644
index 2c5823ade..000000000
--- a/docs/reference/pathpyG/visualisations/plot.md
+++ /dev/null
@@ -1,186 +0,0 @@
-# PathpyG Visualisations
-
-This page provides an overview of the available visualisations and the supported backends.
-It also describes which displaying and saving options are available as well as the supported keyword arguments for customized plot styling.
-
----
-
-**Methods**
-
-- `show(**kwargs)`: Show Visualisation
-- `save(filename: str, **kwargs)`: Save Visualisation to hard drive
-
-**kwargs** for saving Manim plots:
-
-- `filename` (`str`): Name to assign to the output file. This keyword is necessary for saving.
-
-For display use the `show()` method instead of `save()`.
-
-
-## Supported Features by Backend
-
-| Backend | Static Networks | Temporal Networks | Available File Formats|
-|---------------|------------|-------------|----------------------|
-| **d3.js** | ✔️ | ✔️ | `svg`, `html`, `json` (dynamic) |
-| **manim** | ❌ | ✔️ | `mp4`, `gif` |
-| **matplotlib**| ✔️ | ❌ | `png`, `svg`, `pdf`, etc.|
-| **tikz** | ✔️ | ❌ | `pdf`, `tex`|
-
-
-
-## Keyword Arguments Overview
-| Argument | d3.js | manim | matplotlib | tikz | Short Description |
-| ------------------------- | :-----: | :-----: | :-----: | :-----: | --------------------------------------------- |
-| **General** | | | | | |
-| `delta` | ✔️ | ✔️ | ❌ | | Duration of timestep (ms) |
-| `start` | ✔️ | ✔️ | ❌ | | Animation start timestep |
-| `end` | ✔️ | ✔️ | ❌ | | Animation end timestep (last edge by default) |
-| `intervals` | ✔️ | ✔️ | ❌ | | Number of animation intervals |
-| `dynamic_layout_interval` | ❌ | ✔️ | ❌ | | Steps between layout recalculations |
-| `background_color` | ❌ | ✔️ | ❌ | | Background color (name, hex, RGB) |
-| `width` | ✔️ | ❌ | ❌ | | Width of the output |
-| `height` | ✔️ | ❌ | ❌ | | Height of the output |
-| `lookahead` | ❌ | ✔️ | ❌ | ❌ | for layout computation |
-| `lookbehind` | ❌ | ✔️ | ❌ | ❌ | for layout computation |
-| **Nodes** | | | | | |
-| `node_size` | ✔️ | ✔️ | ✔️ | ✔️ | Radius of nodes (uniform or per-node) |
-| `node_color` | 🟨 | ✔️ | 🟨 | 🟨 | Node fill color |
-| `node_cmap` | ✔️ | ✔️ | ✔️ | ✔️ | Colormap for scalar node values |
-| `node_opacity` | ✔️ | ✔️ | ✔️ | ✔️ | Node fill opacity (0 transparent, 1 solid) |
-| `node_label` | ✔️ | ✔️ | ❌ | | Label text shown with nodes |
-| **Edges** | | | | | |
-| `edge_size` | ✔️ | ✔️ | ✔️ | ✔️ | Edge width (uniform or per-edge) |
-| `edge_color` | ✔️ | ✔️ | ✔️ | ✔️ | Edge line color |
-| `edge_cmap` | ✔️ | ✔️ | ✔️ | ✔️ | Colormap for scalar edge values |
-| `edge_opacity` | ✔️ | ✔️ | ✔️ | ✔️ | Edge line opacity (0 transparent, 1 solid) |
-
-**Legend:** ✔️ Supported 🟨 Partially Supported ❌ Not Supported
-
-### Detailed Description of Keywords
-The default values may differ for each individual Backend.
-
-#### General
-
-- `delta` (int): Duration (in milliseconds) of each animation timestep.
-- `start` (int): Starting timestep of the animation sequence.
-- `end`(int or None): Ending timestep; defaults to the last timestamp of the input data.
-- `intervals`(int): Number of discrete animation steps.
-- `dynamic_layout_interval` (int): How often (in timesteps) the layout recomputes.
-- `background_color`(str or tuple): Background color of the plot, accepts color names, hex codes or RGB tuples.
-- `width` (int): width of the output
-- `height` (int): height of the output
-- `look_ahead` (int): timesteps in the future to include while calculating layout
-- `look_behind` (int): timesteps into the past to include while calculating layout
-
-
-
-#### Nodes
-
-- `node_size`: Node radius; either a single float applied to all nodes or a dictionary with sizes per node ID.
-- `node_color`: Fill color(s) for nodes. Can be a single color string referred to by name (`"blue"`), HEX (`"#ff0000"`), RGB(`(255,0,0)`), float, a list of colors cycling through nodes or a dictionary with color per node in one of the given formats.
-**Manim** additionally supports timed node color changes in the format `{"node_id-timestep": color}` (i.e. `{a-2.0" : "yellow"}`)
-- `node_cmap`: Colormap used when node colors are numeric.
-- `node_opacity`: Opacity level for nodes, either uniform or per node.
-- `node_label` (dict): Assign text labels to nodes
-
-#### Edges
-
-- `edge_size`: Width of edges, can be uniform or specified per edge in a dictionary with size per edge ID.
-- `edge_color`: Color(s) of edges; supports single or multiple colors (see `node_color` above).
-- `edge_cmap`: Colormap used when edge colors are numeric.
-- `edge_opacity`: Opacity for edges, uniform or per edge.
-
----
-## Usage Examples
-**d3.js**
-```python
-import pathpyG as pp
-
-# Example network data
-tedges = [('a', 'b', 1),('a', 'b', 2), ('b', 'a', 3), ('b', 'c', 3), ('d', 'c', 4), ('a', 'b', 4), ('c', 'b', 4),
- ('c', 'd', 5), ('b', 'a', 5), ('c', 'b', 6)]
-t = pp.TemporalGraph.from_edge_list(tedges)
-
-# Create temporal plot with custom settings and display inline
-pp.plot(
- t,
- backend= 'd3js',
- node_size = {"a": 15, "b": 5},
- node_color = "grey",
- edge_opacity = 0.7,
-)
-```
-
-
-
-
-**manim**
-```python
-import pathpyG as pp
-
-# Example network data
-tedges = [('a', 'b', 1),('a', 'b', 2), ('b', 'a', 3), ('b', 'c', 3), ('d', 'c', 4), ('a', 'b', 4), ('c', 'b', 4),
- ('c', 'd', 5), ('b', 'a', 5), ('c', 'b', 6)]
-t = pp.TemporalGraph.from_edge_list(tedges)
-
-# Create temporal plot with custom settings and display inline
-pp.plot(
- t,
- backend="manim",
- dynamic_layout_interval=1,
- edge_color={"b-a-3.0": "red", "c-b-4.0": (220,30,50)},
- node_color = {"c-3.0" : "yellow"},
- edge_size=6,
- node_label={"a": "a", "b": "b", "c": "c", "d" : "d"},
- font_size=20,
-)
-```
-
-
-
-
-
-
-
-
-**matplotlib**
-```python
-import pathpyG as pp
-
-# Example network data (static)
-g = Graph.from_edge_index(torch.tensor([[0,1,0], [2,2,1]]))
-
-# Create static plot with custom settings and display inline
-pp.plot(
- g,
- backend= 'matplotlib',
- edge_color= "grey",
- node_color = "blue"
-)
-```
-
-
-
-
-
-
----
-For more details and usage examples, see [Manim Visualisation Tutorial](/tutorial/manim_tutorial),[Visualisation Tutorial](/tutorial/visualisation) and [Develop your own plot Functions](/plot_tutorial)
diff --git a/docs/reference/pathpyG/visualisations/plot/d3js_custom_node_images.html b/docs/reference/pathpyG/visualisations/plot/d3js_custom_node_images.html
new file mode 100644
index 000000000..575799af2
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/d3js_custom_node_images.html
@@ -0,0 +1,506 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/d3js_demo.html b/docs/reference/pathpyG/visualisations/plot/d3js_demo.html
deleted file mode 100644
index 3a958b429..000000000
--- a/docs/reference/pathpyG/visualisations/plot/d3js_demo.html
+++ /dev/null
@@ -1,405 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/d3js_node_opacity.html b/docs/reference/pathpyG/visualisations/plot/d3js_node_opacity.html
new file mode 100644
index 000000000..b615a7ac2
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/d3js_node_opacity.html
@@ -0,0 +1,506 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/d3js_static.html b/docs/reference/pathpyG/visualisations/plot/d3js_static.html
new file mode 100644
index 000000000..0ca847239
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/d3js_static.html
@@ -0,0 +1,506 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/d3js_temporal.html b/docs/reference/pathpyG/visualisations/plot/d3js_temporal.html
new file mode 100644
index 000000000..13794f4b9
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/d3js_temporal.html
@@ -0,0 +1,628 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/dynamic_network.mp4 b/docs/reference/pathpyG/visualisations/plot/dynamic_network.mp4
new file mode 100644
index 000000000..4784a5920
Binary files /dev/null and b/docs/reference/pathpyG/visualisations/plot/dynamic_network.mp4 differ
diff --git a/docs/reference/pathpyG/visualisations/plot/graph.png b/docs/reference/pathpyG/visualisations/plot/graph.png
new file mode 100644
index 000000000..bc105ffd9
Binary files /dev/null and b/docs/reference/pathpyG/visualisations/plot/graph.png differ
diff --git a/docs/reference/pathpyG/visualisations/plot/manim_custom_properties.gif b/docs/reference/pathpyG/visualisations/plot/manim_custom_properties.gif
new file mode 100644
index 000000000..d770bb52c
Binary files /dev/null and b/docs/reference/pathpyG/visualisations/plot/manim_custom_properties.gif differ
diff --git a/docs/reference/pathpyG/visualisations/plot/manim_temporal_fa2.gif b/docs/reference/pathpyG/visualisations/plot/manim_temporal_fa2.gif
new file mode 100644
index 000000000..4ca9edca8
Binary files /dev/null and b/docs/reference/pathpyG/visualisations/plot/manim_temporal_fa2.gif differ
diff --git a/docs/reference/pathpyG/visualisations/plot/matplotlib_undirected.png b/docs/reference/pathpyG/visualisations/plot/matplotlib_undirected.png
new file mode 100644
index 000000000..f68aec539
Binary files /dev/null and b/docs/reference/pathpyG/visualisations/plot/matplotlib_undirected.png differ
diff --git a/docs/reference/pathpyG/visualisations/plot/network.png b/docs/reference/pathpyG/visualisations/plot/network.png
new file mode 100644
index 000000000..2caa0d7fe
Binary files /dev/null and b/docs/reference/pathpyG/visualisations/plot/network.png differ
diff --git a/docs/reference/pathpyG/visualisations/plot/simple_network.html b/docs/reference/pathpyG/visualisations/plot/simple_network.html
new file mode 100644
index 000000000..aecd53e01
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/simple_network.html
@@ -0,0 +1,506 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/temporal_network.gif b/docs/reference/pathpyG/visualisations/plot/temporal_network.gif
new file mode 100644
index 000000000..1b430d928
Binary files /dev/null and b/docs/reference/pathpyG/visualisations/plot/temporal_network.gif differ
diff --git a/docs/reference/pathpyG/visualisations/plot/temporal_network.html b/docs/reference/pathpyG/visualisations/plot/temporal_network.html
new file mode 100644
index 000000000..c4d06c2e9
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/temporal_network.html
@@ -0,0 +1,628 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/temporal_network.mp4 b/docs/reference/pathpyG/visualisations/plot/temporal_network.mp4
new file mode 100644
index 000000000..70391de9e
Binary files /dev/null and b/docs/reference/pathpyG/visualisations/plot/temporal_network.mp4 differ
diff --git a/docs/reference/pathpyG/visualisations/plot/tikz_backend_example.svg b/docs/reference/pathpyG/visualisations/plot/tikz_backend_example.svg
new file mode 100644
index 000000000..6136d2cbb
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/tikz_backend_example.svg
@@ -0,0 +1,35 @@
+
+
+
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/tikz_circle_layout.svg b/docs/reference/pathpyG/visualisations/plot/tikz_circle_layout.svg
new file mode 100644
index 000000000..391d9f748
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/tikz_circle_layout.svg
@@ -0,0 +1,131 @@
+
+
+
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/tikz_custom_properties.svg b/docs/reference/pathpyG/visualisations/plot/tikz_custom_properties.svg
new file mode 100644
index 000000000..0278ac7a7
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/tikz_custom_properties.svg
@@ -0,0 +1,45 @@
+
+
+
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/tikz_fa2_layout.svg b/docs/reference/pathpyG/visualisations/plot/tikz_fa2_layout.svg
new file mode 100644
index 000000000..b2107dbbc
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/tikz_fa2_layout.svg
@@ -0,0 +1,131 @@
+
+
+
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/tikz_grid_layout.svg b/docs/reference/pathpyG/visualisations/plot/tikz_grid_layout.svg
new file mode 100644
index 000000000..9d4bc101d
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/tikz_grid_layout.svg
@@ -0,0 +1,131 @@
+
+
+
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/tikz_init_advanced.svg b/docs/reference/pathpyG/visualisations/plot/tikz_init_advanced.svg
new file mode 100644
index 000000000..1e93a28e3
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/tikz_init_advanced.svg
@@ -0,0 +1,43 @@
+
+
+
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/tikz_init_basic.svg b/docs/reference/pathpyG/visualisations/plot/tikz_init_basic.svg
new file mode 100644
index 000000000..f2a5b8732
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/tikz_init_basic.svg
@@ -0,0 +1,37 @@
+
+
+
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/tikz_kk_layout.svg b/docs/reference/pathpyG/visualisations/plot/tikz_kk_layout.svg
new file mode 100644
index 000000000..9727d75cc
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/tikz_kk_layout.svg
@@ -0,0 +1,131 @@
+
+
+
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/tikz_layout.svg b/docs/reference/pathpyG/visualisations/plot/tikz_layout.svg
new file mode 100644
index 000000000..7fad67811
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/tikz_layout.svg
@@ -0,0 +1,45 @@
+
+
+
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/tikz_random_layout.svg b/docs/reference/pathpyG/visualisations/plot/tikz_random_layout.svg
new file mode 100644
index 000000000..b333f9280
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/tikz_random_layout.svg
@@ -0,0 +1,131 @@
+
+
+
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/tikz_shell_layout.svg b/docs/reference/pathpyG/visualisations/plot/tikz_shell_layout.svg
new file mode 100644
index 000000000..db6f8b959
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/tikz_shell_layout.svg
@@ -0,0 +1,131 @@
+
+
+
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/tikz_spectral_layout.svg b/docs/reference/pathpyG/visualisations/plot/tikz_spectral_layout.svg
new file mode 100644
index 000000000..ab945aac5
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/tikz_spectral_layout.svg
@@ -0,0 +1,131 @@
+
+
+
\ No newline at end of file
diff --git a/docs/reference/pathpyG/visualisations/plot/tikz_spring_layout.svg b/docs/reference/pathpyG/visualisations/plot/tikz_spring_layout.svg
new file mode 100644
index 000000000..15170e6e9
--- /dev/null
+++ b/docs/reference/pathpyG/visualisations/plot/tikz_spring_layout.svg
@@ -0,0 +1,131 @@
+
+
+
\ No newline at end of file
diff --git a/docs/tutorial/manim_tutorial.ipynb b/docs/tutorial/manim_tutorial.ipynb
index c231df3d1..9330377b2 100644
--- a/docs/tutorial/manim_tutorial.ipynb
+++ b/docs/tutorial/manim_tutorial.ipynb
@@ -16,15 +16,15 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 1,
"id": "26f1e825",
"metadata": {},
"outputs": [],
"source": [
- "%%capture\n",
- "# !pip install torch\n",
- "!pip install torch_geometric\n",
- "!pip install git+https://github.com/pathpy/pathpyG.git"
+ "# %%capture\n",
+ "# # !pip install torch\n",
+ "# !pip install torch_geometric\n",
+ "# !pip install git+https://github.com/pathpy/pathpyG.git"
]
},
{
@@ -52,7 +52,7 @@
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 1,
"id": "3771679e",
"metadata": {},
"outputs": [],
@@ -111,18 +111,18 @@
"name": "stderr",
"output_type": "stream",
"text": [
- "100%|██████████| 21/21 [00:29<00:00, 1.39s/it]\n"
+ " \r"
]
},
{
"data": {
"text/html": [
"\n",
- " \n",
- " "
+ " \n",
+ " "
],
"text/plain": [
""
@@ -134,7 +134,7 @@
{
"data": {
"text/plain": [
- ""
+ ""
]
},
"execution_count": 3,
@@ -143,7 +143,7 @@
}
],
"source": [
- "pp.plot(t, backend=\"manim\", node_size=0.3, edge_size=4, edge_color=[\"red\", \"blue\"], node_color=0.89, node_label=\"Node\")"
+ "pp.plot(t, backend=\"manim\", node_size=20, edge_size=10, edge_color=\"red\", node_color=\"gray\")"
]
},
{
@@ -156,37 +156,18 @@
},
{
"cell_type": "code",
- "execution_count": 4,
+ "execution_count": null,
"id": "8e80f87a",
"metadata": {},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "100%|██████████| 21/21 [00:31<00:00, 1.48s/it]\n"
- ]
- },
- {
- "data": {
- "text/plain": [
- ""
- ]
- },
- "execution_count": 4,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
+ "outputs": [],
"source": [
"pp.plot(\n",
" t,\n",
" backend=\"manim\",\n",
- " node_size=0.3,\n",
+ " node_size=20,\n",
" edge_size=4,\n",
- " edge_color=[\"red\", \"blue\"],\n",
- " node_color=0.89,\n",
- " node_label=\"Node\",\n",
+ " edge_color=\"red\",\n",
+ " node_color=\"gray\",\n",
" filename=\"tutorial_plot.gif\",\n",
")"
]
@@ -196,26 +177,19 @@
"id": "7016626b",
"metadata": {},
"source": [
- "## Dynamically Changing Customisations\n",
+ "## Dynamic Customisations and Layout\n",
"\n",
- "So far, we only used customisations that you were already familiar with from other plotting backends.\n",
- "Manim offers four additional customisations:\n",
- "1. Both node and edge colors can be specified for single time stamps with the `node_color` and `edge_color` keyword arguments.\n",
+ "Since we use `manim` to animate temporal networks, we can also use dynamic customisations that change over time. For example, we can change the `node_size` or `edge_size` over time.\n",
+ "To change those properties dynamically, we can provide a dictionary that maps node/source-target id and the time step to the desired property value.\n",
"\n",
- " e.g. : `node_color = {'a-1.0': 'yellow', 'a': 'green', 'b-3.0':'black'}`, where the keys `node_id-time_stamp` specify the node and the time stamp and the values the colors. If no time stamp is specified the first time stamp of the temporal graph is used.\n",
- "\n",
- " e.g. : `edge_color = {'a-b-1.0':'purple', 'd-c-4.0':'green'}`, where the keys `node_id_1-node_id_2-time_stamp` specify the nodes and the time stamp and the values the colors\n",
- "\n",
- "2. Additionally, the user can specify after how many time steps the layout is recalculated with the Fruchtermann Rheingold algorithm based on the edges that existed in the last interval with the `dynamic_layout_interval` keyword argument. Additionally the user can specify based on how many timesteps backwards and forwards the new layout is calculated with the keyword arguments `look_forward` and `look_behind`\n",
- "3. The background color can be changed with the `background_color` keyword argument\n",
- "4. The font size of the node labels is adjustable with the `font_size` keyword argument.\n",
+ "Additionally, we can use dynamic layouts that are based on sliding windows. For example, we can use a `ForceAtlas2` layout that is computed based on a sliding window of the last 3 time steps. This allows us to have a layout that changes over time but is still stable enough to be visually appealing.\n",
"\n",
"The following shows an example where all of the customisations described above are used:"
]
},
{
"cell_type": "code",
- "execution_count": 5,
+ "execution_count": null,
"id": "26a31185",
"metadata": {},
"outputs": [
@@ -223,18 +197,18 @@
"name": "stderr",
"output_type": "stream",
"text": [
- "100%|██████████| 21/21 [00:28<00:00, 1.36s/it]\n"
+ " \r"
]
},
{
"data": {
"text/html": [
"\n",
- " \n",
- " "
+ " \n",
+ " "
],
"text/plain": [
""
@@ -246,10 +220,10 @@
{
"data": {
"text/plain": [
- ""
+ ""
]
},
- "execution_count": 5,
+ "execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
@@ -258,13 +232,11 @@
"pp.plot(\n",
" t,\n",
" backend=\"manim\",\n",
- " node_size=0.1,\n",
- " edge_size=4,\n",
- " edge_color={\"a-b-1.0\": \"purple\", \"b-c-10.0\": \"green\"},\n",
- " node_color={\"a-1.0\": \"yellow\", \"a\": \"green\", \"b-3.0\": \"black\"},\n",
- " node_label=t.mapping.node_ids.tolist(),\n",
- " font_size=40,\n",
- " dynamic_layout_interval=5,\n",
+ " layout_window_size=[3, 1],\n",
+ " layout=\"fa2\",\n",
+ " node_size=12,\n",
+ " edge_color={(\"b\", \"c\", 10): \"green\"},\n",
+ " node_color={(\"a\", 3): \"yellow\", (\"a\", 0): \"green\", (\"b\", 3): \"black\"},\n",
")"
]
},
@@ -280,7 +252,7 @@
},
{
"cell_type": "code",
- "execution_count": 6,
+ "execution_count": 7,
"id": "2475ae88",
"metadata": {},
"outputs": [],
@@ -288,24 +260,54 @@
"g = pp.io.read_netzschleuder_graph('sp_baboons', 'observational', time_attr='time')"
]
},
+ {
+ "cell_type": "markdown",
+ "id": "7c59349b",
+ "metadata": {},
+ "source": [
+ "The timestamps are provided in seconds since epoch (converted from unix time). We first convert them to a more human-readable format (hours since the first interaction): "
+ ]
+ },
{
"cell_type": "code",
- "execution_count": 7,
+ "execution_count": 8,
+ "id": "73dd5b14",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "g.data.time -= g.data.time.min()\n",
+ "g.data.time = g.data.time // (60 * 60 * 12) # convert to 12 hours intervals"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "dcdda88b",
+ "metadata": {},
+ "source": [
+ "The dataset contains different types of interactions between the baboons. We can use this information to color the edges based on the interaction type:"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
"id": "42520b99",
"metadata": {},
"outputs": [],
"source": [
"colors = []\n",
- "for category in g.data['edge_category']:\n",
+ "for category in g.data[\"edge_category\"]:\n",
" match category:\n",
- " case 'Affiliative': colors.append('red')\n",
- " case 'Agonistic': colors.append('green')\n",
- " case 'Other': colors.append('grey')"
+ " case \"Affiliative\":\n",
+ " colors.append(\"red\")\n",
+ " case \"Agonistic\":\n",
+ " colors.append(\"green\")\n",
+ " case \"Other\":\n",
+ " colors.append(\"grey\")"
]
},
{
"cell_type": "code",
- "execution_count": null,
+ "execution_count": 11,
"id": "8bc45755",
"metadata": {},
"outputs": [
@@ -313,18 +315,18 @@
"name": "stderr",
"output_type": "stream",
"text": [
- "100%|██████████| 21/21 [00:38<00:00, 1.83s/it]\n"
+ " \r"
]
},
{
"data": {
"text/html": [
"\n",
- " \n",
- " "
+ "