Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

## New features

* Added a new utility function to color relationships `VisualizationGraph.color_relationships`
* Added a new utility function to resize relationships `VisualizationGraph.resize_relationships`


## Bug fixes

Expand Down
85 changes: 62 additions & 23 deletions docs/antora/modules/ROOT/pages/customizing.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -49,35 +49,35 @@ for node in VG.nodes:
node.caption = str(caption)
----

== Coloring nodes
== Coloring the graph

Nodes can be colored directly by providing them with a color field, upon creation.
This can for example be done by passing a color as a string to the `color` parameter of the
link:{api-docs-uri}/node[Node] object.
Nodes and relationships can be colored directly by providing them with a color field, upon creation.
This can be done by passing a color as a string to the `color` parameter of the
link:{api-docs-uri}/node[Node] or link:{api-docs-uri}/relationship[Relationship] object.

Alternatively, you can color nodes based on a field or property of the nodes after a `VisualizationGraph` object has been
Alternatively, you can color nodes or relationships based on a field or property after a `VisualizationGraph` object has been
created.

=== The `color_nodes` method
=== The `color_nodes` and `color_relationships` methods

By calling the link:{api-docs-uri}/visualization-graph/#neo4j_viz.VisualizationGraph.color_nodes[`neo4j_viz.VisualizationGraph.color_nodes()`] method, you can color nodes based on a node field or property (members of the `Node.properties` map).
By calling the link:{api-docs-uri}/visualization-graph/#neo4j_viz.VisualizationGraph.color_nodes[`neo4j_viz.VisualizationGraph.color_nodes()`] or link:{api-docs-uri}/visualization-graph/#neo4j_viz.VisualizationGraph.color_relationships[`neo4j_viz.VisualizationGraph.color_relationships()`] method, you can color nodes or relationships based on a field or property (members of the `Node.properties` or `Relationship.properties` map).

It's possible to color the nodes based on a discrete or continuous color space (see link:{api-docs-uri}/colors[`ColorSpace`]).
In the discrete case, a new color from the `colors` provided is assigned to each unique value of the node field/property.
It's possible to color them based on a discrete or continuous color space (see link:{api-docs-uri}/colors[`ColorSpace`]).
In the discrete case, a new color from the `colors` provided is assigned to each unique value of the field/property.
In the continuous case, the `colors` should be a list of colors representing a range that are used to
create a gradient of colors based on the values of the node field/property.
create a gradient of colors based on the values of the field/property.

By default the Neo4j color palette, that works for both light and dark mode, will be used.
If you want to use a different color palette, you can pass a dictionary or iterable of colors as the `colors`
parameter.
A color value can for example be either strings like "blue", or hexadecimal color codes like "#FF0000", or even a tuple of RGB values like (255, 0, 255).

If some nodes already have a `color` set, you can choose whether or not to override it with the `override`
If some nodes or relationships already have a `color` set, you can choose whether or not to override it with the `override`
parameter.

==== By discrete color space

To not use the default colors, we can provide a list of custom colors based on the discrete node field "caption" to the `color_nodes` method:
To not use the default colors, we can provide a list of custom colors based on a discrete field to the coloring methods:

[source, python]
----
Expand All @@ -86,9 +86,12 @@ from neo4j_viz.colors import ColorSpace
# VG is a VisualizationGraph object
VG.color_nodes(
field="caption",
["red", "#7fffd4", (255, 255, 255, 0.5), "hsl(270, 60%, 70%)"],
colors=["red", "#7fffd4", (255, 255, 255, 0.5), "hsl(270, 60%, 70%)"],
color_space=ColorSpace.DISCRETE
)

# Color relationships based on their caption
VG.color_relationships(field="caption")
----

The full set of allowed values for colors are listed link:https://docs.pydantic.dev/2.0/usage/types/extra_types/color_types/[here].
Expand All @@ -100,43 +103,54 @@ Instead of defining your own colors, you could also for example use the color pa
from palettable.wesanderson import Moonrise1_5

# VG is a VisualizationGraph object
VG.color_nodes(field="caption", Moonrise1_5.colors) # PropertyType.DISCRETE is default
VG.color_nodes(field="caption", colors=Moonrise1_5.colors) # ColorSpace.DISCRETE is default
----

In theses cases, all nodes with the same caption will get the same color.
In theses cases, all items with the same field value will get the same color.

If there are fewer colors than unique values for the node `field` or `property` provided, the colors will be reused in a cycle.
If there are fewer colors than unique values for the `field` or `property` provided, the colors will be reused in a cycle.
To avoid that, you could use a larger palette or extend one with additional colors.
Refer to the link:/tutorials/gds-example[Visualizing Neo4j Graph Data Science (GDS) Graphs tutorial] for an example on how
to do the latter.

==== By continuous color space

To not use the default colors, we can provide a list of custom colors representing a range to the `color_nodes` method:
To not use the default colors, we can provide a list of custom colors representing a range:

[source, python]
----
from neo4j_viz.colors import PropertyType
from neo4j_viz.colors import ColorSpace

# VG is a VisualizationGraph object
VG.color_nodes(
property="centrality_score",
[(255, 0, 0), (191, 64, 0), (128, 128, 0), (64, 191, 0), (0, 255, 0)] # From red to green
colors=[(255, 0, 0), (191, 64, 0), (128, 128, 0), (64, 191, 0), (0, 255, 0)], # From red to green
color_space=ColorSpace.CONTINUOUS
)

# Color relationships based on a "weight" property
VG.color_relationships(
property="weight",
colors=["#E0E0E0", "#000000"], # From light grey to black
color_space=ColorSpace.CONTINUOUS
)
----

In this case, the nodes will be colored based on the value of the "centrality_score" property, with the lowest values being colored red and the highest values being colored green.
In these cases, the nodes or relationships will be colored based on the value of the property, with the lowest values being colored with the first color and the highest values being colored with the last color.
Since we only provided five colors in the range, the granularity of the gradient will be limited to five steps.

`palettable` and `matplotlib` are great libraries to use to create custom color gradients.

== Sizing nodes
== Sizing nodes and relationships

Nodes can be given a size directly by providing them with a size field, upon creation.
This can for example be done by passing a size as an integer to the `size` parameter of the link:{api-docs-uri}/node[Node] object.
This can be done by passing a size as an integer to the `size` parameter of the link:{api-docs-uri}/node[Node] object.


Alternatively, you can size nodes after a `VisualizationGraph` object has been created.
Relationships can be given a width directly by providing them with a width field, upon creation.
This can be done by passing a size as an integer to the `width` parameter of the link:{api-docs-uri}/relationship[Relationship] object.

Alternatively, you can resize them after a `VisualizationGraph` object has been created.

=== The `resize_nodes` method

Expand Down Expand Up @@ -166,6 +180,31 @@ VG.resize_nodes(sizes={42: 88}, node_radius_min_max=(5, 20))

Note that means that also the node with ID 42 will be scaled to be between 5 and 20 pixels in size.

=== The `resize_relationships` method

By calling the link:{api-docs-uri}/visualization-graph/#neo4j_viz.VisualizationGraph.resize_relationships[`neo4j_viz.VisualizationGraph.resize_relationships()`] method, you can resize relationship widths by:

* passing new widths as a dictionary `widths`, mapping relationship IDs to widths in pixels, or
* providing a `property` name of the relationships to use for sizing.

In the following example, we resize the relationship with ID "r1" to have a width of 5:

[source, python]
----
# VG is a VisualizationGraph object
VG.resize_relationships(widths={"r1": 5})
----

You can also resize relationships based on a property:

[source, python]
----
# VG is a VisualizationGraph object
VG.resize_relationships(property="weight")
----

Unlike `resize_nodes`, relationship widths are used as-is and are not scaled to a specific range.

== Pinning nodes

Nodes can be pinned to their current position in the visualization, so that they will not be moved by the force-directed
Expand Down
3 changes: 0 additions & 3 deletions python-wrapper/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ Alternatively, you can export the output to a file and view it in a web browser.

The package wraps the [Neo4j Visualization JavaScript library (NVL)](https://neo4j.com/docs/nvl/current/).

> [!WARNING]
> This package is still in development and the API is subject to change.


![Example Graph](https://github.com/neo4j/python-graph-visualization/blob/main/examples/example_cora_graph.png)

Expand Down
9 changes: 9 additions & 0 deletions python-wrapper/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ notebook = [
"palettable>=3.3.3",
"matplotlib>=3.9.4",
"snowflake-snowpark-python==1.42.0",
"dotenv"
]

[project.urls]
Expand Down Expand Up @@ -100,6 +101,14 @@ neo4j_viz = [

[tool.pytest.ini_options]
addopts = ["--import-mode=importlib"]
markers = [
"requires_neo4j_and_gds: mark a test as a requiring a Neo4j instance with GDS running",
"requires_snowflake: mark a test as a requiring a Snowflake connection"
]
filterwarnings = [
"error",
"ignore:Jupyter is migrating its paths to use standard platformdirs:DeprecationWarning"
]

[tool.ruff]
line-length = 120
Expand Down
2 changes: 2 additions & 0 deletions python-wrapper/src/neo4j_viz/relationship.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ class Relationship(
caption_size: Optional[Union[int, float]] = Field(None, gt=0.0, description="The size of the caption text")
#: The color of the relationship. Allowed input is for example "#FF0000", "red" or (255, 0, 0)
color: Optional[ColorType] = Field(None, description="The color of the relationship")
# The width of the relationship
width: Optional[int | float] = Field(None, gt=0.0, description="The width of the relationship")
#: Additional properties of the relationship that do not directly impact the visualization
properties: dict[str, Any] = Field(
default_factory=dict,
Expand Down

Large diffs are not rendered by default.

Loading