diff --git a/docs/.gitignore b/docs/.gitignore index 378eac25..d6846fe5 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1 +1,5 @@ build + +# Antora docs +antora/package-lock.json +antora/node_modules/ \ No newline at end of file diff --git a/docs/Makefile b/docs/Makefile index d0c3cbf1..f2d75902 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -3,7 +3,7 @@ # You can set these variables from the command line, and also # from the environment for the first two. -SPHINXOPTS ?= +SPHINXOPTS ?= -b dirhtml SPHINXBUILD ?= sphinx-build SOURCEDIR = source BUILDDIR = build @@ -14,7 +14,6 @@ help: .PHONY: help Makefile -# Catch-all target: route all unknown targets to Sphinx using the new -# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +# Use this format to make use of the "-b dirhtml" switch %: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + @$(SPHINXBUILD) $(SPHINXOPTS) "$(SOURCEDIR)" "$(BUILDDIR)" diff --git a/docs/antora/antora.yml b/docs/antora/antora.yml new file mode 100644 index 00000000..319e2218 --- /dev/null +++ b/docs/antora/antora.yml @@ -0,0 +1,12 @@ +name: python-graph-visualization +title: Neo4j Python Graph Visualization +version: '1' +start_page: ROOT:index.adoc +nav: +- modules/ROOT/content-nav.adoc +asciidoc: + attributes: + # Version needs to be updated manually for now + docs-version: '1' + api-docs-uri: '{neo4j-docs-base-uri}/python-graph-visualization/{docs-version}/reference/api-reference' + tutorials-docs-uri: '{neo4j-docs-base-uri}/python-graph-visualization/{docs-version}/reference/tutorials' \ No newline at end of file diff --git a/docs/antora/modules/ROOT/content-nav.adoc b/docs/antora/modules/ROOT/content-nav.adoc new file mode 100644 index 00000000..1d5e0b39 --- /dev/null +++ b/docs/antora/modules/ROOT/content-nav.adoc @@ -0,0 +1,8 @@ +* xref:index.adoc[] +* xref:installation.adoc[] +* xref:getting-started.adoc[] +* xref:integration.adoc[] +* xref:rendering.adoc[] +* xref:customizing.adoc[] +* link:{tutorials-docs-uri}[Tutorials] +* link:{api-docs-uri}[API reference] \ No newline at end of file diff --git a/docs/antora/modules/ROOT/images/graph_00ff5513.png b/docs/antora/modules/ROOT/images/graph_00ff5513.png new file mode 100644 index 00000000..7b5697c9 Binary files /dev/null and b/docs/antora/modules/ROOT/images/graph_00ff5513.png differ diff --git a/docs/antora/modules/ROOT/images/graph_2120034f.png b/docs/antora/modules/ROOT/images/graph_2120034f.png new file mode 100644 index 00000000..10353d73 Binary files /dev/null and b/docs/antora/modules/ROOT/images/graph_2120034f.png differ diff --git a/docs/antora/modules/ROOT/pages/customizing.adoc b/docs/antora/modules/ROOT/pages/customizing.adoc new file mode 100644 index 00000000..a0692c0a --- /dev/null +++ b/docs/antora/modules/ROOT/pages/customizing.adoc @@ -0,0 +1,222 @@ += Customizing the visualization + +Once created, a link:{api-docs-uri}/visualization-graph[`VisualizationGraph` object] can be modified in various ways +to adjust what the visualization looks like the next time you render it. +In this section we will discuss how to color, size, and pin nodes, as well as how to directly modify nodes and +relationships of existing `VisualizationGraph` objects. + +If you have not yet created a `VisualizationGraph` object, refer to one of the following sections: + +* xref:getting-started.adoc[Getting started] for creating a visualization graph from scratch using `neo4j-viz` + primitives like link:{api-docs-uri}/node/[Node] and link:{api-docs-uri}/relationship/[Relationship] and + link:{api-docs-uri}/visualization-graph[`VisualizationGraph`] directly. +* xref:integration.adoc[] for importing data from a Pandas DataFrame or Neo4j GDS projection. + +== Setting node captions + +Node captions are the text labels displayed on nodes in the visualization. + +=== The `set_node_captions` method + +By calling the `neo4j_viz.VisualizationGraph.set_node_captions()` method, you can set node captions based on a node field (like `id`, `size`, etc.) or a node property (members of the `Node.properties` map). + +The method accepts an `override` parameter (default `True`) that controls whether to replace existing captions. +If `override=False`, only nodes without captions will be updated. + +Here is an example of setting node captions from a property: + +[source, python] +---- +# VG is a VisualizationGraph object with nodes that have a "name" property +VG.set_node_captions(property="name") +---- + +You can also set captions from a node field, and choose not to override existing captions: + +[source, python] +---- +# VG is a VisualizationGraph object +VG.set_node_captions(field="id", override=False) +---- + +For more complex scenarios where you need fallback logic or want to combine multiple properties, you can iterate over nodes directly: + +[source, python] +---- +# VG is a VisualizationGraph object +for node in VG.nodes: + caption = node.properties.get("name") or node.properties.get("title") or node.id + node.caption = str(caption) +---- + +== Coloring nodes + +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. + +Alternatively, you can color nodes based on a field or property of the nodes after a `VisualizationGraph` object has been +created. + +=== The `color_nodes` method + +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). + +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. +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. + +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` +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: + +[source, python] +---- +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%)"], + color_space=ColorSpace.DISCRETE +) +---- + +The full set of allowed values for colors are listed link:https://docs.pydantic.dev/2.0/usage/types/extra_types/color_types/[here]. + +Instead of defining your own colors, you could also for example use the color palettes from the link:https://jiffyclub.github.io/palettable/[`palettable` library] as in this snippet: + +[source, python] +---- +from palettable.wesanderson import Moonrise1_5 + +# VG is a VisualizationGraph object +VG.color_nodes(field="caption", Moonrise1_5.colors) # PropertyType.DISCRETE is default +---- + +In theses cases, all nodes with the same caption 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. +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: + +[source, python] +---- +from neo4j_viz.colors import PropertyType + +# 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 + 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. +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 + +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. + +Alternatively, you can size nodes after a `VisualizationGraph` object has been created. + +=== The `resize_nodes` method + +By calling the link:{api-docs-uri}/visualization-graph/#neo4j_viz.VisualizationGraph.resize_nodes[`neo4j_viz.VisualizationGraph.resize_nodes()`] method, you can resize nodes by: + +* passing new nodes sizes as a dictionary `sizes`, mapping node IDs to sizes in pixels, or +* providing a tuple of two numbers `node_radius_min_max`: minimum and maximum radii (sizes) in pixels to which the + nodes will be scaled. + +Or you could provide both `sizes` and `node_radius_min_max`, in which case the dictionary will be used to first set +the sizes of the nodes, and then the minimum and maximum values of the tuple will be subsequently used to scale the +sizes to the provided range. + +If you provide only the `node_radius_min_max` parameter, the sizes of the nodes will be scaled such that the smallest +node will have the size of the first value, and the largest node will have the size of the second value. +The other nodes will be scaled linearly between these two values according to their relative size. +This can be useful if node sizes vary a lot, or are all very small or very big. + +In the following example, we resize the node with ID 42 to have a size of 88 pixels, and then scales all nodes to have +sizes between 5 and 20 pixels: + +[source, python] +---- +# VG is a VisualizationGraph object +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. + +== Pinning nodes + +Nodes can be pinned to their current position in the visualization, so that they will not be moved by the force-directed +layout algorithm. +This can be useful if you want to keep a node in a specific position, for example to highlight it. + +Nodes can be pinned directly upon creation. +This can for example be done by passing `pinned=True` to the link:{api-docs-uri}/node[Node] object. + +Alternatively, you can toggle node pinning after a `VisualizationGraph` object has been created. + +=== The `toggle_nodes_pinned` method + +By calling the link:{api-docs-uri}/visualization-graph/#neo4j_viz.VisualizationGraph.toggle_nodes_pinned[`neo4j_viz.VisualizationGraph.toggle_nodes_pinned()`] method, you can toggle whether nodes should be +pinned or not. +This method takes dictionary that maps node IDs to boolean values, where `True` means that the node is pinned, and +`False` means that the node is not pinned. + +In the following example, we pin the node with ID 1337 and unpin the node with ID 42: + +[source, python] +---- +# VG is a VisualizationGraph object +VG.toggle_nodes_pinned(1337: True, 42: False)}) +---- + +== Direct modification of nodes and relationships + +Nodes and relationships can also be modified directly by accessing the `nodes` and `relationships` fields of an +existing `VisualizationGraph` object. +These fields list of all the link:{api-docs-uri}/node[Nodes] and link:{api-docs-uri}/relationship[Relationships] in the graph, respectively. + +Each node and relationship has attributes that can be accessed and modified directly, as in the following example: + +[source, python] +---- +# VG is a VisualizationGraph object + +# Modify the first node and fifth relationship +VG.nodes[0].size = 10 +VG.nodes[0].properties["height"] = 170 +VG.relationships[4].caption = "BUYS" + +# Set the coordinates for all nodes from an existing property +for node in VG.nodes: + node.x = node.properties.get("x") + node.y = node.properties.get("y") + +# Change the caption size for all relationships +for relationship in VG.relationships: + relationship.caption_size = 15 +---- + +Any changes made to the nodes and relationships will be reflected in the next rendering of the graph. \ No newline at end of file diff --git a/docs/antora/modules/ROOT/pages/getting-started.adoc b/docs/antora/modules/ROOT/pages/getting-started.adoc new file mode 100644 index 00000000..6d570337 --- /dev/null +++ b/docs/antora/modules/ROOT/pages/getting-started.adoc @@ -0,0 +1,91 @@ += Getting started + +In this section, we will cover the very basics of creating a visualization graph using the `neo4j-viz` library. +We will use a small toy graph representing the purchase history of a few people and products. + +To follow along with the example below, please make sure you have xref:installation.adoc[installed the library] first. + +We start by instantiating the link:{api-docs-uri}/node/[Nodes] and link:{api-docs-uri}/relationship[Relationships] we want in our graph. +The only mandatory fields for a node are the “id”, and “source” and “target” for a relationship. +But the other fields can optionally be used to customize the appearance of the nodes and relationships in the visualization. + +Lastly we create a link:{api-docs-uri}/visualization-graph/[VisualizationGraph] object with the nodes and relationships we created, and call its `render` method to display the graph. + +[source, python, role=nocollapse] +---- +from neo4j_viz import Node, Relationship, VisualizationGraph + +nodes = [ + Node(id=0, size=10, caption="Person"), + Node(id=1, size=10, caption="Product"), + Node(id=2, size=20, caption="Product"), + Node(id=3, size=10, caption="Person"), + Node(id=4, size=10, caption="Product"), +] + +relationships = [ + Relationship( + source=0, + target=1, + caption="BUYS", + ), + Relationship( + source=0, + target=2, + caption="BUYS", + ), + Relationship( + source=3, + target=2, + caption="BUYS", + ), +] + +VG = VisualizationGraph(nodes=nodes, relationships=relationships) + +VG.render(initial_zoom=2) +---- + +[TIP] +==== +See the link:{tutorials-docs-uri}/getting-started/[Jupyter notebook] to interact with the graph. +==== + +image::graph_2120034f.png["Graph"] + +As we can see in the graph above, the radius of one of the nodes is larger than the others. +This is because we set the “size” field of the node to 20, while the others are set to 10. + +At this time all nodes have the same color. +If we want to distinguish between the different types of nodes, we can color them differently with the `color_nodes` method. +We can pass the field we want to use to color the nodes as an argument. +In this case, we will use the "caption" field. +Nodes with the same "caption" will have the same color. +We will use the default color scheme, which is the Neo4j color scheme. + +[source, python] +---- +VG.color_nodes(field="caption") + +VG.render(initial_zoom=2) +---- + +[TIP] +==== +See the link:{tutorials-docs-uri}/getting-started/[Jupyter notebook] to interact with the graph. +==== + +image::graph_00ff5513.png["Graph with colors"] + +We are now easily able to distinguish between the different types of nodes in the graph. + +== Next steps + +Now that we have covered the basics of creating and rendering a visualization graph, we can move on to more advanced topics. + +Here are some suggestions for what to do next: + +* Browse the link:{tutorials-docs-uri}[notebook examples] for more examples and use cases +* Learn about the xref:integration.adoc[integrations] with other libraries such as Neo4j GDS and Pandas +* Check out the xref:rendering.adoc[rendering options] and convenience methods of `VisualizationGraph` +* Read about our link:{api-docs-uri}/[API reference] diff --git a/docs/antora/modules/ROOT/pages/index.adoc b/docs/antora/modules/ROOT/pages/index.adoc new file mode 100644 index 00000000..9f2ef116 --- /dev/null +++ b/docs/antora/modules/ROOT/pages/index.adoc @@ -0,0 +1,10 @@ += Introduction + +This is the documentation for the `neo4j-viz` Python library by Neo4j. +The library allows you to visualize graph data interactively in Python using a simple API. + +The library wraps the link:https://neo4j.com/docs/nvl/current/[Neo4j Visualization JavaScript library (NVL)], and provides additional features for working with graph data in Python. +Notably, there are convenience methods for importing data from source such as link:https://pandas.pydata.org/[Pandas DataFrames], link:https://neo4j.com/docs/graph-data-science/current/[Neo4j Graph Data Science], link:https://neo4j.com/docs/python-manual/current/[Neo4j Database], and link:https://docs.snowflake.com/[Snowflake tables]. + +The source code is available on link:https://github.com/neo4j/python-graph-visualization[GitHub]. +If you have a suggestion on how we can improve the library or want to report a problem, you can create a link:https://github.com/neo4j/python-graph-visualization/issues/new[new issue]. \ No newline at end of file diff --git a/docs/antora/modules/ROOT/pages/installation.adoc b/docs/antora/modules/ROOT/pages/installation.adoc new file mode 100644 index 00000000..47ef95f6 --- /dev/null +++ b/docs/antora/modules/ROOT/pages/installation.adoc @@ -0,0 +1,57 @@ += Installation + +To install the latest version of the library, run: + +[source, console] +---- +pip install neo4j-viz +---- + +== Optional dependencies + +In addition, there are a few optional dependencies that you can install to enable additional features of the library. + +=== Pandas DataFrames `from_dfs` importer + +To install the additional dependencies required for the link:{api-docs-uri}/from_pandas[`from_dfs`] importer you can run: + +[source, console] +---- +pip install neo4j-viz[pandas] +---- + +=== Neo4j `from_neo4j` importer + +To install the additional dependencies required for the link:{api-docs-uri}/from_neo4j[`from_neo4j`] importer you can run: + +[source, console] +---- +pip install neo4j-viz[neo4j] +---- + +=== Neo4j Graph Data Science `from_gds` importer + +To install the additional dependencies required for the link:{api-docs-uri}/from_gds[`from_gds`] importer you can run: + +[source, console] +---- +pip install neo4j-viz[gds] +---- + +=== Neo4j Graph Data Science `from_snowflake` importer + +To install the additional dependencies required for the link:{api-docs-uri}/from_snowflake[`from_snowflake`] importer you can run: + +[source, console] +---- +pip install neo4j-viz[snowflake] +---- + +=== Notebook tutorials + +To install all the additional dependencies required for the link:{tutorials-docs-uri}[notebook examples] you can run: + +[source, console] +---- +pip install neo4j-viz[notebook] +---- \ No newline at end of file diff --git a/docs/antora/modules/ROOT/pages/integration.adoc b/docs/antora/modules/ROOT/pages/integration.adoc new file mode 100644 index 00000000..67c7422b --- /dev/null +++ b/docs/antora/modules/ROOT/pages/integration.adoc @@ -0,0 +1,286 @@ += Integration with other libraries + +In addition to creating graphs from scratch, with `neo4j-viz` as is shown in the +xref:getting-started.adoc[], you can also import data directly from external sources. +In this section we will cover how to import data from link:https://pandas.pydata.org/[Pandas DataFrames], +link:https://neo4j.com/docs/graph-data-science/current/[Neo4j Graph Data Science], +link:https://neo4j.com/docs/python-manual/current/[Neo4j Database], +link:https://neo4j.com/docs/cypher-manual/current/clauses/create/[GQL `CREATE` queries], and link:https://docs.snowflake.com/[Snowflake tables]. + +== Pandas DataFrames + +The `neo4j-viz` library provides a convenience method for importing data from Pandas DataFrames. +These DataFrames can be created from many sources, such as CSV files. +It requires and additional dependency to be installed, which you can do by running: + +[source, console] +---- +pip install neo4j-viz[pandas] +---- + +Once you have installed the additional dependency, you can use the link:{api-docs-uri}/from_pandas[`from_pandas`] method +to import pandas DataFrames. + +The `from_dfs` method takes two mandatory positional parameters: + +* A Pandas `DataFrame`, or iterable (eg. list) of DataFrames representing the nodes of the graph. + The rows of the DataFrame(s) should represent the individual nodes, and the columns should represent the node + IDs and attributes. + The node ID will be set on the link:{api-docs-uri}/node[Node], Other columns will be a key in each node's properties dictionary, that maps to the node's corresponding value in the column. + If the graph has no node properties, the nodes can be derived from the relationships DataFrame alone. +* A Pandas `DataFrame`, or iterable (eg. list) of DataFrames representing the relationships of the graph. + The rows of the DataFrame(s) should represent the individual relationships, and the columns should represent the + relationship IDs and attributes. + The relationship id, source and target node IDs will be set on the link:{api-docs-uri}/relationship[Relationship]. + Other columns will be a key in each relationship's _properties_ dictionary, that maps to the relationship's corresponding value in the column. + +=== Example + +In this small example, we import a tiny toy graph representing a social network from two Pandas DataFrames. +As we can see the column names of the DataFrames map directly to the fields of link:{api-docs-uri}/node[Nodes] +and link:{api-docs-uri}/relationship[Relationships]. + +[source, python] +---- +from pandas import DataFrame +from neo4j_viz.pandas import from_dfs + +nodes = DataFrame({ + "id": [1, 2, 3], + "caption": ["Alice", "Bob", "Charlie"], + "size": [20, 10, 10], +}) + +relationships = DataFrame({ + "source": [1, 2], + "target": [2, 3], + "caption": ["LIKES", "KNOWS"], +}) + +VG = from_dfs(nodes, relationships) +---- + +== Neo4j Graph Data Science (GDS) library + +The `neo4j-viz` library provides a convenience method for importing data from the Neo4j Graph Data Science (GDS) +library. +It requires and additional dependency to be installed, which you can do by running: + +[source, console] +---- +pip install neo4j-viz[gds] +---- + +Once you have installed the additional dependency, you can use the link:{api-docs-uri}/from_gds[`from_gds`] method +to import projections from the GDS library. + +The `from_gds` method takes two mandatory positional parameters: + +* An initialized `GraphDataScience` object for the connection to the GDS instance, and +* A `Graph` representing the projection that one wants to import. + +The optional `max_node_count` parameter can be used to limit the number of nodes that are imported from the projection. +By default, it is set to 10.000, meaning that if the projection has more than 10.000 nodes, `from_gds` will sample from it using random walk with restarts, to get a smaller graph that can be visualized. +If you want to have more control of the sampling, such as choosing a specific start node for the sample, you can call a link:https://neo4j.com/docs/graph-data-science/current/management-ops/graph-creation/sampling/[sampling] method yourself and passing the resulting projection to `from_gds`. + +The `node_properties` parameter is also optional, and should be a list of additional node properties of the projection that you want to include in the visualization. +The default is `None`, which means that all properties of the nodes in the projection will be included. +Apart from being visible through on-hover tooltips, these properties could be used to color the nodes, or give captions to them in the visualization, or simply included in the nodes' `Node.properties` maps without directly impacting the visualization. +If you want to include node properties stored at the Neo4j database, you can include them in the visualization by using the _db_node_properties_ parameter. + +=== Example + +In this small example, we import a graph projection from the GDS library, that has the node properties "pagerank" and +"componentId". +We use the "pagerank" property to determine the size of the nodes, and the "componentId" property to color the nodes. + +[source, python] +---- +from graphdatascience import GraphDataScience +from neo4j_viz.gds import from_gds +gds = GraphDataScience(...) +G = gds.graph.project(...) +# Compute the PageRank and Weakly Connected Components +gds.pageRank.mutate(G, mutateProperty="pagerank") +gds.wcc.mutate(G, mutateProperty="componentId") +# Import the projection into a `VisualizationGraph` +# Make sure to include `pagerank` and `componentId` +VG = from_gds( + gds, + G, + size_property="pagerank", + additional_node_properties=["componentId"], +) +# Color the nodes by the `componentId` property, so that the nodes are +# colored by the connected component they belong to +VG.color_nodes(property="componentId") +---- + +See the link:/tutorials/gds-example[Visualizing Neo4j Graph Data Science (GDS) Graphs tutorial] for a more extensive example. + +== Neo4j Database + +The `neo4j-viz` library provides a convenience method for importing data from Neo4j. +It requires and additional dependency to be installed, which you can do by running: + +[source, console] +---- +pip install neo4j-viz[neo4j] +---- + +Once you have installed the additional dependency, you can use the link:{api-docs-uri}/from_neo4j[`from_neo4j`] method +to import query results from Neo4j. + +The `from_neo4j` method takes one mandatory positional parameter: A `data` argument representing either a query result in the shape of a `neo4j.graph.Graph` or `neo4j.Result`, or a `neo4j.Driver` in which case a simple default query will be executed internally to retrieve the graph data. + +The optional `max_rows` parameter can be used to limit the number of relationships shown in the visualization. +By default, it is set to 10.000, meaning that if the database has more than 10.000 rows, a warning will be raised. +Note, this only applies if the `data` parameter is a `neo4j.Driver`. + +=== Example + +In this small example, we import a graph from a Neo4j query result. + +[source, python] +---- +from neo4j import GraphDatabase, RoutingControl, Result +from neo4j_viz.gds import from_gds + +# Modify this to match your Neo4j instance's URI and credentials +URI = "neo4j://localhost:7687" +auth = ("neo4j", "password") + +with GraphDatabase.driver(URI, auth=auth) as driver: + driver.verify_connectivity() + result = driver.execute_query( + "MATCH (n)-[r]->(m) RETURN n,r,m", + database_="neo4j", + routing_=RoutingControl.READ, + result_transformer_=Result.graph, + ) + +VG = from_neo4j(result) +---- + +See the link:/tutorials/neo4j-example[Visualizing Neo4j Graphs tutorial] for a more extensive example. + +== GQL `CREATE` query + +The `neo4j-viz` library provides convenience for creating visualization graphs from GQL `CREATE` queries via the link:{api-docs-uri}/from_gql_create[`from_gql_create`] method. + +The `from_gql_create` method takes one mandatory positional parameter: + +* A valid `query` representing a GQL `CREATE` query as a string. + +=== Example + +In this small example, we create a visualization graph from a GQL `CREATE` query. + +[source, python] +---- +from neo4j_viz.gql_create import from_gql_create + +query = """ + CREATE + (a:User {name: 'Alice', age: 23}), + (b:User {name: 'Bridget', age: 34}), + (c:User {name: 'Charles', age: 45}), + (d:User {name: 'Dana', age: 56}), + (e:User {name: 'Eve', age: 67}), + (f:User {name: 'Fawad', age: 78}), + (a)-[:LINK {weight: 0.5}]->(b), + (a)-[:LINK {weight: 4}]->(c), + (e)-[:LINK {weight: 1.1}]->(d), + (e)-[:LINK {weight: -2}]->(f); + """ + +VG = from_gql_create(query) +---- + +== Snowflake Tables + +The `neo4j-viz` library provides a convenience method for importing data from Snowflake tables. +It requires and additional dependency to be installed, which you can do by running: + +[source, console] +---- +pip install neo4j-viz[snowflake] +---- + +Once you have installed the additional dependency, you can use the link:{api-docs-uri}/from_snowflake[`from_snowflake`] method to import Snowflake tables into a `VisualizationGraph`. + +The `from_snowflake` method takes two mandatory positional parameters: + +* A `snowflake.snowpark.Session` object for the connection to Snowflake, and + +* A link:https://neo4j.com/docs/snowflake-graph-analytics/current/jobs/#jobs-project[project configuration] as a dictionary, that specifies how you want your tables to be projected as a graph. +This configuration is the same as the project configuration of the link:https://neo4j.com/docs/snowflake-graph-analytics/current/[Neo4j Snowflake Graph Analytics application]. + +You can further customize the visualization after the _VisualizationGraph_ has been created, by using the methods described in the link:{api-docs-uri}/customizing/[Customizing the visualization] section. + +=== Default behavior + +The node and relationship captions will be set to the names of the corresponding tables. +The nodes will be colored so that nodes from the same table have the same color, and different tables have different colors. + +=== Example + +In this small example, we import a toy graph representing a social network from two tables in Snowflake. + +[source, python] +---- +from snowflake.snowpark import Session +from neo4j_viz.snowflake import from_dfs + +# Configure according to your own setup +connection_parameters = { + "account": os.environ.get("SNOWFLAKE_ACCOUNT"), + "user": os.environ.get("SNOWFLAKE_USER"), + "password": os.environ.get("SNOWFLAKE_PASSWORD"), + "role": os.environ.get("SNOWFLAKE_ROLE"), + "warehouse": os.environ.get("SNOWFLAKE_WAREHOUSE"), +} + +session.sql( + "CREATE OR REPLACE TABLE EXAMPLE_DB.DATA_SCHEMA.PERSONS (NODEID VARCHAR);" +).collect() + +session.sql(""" +INSERT INTO EXAMPLE_DB.DATA_SCHEMA.PERSONS VALUES + ('Alice'), + ('Bob'), + ('Carol'), + ('Dave'), + ('Eve'); + """).collect() + +session.sql( + "CREATE OR REPLACE TABLE EXAMPLE_DB.DATA_SCHEMA.KNOWS (SOURCENODEID VARCHAR, TARGETNODEID VARCHAR);" +).collect() + +session.sql(""" +INSERT INTO EXAMPLE_DB.DATA_SCHEMA.KNOWS VALUES + ('Alice', 'Dave'), + ('Alice', 'Carol'), + ('Bob', 'Carol'), + ('Dave', 'Eve'), + """).collect() + +VG = from_snowflake( + session, + { + "nodeTables": [ + "EXAMPLE_DB.DATA_SCHEMA.PERSONS", + ], + "relationshipTables": { + "EXAMPLE_DB.DATA_SCHEMA.KNOWS": { + "sourceTable": "EXAMPLE_DB.DATA_SCHEMA.PERSONS", + "targetTable": "EXAMPLE_DB.DATA_SCHEMA.PERSONS", + "orientation": "UNDIRECTED", + } + }, + }, +) +---- + +For a full example of the `from_snowflake` importer in action, please see the link:http://localhost:9000/tutorials/snowflake-example/[Visualizing Snowflake Tables tutorial]. \ No newline at end of file diff --git a/docs/antora/modules/ROOT/pages/rendering.adoc b/docs/antora/modules/ROOT/pages/rendering.adoc new file mode 100644 index 00000000..2ce089ed --- /dev/null +++ b/docs/antora/modules/ROOT/pages/rendering.adoc @@ -0,0 +1,63 @@ += Rendering a graph + +In this section, we will discuss how to render a link:{api-docs-uri}/visualization-graph[`VisualizationGraph` object] +to display the graph visualization. + +If you have not yet created a `VisualizationGraph` object, refer to one of the following sections: + +* xref:getting-started.adoc[Getting started] for creating a visualization graph from scratch using `neo4j-viz` + primitives like link:{api-docs-uri}/node[Node] and link:{api-docs-uri}/relationship[Relationship] and + link:{api-docs-uri}/visualization-graph>[`VisualizationGraph`] directly. +* xref:integration.adoc[] for importing data from a Pandas DataFrame or Neo4j GDS projection. + +== The `render` method + +Once you have a `VisualizationGraph` object, you can render it using the `render` method. +This will return a HTML object that will be displayed in an environment that supports HTML rendering, such as +Jupyter notebooks or streamlit application. + +All parameter of the `render` method are optional, and the full list of parameters of them is listed in the API +reference: link:{api-docs-uri}/visualization-graph/#neo4j_viz.VisualizationGraph.render[neo4j_viz.VisualizationGraph.render()]. + +The most important parameters to be aware of are the `width` and `height` parameters, which control the size of +HTML object that will be rendered. +You can provide these either as a percentage of the available space (eg. `"80%"`), or as an absolute pixel value +(eg. `"800px"`). + +Further you can change the layout of the graph using the `layout` parameter, which can be set to one of the following values: + +* `Layout.FORCE_DIRECTED` - Nodes are arranged using the Force-Directed algorithm, which simulates physical forces. +To customize the layout, use _ForceDirectedOptions_ via _layout_options_. +* `Layout.HIERARCHICAL` - Arranges nodes by the directionality of their relationships, creating a tree-like structure. +To customize the layout use _HierarchicalLayoutOptions_ via _layout_options_. +* `Layout.COORDINATE` - Arranges nodes based on coordinates defined in _x_ and _y_ properties on each node. + +Another thing of note is the `max_allowed_nodes` parameter, which controls the maximum number of nodes that is allowed +for the graph to contain in order to be rendered. +It defaults to 10.000, because rendering a large number of nodes can be slow and unresponsive. +However, you can increase this value if you are confident that your environment can handle the scale. +In this case you might also want to pass `Renderer.WEB_GL` as the `renderer` to improve performance. + +By default a tooltip showing IDs and properties will be shown when mouse hovering over a node or relationship. +But you can disable this by passing `show_hover_tooltip=False`. + +=== Examples + +Refer to the xref:getting-started.adoc[] and the link:/tutorials/index[tutorials] for +examples of `render` method usage. + +== Exporting to HTML + +The object returned by the `render` method is a `IPython.display.HTML` object. +In addition to being displayed in a Jupyter notebook or streamlit application, it can also be saved as an HTML file. +This could be useful if you want to share the visualization with others or embed it in a web page. + +To save the HTML data to a file, you can use the `data` attribute of the HTML object: + +[source, python] +---- +html = VG.render(...) + +with open("my_graph.html", "w") as f: + f.write(html.data) +---- \ No newline at end of file diff --git a/docs/antora/package.json b/docs/antora/package.json new file mode 100644 index 00000000..4a64a880 --- /dev/null +++ b/docs/antora/package.json @@ -0,0 +1,54 @@ +{ + "name": "python-graph-visualization", + "version": "1.0.0", + "description": "Neo4j Python Graph Visualization", + "main": "server.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "prestart": "npm update", + "start": "nodemon --exec \"npm run build\"", + "serve": "node server.js", + "clean": "rm -rf build", + "build": "npm run build:preview", + "postbuild": "node server.js", + "build:preview": "antora preview.yml --stacktrace --log-format=pretty", + "build:publish": "npm run clean && antora publish.yml --stacktrace --log-format=pretty", + "verify:preview": "antora --stacktrace --fetch preview.yml --log-format=json --log-level=info --log-file ./build/log/log.json", + "verify:publish": "antora --stacktrace --fetch publish.yml --log-format=json --log-level=info --log-file ./build/log/log.json" + }, + "keywords": [ + "antora", + "neo4j" + ], + "author": "Neo4j", + "license": "ISC", + "dependencies": { + "@neo4j-antora/aliases-redirects": "^0.2.5", + "@neo4j-antora/antora-add-notes": "^0.3.2", + "@neo4j-antora/mark-terms": "^1.1.0", + "@neo4j-antora/roles-labels": "^0.1.1", + "@neo4j-antora/selector-labels": "^0.1.1", + "@neo4j-antora/table-footnotes": "^1.0.0", + "@neo4j-antora/xref-hash-validator": "^0.1.3", + "@neo4j-documentation/macros": "^1.0.4", + "@neo4j-documentation/remote-include": "^1.0.0", + "antora": "3.1.14" + }, + "devDependencies": { + "express": "^5.1.0", + "nodemon": "^3.1.0" + }, + "nodemonConfig": { + "watch": [ + "**/modules/**", + "**/antora.yml", + "**/preview.yml", + "**/publish.yml" + ], + "ext": "yml,yaml,adoc,svg,png,jpg", + "ignore": [ + "build", + "node_modules" + ] + } +} diff --git a/docs/antora/preview.yml b/docs/antora/preview.yml new file mode 100644 index 00000000..3845f156 --- /dev/null +++ b/docs/antora/preview.yml @@ -0,0 +1,64 @@ +site: + title: Neo4j Docs + url: https://neo4j.com/docs + start_page: python-graph-visualization:ROOT:index.adoc + +content: + sources: + - url: ../../ + start_path: docs/antora + branches: HEAD + exclude: + - '!**/_includes/*' + - '!**/readme.adoc' + - '!**/README.adoc' + +ui: + bundle: + url: https://static-content.neo4j.com/build/ui-bundle-latest.zip + snapshot: true + output_dir: /assets + +urls: + html_extension_style: indexify + +antora: + extensions: + - "@neo4j-antora/roles-labels" + - "@neo4j-antora/table-footnotes" + - "@neo4j-antora/xref-hash-validator" + +asciidoc: + extensions: + - "@neo4j-documentation/remote-include" + - "@neo4j-documentation/macros" + - "@neo4j-antora/mark-terms" + attributes: + # page-attributes are used by the ui-bundle and by extensions + page-theme: docs + page-type: Docs + page-search-type: Docs + page-search-site: Reference Docs + page-canonical-root: /docs + page-terms-to-mark: Cypher + page-pagination: true + page-no-canonical: true + page-origin-private: true # change to false to display 'Raise an issue' links + page-hide-toc: false + page-mixpanel: 4bfb2414ab973c741b6f067bf06d5575 + # legacy attributes - do not change these + includePDF: false + nonhtmloutput: "" + experimental: '' + # update the copyright value with the first commit in a new year + copyright: 2025 + # icon attributes + check-mark: icon:check[] + cross-mark: icon:times[] + # neo4j.com attributes. Always use when linking to neo4j.com URLs + neo4j-base-uri: https://neo4j.com + neo4j-docs-base-uri: '{neo4j-base-uri}/docs' + common-license-page-uri: '{neo4j-docs-base-uri}/license' + # Uses a local server to preview Sphinx docs + api-docs-uri: http://localhost:9000/api-reference + tutorials-docs-uri: http://localhost:9000/tutorials diff --git a/docs/antora/publish.yml b/docs/antora/publish.yml new file mode 100644 index 00000000..e67574ec --- /dev/null +++ b/docs/antora/publish.yml @@ -0,0 +1,63 @@ +# Use this playbook file when you need to build and publish multiple versions of your content together +site: + title: Neo4j Docs + url: https://neo4j.com/docs + start_page: python-graph-visualization:ROOT:index.adoc + +content: + sources: + - url: ../../ + start_path: docs/antora + branches: ['HEAD'] + exclude: + - '!**/_includes/*' + - '!**/readme.adoc' + - '!**/README.adoc' + +ui: + bundle: + url: https://static-content.neo4j.com/build/ui-bundle-latest.zip + snapshot: true + output_dir: /assets + +urls: + html_extension_style: indexify + +antora: + extensions: + - "@neo4j-antora/aliases-redirects" + - "@neo4j-antora/roles-labels" + - "@neo4j-antora/table-footnotes" + - "@neo4j-antora/xref-hash-validator" + +asciidoc: + extensions: + - "@neo4j-documentation/remote-include" + - "@neo4j-documentation/macros" + - "@neo4j-antora/mark-terms" + attributes: + # page-attributes are used by the ui-bundle and by extensions + page-theme: docs + page-type: Docs + page-search-type: Docs + page-search-site: Reference Docs + page-canonical-root: /docs + page-terms-to-mark: Cypher + page-pagination: true + page-no-canonical: true + page-origin-private: true # change to false to display 'Raise an issue' links + page-hide-toc: false + page-mixpanel: 4bfb2414ab973c741b6f067bf06d5575 + # legacy attributes - do not change these + includePDF: false + nonhtmloutput: "" + experimental: '' + # update the copyright value with the first commit in a new year + copyright: 2025 + # icon attributes + check-mark: icon:check[] + cross-mark: icon:times[] + # neo4j.com attributes. Always use when linking to neo4j.com URLs + neo4j-base-uri: '' + neo4j-docs-base-uri: /docs + common-license-page-uri: '{neo4j-docs-base-uri}/license' \ No newline at end of file diff --git a/docs/antora/server.js b/docs/antora/server.js new file mode 100644 index 00000000..294616fe --- /dev/null +++ b/docs/antora/server.js @@ -0,0 +1,10 @@ +const express = require('express') + +const app = express() +app.use(express.static('./build/site')) + +app.use('/static/assets', express.static('./build/site/assets')) + +app.get('/', (req, res) => res.redirect('/docs/')) + +app.listen(8000, () => console.log('📘 http://localhost:8000')) \ No newline at end of file diff --git a/docs/extra/.ipynb_checkpoints/getting-started-checkpoint.ipynb b/docs/extra/.ipynb_checkpoints/getting-started-checkpoint.ipynb deleted file mode 100644 index 2d08544a..00000000 --- a/docs/extra/.ipynb_checkpoints/getting-started-checkpoint.ipynb +++ /dev/null @@ -1,378 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "0d3ffc27", - "metadata": {}, - "source": [ - "# Getting started" - ] - }, - { - "cell_type": "markdown", - "id": "6b83277d", - "metadata": {}, - "source": [ - "In this section, we will cover the very basics of creating a visualization graph using the `neo4j-viz` library.\n", - "We will use a small toy graph representing the purchase history of a few people and products.\n", - "\n", - "To follow along with the example below, please make sure you have [installed the library](installation.rst) first.\n", - "\n", - "We start by instantiating the [Nodes](./api-reference/node.rst) and [Relationships](./api-reference/relationship.rst) we want in our graph.\n", - "The only mandatory fields for a node are the \"id\", and \"source\" and \"target\" for a relationship.\n", - "But the other fields can optionally be used to customize the appearance of the nodes and relationships in the visualization.\n", - "\n", - "Lastly we create a [VisualizationGraph](./api-reference/visualization-graph.rst) object with the nodes and relationships we created, and call its `render` method to display the graph.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "801d0bed", - "metadata": { - "tags": [ - "preserve-output" - ] - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - "
\n", - "
\n", - " \n", - " \n", - " \n", - "
\n", - "
\n", - "
\n", - "\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from neo4j_viz import Node, Relationship, VisualizationGraph\n", - "\n", - "nodes = [\n", - " Node(id=0, size=10, caption=\"Person\"),\n", - " Node(id=1, size=10, caption=\"Product\"),\n", - " Node(id=2, size=20, caption=\"Product\"),\n", - " Node(id=3, size=10, caption=\"Person\"),\n", - " Node(id=4, size=10, caption=\"Product\"),\n", - "]\n", - "relationships = [\n", - " Relationship(\n", - " source=0,\n", - " target=1,\n", - " caption=\"BUYS\",\n", - " ),\n", - " Relationship(\n", - " source=0,\n", - " target=2,\n", - " caption=\"BUYS\",\n", - " ),\n", - " Relationship(\n", - " source=3,\n", - " target=2,\n", - " caption=\"BUYS\",\n", - " ),\n", - "]\n", - "\n", - "VG = VisualizationGraph(nodes=nodes, relationships=relationships)\n", - "\n", - "VG.render(initial_zoom=2)" - ] - }, - { - "cell_type": "markdown", - "id": "365a1c31", - "metadata": {}, - "source": [ - "As we can see in the graph above, the radius of one of the nodes is larger than the others.\n", - "This is because we set the \"size\" field of the node to 20, while the others are set to 10.\n", - "\n", - "At this time all nodes have the same color.\n", - "If we want to distinguish between the different types of nodes, we can color them differently with the `color_nodes` method.\n", - "We can pass the field we want to use to color the nodes as an argument.\n", - "In this case, we will use the \"caption\" field.\n", - "Nodes with the same \"caption\" will have the same color.\n", - "We will use the default colorscheme, which is the Neo4j colorscheme.\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "d935b3d4", - "metadata": { - "tags": [ - "preserve-output" - ] - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - "
\n", - "
\n", - " \n", - " \n", - " \n", - "
\n", - "
\n", - "
\n", - "\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "VG.color_nodes(field=\"caption\")\n", - "\n", - "VG.render(initial_zoom=2)" - ] - }, - { - "cell_type": "markdown", - "id": "a28bd5aa", - "metadata": {}, - "source": [ - "We are now easily able to distinguish between the different types of nodes in the graph." - ] - }, - { - "cell_type": "markdown", - "id": "b3bb58465761e57a", - "metadata": {}, - "source": [ - "## Next steps\n", - "\n", - "Now that we have covered the basics of creating and rendering a visualization graph, we can move on to more advanced topics.\n", - "\n", - "Here are some suggestions for what to do next:\n", - "\n", - "* Browse the [notebook examples](./tutorials/index.rst) for more examples and use cases\n", - "* Learn about the [integrations](./integration.rst) with other libraries such as Neo4j GDS and Pandas \n", - "* Check out the [rendering](./rendering.rst) options and convenience methods of `VisualizationGraph`\n", - "* Read about our [API reference](./api-reference/index.rst)\n" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.9" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/docs/source/customizing.rst b/docs/source/customizing.rst deleted file mode 100644 index a1dbca86..00000000 --- a/docs/source/customizing.rst +++ /dev/null @@ -1,248 +0,0 @@ -Customizing the visualization -============================= - -Once created, a :doc:`VisualizationGraph object <./api-reference/visualization-graph>` can be modified in various ways -to adjust what the visualization looks like the next time you render it. -In this section we will discuss how to color, size, and pin nodes, as well as how to directly modify nodes and -relationships of existing ``VisualizationGraph`` objects. - -If you have not yet created a ``VisualizationGraph`` object, please refer to one of the following sections: - -* :doc:`Getting started <./getting-started>` for creating a visualization graph from scratch using ``neo4j-viz`` - primitives like :doc:`Node <./api-reference/node>` and :doc:`Relationship <./api-reference/relationship>` and - :doc:`VisualizationGraph <./api-reference/visualization-graph>` directly. Or -* :doc:`Integration with other libraries <./integration>` for importing data from a Pandas DataFrame or Neo4j GDS - projection. - -.. contents:: On this page: - :depth: 1 - :local: - :backlinks: none - - -Setting node captions ---------------------- - -Node captions are the text labels displayed on nodes in the visualization. - -The ``set_node_captions`` method -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -By calling the :meth:`neo4j_viz.VisualizationGraph.set_node_captions` method, you can set node captions based on a -node field (like ``id``, ``size``, etc.) or a node property (members of the ``Node.properties`` map). - -The method accepts an ``override`` parameter (default ``True``) that controls whether to replace existing captions. -If ``override=False``, only nodes without captions will be updated. - -Here's an example of setting node captions from a property: - -.. code-block:: python - - # VG is a VisualizationGraph object with nodes that have a "name" property - VG.set_node_captions(property="name") - -You can also set captions from a node field, and choose not to override existing captions: - -.. code-block:: python - - # VG is a VisualizationGraph object - VG.set_node_captions(field="id", override=False) - -For more complex scenarios where you need fallback logic or want to combine multiple properties, you can iterate over -nodes directly: - -.. code-block:: python - - # VG is a VisualizationGraph object - for node in VG.nodes: - caption = node.properties.get("name") or node.properties.get("title") or node.id - node.caption = str(caption) - - -Coloring nodes --------------- - -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 -:doc:`Node <./api-reference/node>` object. - -Alternatively, you can color nodes based on a field or property of the nodes after a ``VisualizationGraph`` object has been -created. - - -The ``color_nodes`` method -~~~~~~~~~~~~~~~~~~~~~~~~~~ - -By calling the :meth:`neo4j_viz.VisualizationGraph.color_nodes` method, you can color nodes based on a -node field or property (members of the `Node.properties` map). - -It's possible to color the nodes based on a discrete or continuous color space (see :doc:`ColorSpace <./api-reference/colors>`). -In the discrete case, a new color from the `colors` provided is assigned to each unique value of the node 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. - -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`` -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: - -.. code-block:: python - - 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%)"], - color_space=ColorSpace.DISCRETE - ) - -The full set of allowed values for colors are listed `here `_. - -Instead of defining your own colors, you could also for example use the color palettes from the `palettable library `_ as in -this snippet: - -.. code-block:: python - - from palettable.wesanderson import Moonrise1_5 - - # VG is a VisualizationGraph object - VG.color_nodes(field="caption", Moonrise1_5.colors) # PropertyType.DISCRETE is default - -In theses cases, all nodes with the same caption 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. -To avoid that, you could use a larger palette or extend one with additional colors. Please refer to the -:doc:`Visualizing Neo4j Graph Data Science (GDS) Graphs tutorial <./tutorials/gds-example>` 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: - -.. code-block:: python - - from neo4j_viz.colors import PropertyType - - # 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 - 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. -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 ------------- - -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 -:doc:`Node <./api-reference/node>` object. - -Alternatively, you can size nodes after a ``VisualizationGraph`` object has been created. - - -The ``resize_nodes`` method -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -By calling the :meth:`neo4j_viz.VisualizationGraph.resize_nodes` method, you can resize nodes by: - -* passing new nodes sizes as a dictionary ``sizes``, mapping node IDs to sizes in pixels, or -* providing a tuple of two numbers ``node_radius_min_max``: minimum and maximum radii (sizes) in pixels to which the - nodes will be scaled. - -Or you could provide both ``sizes`` and ``node_radius_min_max``, in which case the dictionary will be used to first set -the sizes of the nodes, and then the minimum and maximum values of the tuple will be subsequently used to scale the -sizes to the provided range. - -If you provide only the ``node_radius_min_max`` parameter, the sizes of the nodes will be scaled such that the smallest -node will have the size of the first value, and the largest node will have the size of the second value. -The other nodes will be scaled linearly between these two values according to their relative size. -This can be useful if node sizes vary a lot, or are all very small or very big. - -In the following example, we resize the node with ID 42 to have a size of 88 pixels, and then scales all nodes to have -sizes between 5 and 20 pixels: - -.. code-block:: python - - # VG is a VisualizationGraph object - VG.resize_nodes(sizes={42: 88}, node_radius_min_max=(5, 20)) - -Please note that means that also the node with ID 42 will be scaled to be between 5 and 20 pixels in size. - - -Pinning nodes -------------- - -Nodes can be pinned to their current position in the visualization, so that they will not be moved by the force-directed -layout algorithm. -This can be useful if you want to keep a node in a specific position, for example to highlight it. - -Nodes can be pinned directly upon creation. -This can for example be done by passing ``pinned=True`` to the :doc:`Node <./api-reference/node>` object. - -Alternatively, you can toggle node pinning after a ``VisualizationGraph`` object has been created. - - -The ``toggle_nodes_pinned`` method -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -By calling the :meth:`neo4j_viz.VisualizationGraph.toggle_nodes_pinned` method, you can toggle whether nodes should be -pinned or not. -This method takes dictionary that maps node IDs to boolean values, where ``True`` means that the node is pinned, and -``False`` means that the node is not pinned. - -In the following example, we pin the node with ID 1337 and unpin the node with ID 42: - -.. code-block:: python - - # VG is a VisualizationGraph object - VG.toggle_nodes_pinned(1337: True, 42: False)}) - - -Direct modification of nodes and relationships ----------------------------------------------- - -Nodes and relationships can also be modified directly by accessing the ``nodes`` and ``relationships`` fields of an -existing ``VisualizationGraph`` object. -These fields list of all the :doc:`Nodes <./api-reference/node>` and -:doc:`Relationships <./api-reference/relationship>` in the graph, respectively. - -Each node and relationship has fields that can be accessed and modified directly, as in the following example: - -.. code-block:: python - - # VG is a VisualizationGraph object - - # Modify the first node and fifth relationship - VG.nodes[0].size = 10 - VG.nodes[0].properties["height"] = 170 - VG.relationships[4].caption = "BUYS" - - # Set the coordinates for all nodes from an existing property - for node in VG.nodes: - node.x = node.properties.get("x") - node.y = node.properties.get("y") - - # Change the caption size for all relationships - for relationship in VG.relationships: - relationship.caption_size = 15 - - -Any changes made to the nodes and relationships will be reflected in the next rendering of the graph. diff --git a/docs/source/getting-started.nblink b/docs/source/getting-started.nblink deleted file mode 100644 index b79d8720..00000000 --- a/docs/source/getting-started.nblink +++ /dev/null @@ -1,3 +0,0 @@ -{ - "path": "../extra/getting-started.ipynb" -} \ No newline at end of file diff --git a/docs/source/index.rst b/docs/source/index.rst index 159d1a99..1db5eeb3 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -23,10 +23,6 @@ If you have a suggestion on how we can improve the library or want to report a p :glob: :maxdepth: 1 - installation.rst - getting-started.nblink - integration.rst - rendering.rst - customizing.rst - api-reference/index.rst + Graph Visualization docs tutorials/index.rst + api-reference/index.rst diff --git a/docs/source/installation.rst b/docs/source/installation.rst deleted file mode 100644 index c25259a0..00000000 --- a/docs/source/installation.rst +++ /dev/null @@ -1,65 +0,0 @@ -Installation -============ - - -To install the latest version of the library, you can simply run: - -.. code-block:: bash - - pip install neo4j-viz - - -Optional dependencies ---------------------- - -In addition, there are a few optional dependencies that you can install to enable additional features of the library. - - -Pandas DataFrames ``from_dfs`` importer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To install the additional dependencies required for the :doc:`from_dfs importer <./api-reference/from_pandas>` you can run: - -.. code-block:: bash - - pip install neo4j-viz[pandas] - - -Neo4j ``from_neo4j`` importer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To install the additional dependencies required for the :doc:`from_neo4j importer <./api-reference/from_neo4j>` you can run: - -.. code-block:: bash - - pip install neo4j-viz[neo4j] - - -Neo4j Graph Data Science ``from_gds`` importer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To install the additional dependencies required for the :doc:`from_gds importer <./api-reference/from_gds>` you can run: - -.. code-block:: bash - - pip install neo4j-viz[gds] - - -Snowflake tables ``from_snowflake`` importer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To install the additional dependencies required for the :doc:`from_snowflake importer <./api-reference/from_snowflake>` you can run: - -.. code-block:: bash - - pip install neo4j-viz[snowflake] - - -Notebook tutorials -~~~~~~~~~~~~~~~~~~ - -To install all the additional dependencies required for the :doc:`notebook examples <./tutorials/index>` you can run: - -.. code-block:: bash - - pip install neo4j-viz[notebook] diff --git a/docs/source/integration.rst b/docs/source/integration.rst deleted file mode 100644 index 0a5284d2..00000000 --- a/docs/source/integration.rst +++ /dev/null @@ -1,330 +0,0 @@ -Integration with other libraries -================================ - -In addition to creating graphs from scratch, with ``neo4j-viz`` as is shown in the -:doc:`Getting started section <./getting-started>`, you can also import data directly from external sources. -In this section we will cover how to import data from `Pandas DataFrames `_, -`Neo4j Graph Data Science `_, -`Neo4j Database `_, -`GQL CREATE queries `_, -and `Snowflake tables `_. - - -.. contents:: On this page: - :depth: 1 - :local: - :backlinks: none - - -Pandas DataFrames ------------------ - -The ``neo4j-viz`` library provides a convenience method for importing data from Pandas DataFrames. -These DataFrames can be created from many sources, such as CSV files. -It requires and additional dependency to be installed, which you can do by running: - -.. code-block:: bash - - pip install neo4j-viz[pandas] - -Once you have installed the additional dependency, you can use the :doc:`from_pandas <./api-reference/from_pandas>` method -to import pandas DataFrames. - -The ``from_dfs`` method takes two mandatory positional parameters: - -* A Pandas ``DataFrame``, or iterable (eg. list) of DataFrames representing the nodes of the graph. - The rows of the DataFrame(s) should represent the individual nodes, and the columns should represent the node - IDs and attributes. - The node ID will be set on the :doc:`Node <./api-reference/node>`, - Other columns will be a key in each node's `properties` dictionary, that maps to the node's corresponding - value in the column. - If the graph has no node properties, the nodes can be derived from the relationships DataFrame alone. -* A Pandas ``DataFrame``, or iterable (eg. list) of DataFrames representing the relationships of the graph. - The rows of the DataFrame(s) should represent the individual relationships, and the columns should represent the - relationship IDs and attributes. - The relationship id, source and target node IDs will be set on the :doc:`Relationship <./api-reference/relationship>`. - Other columns will be a key in each relationship's `properties` dictionary, that maps to the relationship's corresponding - value in the column. - - -Example -~~~~~~~ - -In this small example, we import a tiny toy graph representing a social network from two Pandas DataFrames. -As we can see the column names of the DataFrames map directly to the fields of :doc:`Nodes <./api-reference/node>` -and :doc:`Relationships <./api-reference/relationship>`. - -.. code-block:: python - - from pandas import DataFrame - from neo4j_viz.pandas import from_dfs - - nodes = DataFrame({ - "id": [1, 2, 3], - "caption": ["Alice", "Bob", "Charlie"], - "size": [20, 10, 10], - }) - relationships = DataFrame({ - "source": [1, 2], - "target": [2, 3], - "caption": ["LIKES", "KNOWS"], - }) - - VG = from_dfs(nodes, relationships) - - -Neo4j Graph Data Science (GDS) library --------------------------------------- - -The ``neo4j-viz`` library provides a convenience method for importing data from the Neo4j Graph Data Science (GDS) -library. -It requires and additional dependency to be installed, which you can do by running: - -.. code-block:: bash - - pip install neo4j-viz[gds] - -Once you have installed the additional dependency, you can use the :doc:`from_gds <./api-reference/from_gds>` method -to import projections from the GDS library. - -The ``from_gds`` method takes two mandatory positional parameters: - -* An initialized ``GraphDataScience`` object for the connection to the GDS instance, and -* A ``Graph`` representing the projection that one wants to import. - -The optional ``max_node_count`` parameter can be used to limit the number of nodes that are imported from the -projection. -By default, it is set to 10.000, meaning that if the projection has more than 10.000 nodes, ``from_gds`` will sample -from it using random walk with restarts, to get a smaller graph that can be visualized. -If you want to have more control of the sampling, such as choosing a specific start node for the sample, you can call -a `sampling `_ -method yourself and passing the resulting projection to ``from_gds``. - -The ``node_properties`` parameter is also optional, and should be a list of additional node properties of the -projection that you want to include in the visualization. -The default is ``None``, which means that all properties of the nodes in the projection will be included. -Apart from being visible through on-hover tooltips, these properties could be used to color the nodes, or give captions -to them in the visualization, or simply included in the nodes' ``Node.properties`` maps without directly impacting the -visualization. -If you want to include node properties stored at the Neo4j database, you can include them in the visualization by using the `db_node_properties` parameter. - - -Example -~~~~~~~ - -In this small example, we import a graph projection from the GDS library, that has the node properties "pagerank" and -"componentId". -We use the "pagerank" property to compute the size of the nodes, and the "componentId" property to color the nodes. - -.. code-block:: python - - from graphdatascience import GraphDataScience - from neo4j_viz.gds import from_gds - - gds = GraphDataScience(...) - G = gds.graph.project(...) - - # Compute the PageRank and Weakly Connected Components - gds.pageRank.mutate(G, mutateProperty="pagerank") - gds.wcc.mutate(G, mutateProperty="componentId") - - # Import the projection into a `VisualizationGraph` - # Make sure to include `pagerank` and `componentId` - VG = from_gds( - gds, - G, - node_properties=["componentId"], - ) - # Size the nodes by the `pagerank` property - VG.resize_nodes(property="pagerank") - - # Color the nodes by the `componentId` property, so that the nodes are - # colored by the connected component they belong to - VG.color_nodes(property="componentId") - - -Please see the :doc:`Visualizing Neo4j Graph Data Science (GDS) Graphs tutorial <./tutorials/gds-example>` for a -more extensive example. - - -Neo4j Database --------------- - -The ``neo4j-viz`` library provides a convenience method for importing data from Neo4j. -It requires and additional dependency to be installed, which you can do by running: - -.. code-block:: bash - - pip install neo4j-viz[neo4j] - -Once you have installed the additional dependency, you can use the :doc:`from_neo4j <./api-reference/from_neo4j>` method -to import query results from Neo4j. - -The ``from_neo4j`` method takes one mandatory positional parameter: -A ``data`` argument representing either a query result in the shape of a ``neo4j.graph.Graph`` or ``neo4j.Result``, or a -``neo4j.Driver`` in which case a simple default query will be executed internally to retrieve the graph data. - -The optional ``max_rows`` parameter can be used to limit the number of relationships shown in the visualization. -By default, it is set to 10.000, meaning that if the database has more than 10.000 rows, a warning will be raised. -Note, this only applies if the ``data`` parameter is a ``neo4j.Driver``. - - -Example -~~~~~~~ - -In this small example, we import a graph from a Neo4j query result. - -.. code-block:: python - - from neo4j import GraphDatabase, RoutingControl, Result - from neo4j_viz.gds import from_gds - - # Modify this to match your Neo4j instance's URI and credentials - URI = "neo4j://localhost:7687" - auth = ("neo4j", "password") - - with GraphDatabase.driver(URI, auth=auth) as driver: - driver.verify_connectivity() - - result = driver.execute_query( - "MATCH (n)-[r]->(m) RETURN n,r,m", - database_="neo4j", - routing_=RoutingControl.READ, - result_transformer_=Result.graph, - ) - - VG = from_neo4j(result) - - -Please see the :doc:`Visualizing Neo4j Graphs tutorial <./tutorials/neo4j-example>` for a -more extensive example. - - -GQL ``CREATE`` query --------------------- - -The ``neo4j-viz`` library provides convenience for creating visualization graphs from GQL ``CREATE`` queries via the :doc:`from_gql_create <./api-reference/from_gql_create>` method. - -The ``from_gql_create`` method takes one mandatory positional parameter: - -* A valid ``query`` representing a GQL ``CREATE`` query as a string. - - -Example -~~~~~~~ - -In this small example, we create a visualization graph from a GQL ``CREATE`` query. - -.. code-block:: python - - from neo4j_viz.gql_create import from_gql_create - - query = """ - CREATE - (a:User {name: 'Alice', age: 23}), - (b:User {name: 'Bridget', age: 34}), - (c:User {name: 'Charles', age: 45}), - (d:User {name: 'Dana', age: 56}), - (e:User {name: 'Eve', age: 67}), - (f:User {name: 'Fawad', age: 78}), - - (a)-[:LINK {weight: 0.5}]->(b), - (a)-[:LINK {weight: 4}]->(c), - (e)-[:LINK {weight: 1.1}]->(d), - (e)-[:LINK {weight: -2}]->(f); - """ - - VG = from_gql_create(query) - - -Snowflake Tables ----------------- - -The ``neo4j-viz`` library provides a convenience method for importing data from Snowflake tables. -It requires and additional dependency to be installed, which you can do by running: - -.. code-block:: bash - - pip install neo4j-viz[snowflake] - -Once you have installed the additional dependency, you can use the :doc:`from_snowflake <./api-reference/from_snowflake>` method -to import Snowflake tables into a ``VisualizationGraph``. - -The ``from_snowflake`` method takes two mandatory positional parameters: - -* A ``snowflake.snowpark.Session`` object for the connection to Snowflake, and -* A `project configuration `_ as a dictionary, that specifies how you want your tables to be projected as a graph. - This configuration is the same as the project configuration of the `Neo4j Snowflake Graph Analytics application `_. - -You can further customize the visualization after the `VisualizationGraph` has been created, by using the methods described in the :doc:`Customizing the visualization <./customizing>` section. - - -Default behavior -~~~~~~~~~~~~~~~~ - -The node and relationship captions will be set to the names of the corresponding tables. -The nodes will be colored so that nodes from the same table have the same color, and different tables have different colors. - - -Example -~~~~~~~ - -In this small example, we import a toy graph representing a social network from two tables in Snowflake. - -.. code-block:: python - - from snowflake.snowpark import Session - from neo4j_viz.snowflake import from_dfs - - # Configure according to your own setup - connection_parameters = { - "account": os.environ.get("SNOWFLAKE_ACCOUNT"), - "user": os.environ.get("SNOWFLAKE_USER"), - "password": os.environ.get("SNOWFLAKE_PASSWORD"), - "role": os.environ.get("SNOWFLAKE_ROLE"), - "warehouse": os.environ.get("SNOWFLAKE_WAREHOUSE"), - } - - session.sql( - "CREATE OR REPLACE TABLE EXAMPLE_DB.DATA_SCHEMA.PERSONS (NODEID VARCHAR);" - ).collect() - - session.sql(""" - INSERT INTO EXAMPLE_DB.DATA_SCHEMA.PERSONS VALUES - ('Alice'), - ('Bob'), - ('Carol'), - ('Dave'), - ('Eve'); - """).collect() - - session.sql( - "CREATE OR REPLACE TABLE EXAMPLE_DB.DATA_SCHEMA.KNOWS (SOURCENODEID VARCHAR, TARGETNODEID VARCHAR);" - ).collect() - - session.sql(""" - INSERT INTO EXAMPLE_DB.DATA_SCHEMA.KNOWS VALUES - ('Alice', 'Dave'), - ('Alice', 'Carol'), - ('Bob', 'Carol'), - ('Dave', 'Eve'), - """).collect() - - VG = from_snowflake( - session, - { - "nodeTables": [ - "EXAMPLE_DB.DATA_SCHEMA.PERSONS", - ], - "relationshipTables": { - "EXAMPLE_DB.DATA_SCHEMA.KNOWS": { - "sourceTable": "EXAMPLE_DB.DATA_SCHEMA.PERSONS", - "targetTable": "EXAMPLE_DB.DATA_SCHEMA.PERSONS", - "orientation": "UNDIRECTED", - } - }, - }, - ) - -For a full example of the ``from_snowflake`` importer in action, please see the -:doc:`Visualizing Snowflake Tables tutorial <./tutorials/snowflake-example>`. diff --git a/docs/source/rendering.rst b/docs/source/rendering.rst deleted file mode 100644 index 8e648586..00000000 --- a/docs/source/rendering.rst +++ /dev/null @@ -1,72 +0,0 @@ -Rendering a graph -================= - -In this section, we will discuss how to render a :doc:`VisualizationGraph object <./api-reference/visualization-graph>` -to display the graph visualization. - -If you have not yet created a ``VisualizationGraph`` object, please refer to one of the following sections: - -* :doc:`Getting started <./getting-started>` for creating a visualization graph from scratch using ``neo4j-viz`` - primitives like :doc:`Node <./api-reference/node>` and :doc:`Relationship <./api-reference/relationship>` and - :doc:`VisualizationGraph <./api-reference/visualization-graph>` directly. Or -* :doc:`Integration with other libraries <./integration>` for importing data from a Pandas DataFrame or Neo4j GDS - projection. - -.. contents:: On this page: - :depth: 1 - :local: - :backlinks: none - - -The ``render`` method ---------------------- - -Once you have a ``VisualizationGraph`` object, you can render it using the ``render`` method. -This will return a HTML object that will be displayed in an environment that supports HTML rendering, such as -Jupyter notebooks or streamlit application. - -All parameter of the ``render`` method are optional, and the full list of parameters of them is listed in the API -reference: :meth:`neo4j_viz.VisualizationGraph.render`. - -The most important parameters to be aware of are the ``width`` and ``height`` parameters, which control the size of -HTML object that will be rendered. -You can provide these either as a percentage of the available space (eg. ``"80%"``), or as an absolute pixel value -(eg. ``"800px"``). - -Further you can change the layout of the graph using the ``layout`` parameter, which can be set to one of the following values: - -* ``Layout.FORCE_DIRECTED`` - Nodes are arranged using the Force-Directed algorithm, which simulates physical forces. To customize the layout, use `ForceDirectedOptions` via `layout_options`.` -* ``Layout.HIERARCHICAL`` - Arranges nodes by the directionality of their relationships, creating a tree-like structure. To customize the layout use `HierarchicalLayoutOptions` via `layout_options`.` -* ``Layout.COORDINATE`` - Arranges nodes based on coordinates defined in `x` and `y` properties on each node. - -Another thing of note is the ``max_allowed_nodes`` parameter, which controls the maximum number of nodes that is allowed -for the graph to contain in order to be rendered. -It defaults to 10.000, because rendering a large number of nodes can be slow and unresponsive. -However, you can increase this value if you are confident that your environment can handle the scale. -In this case you might also want to pass ``Renderer.WEB_GL`` as the ``renderer`` to improve performance. - -By default a tooltip showing IDs and properties will be shown when mouse hovering over a node or relationship. -But you can disable this by passing ``show_hover_tooltip=False``. - - -Examples -~~~~~~~~ - -Please refer to the :doc:`Getting started section <./getting-started>` and the :doc:`tutorials <./tutorials/index>` for -examples of ``render`` method usage. - - -Exporting to HTML -~~~~~~~~~~~~~~~~~ - -The object returned by the ``render`` method is a ``IPython.display.HTML`` object. -In addition to being displayed in a Jupyter notebook or streamlit application, it can also be saved as an HTML file. -This could be useful if you want to share the visualization with others or embed it in a web page. - -To save the HTML data to a file, you can use the ``data`` attribute of the HTML object: - -.. code-block:: python - - html = VG.render(...) - with open("my_graph.html", "w") as f: - f.write(html.data) diff --git a/docs/source/tutorials/getting-started.nblink b/docs/source/tutorials/getting-started.nblink new file mode 100644 index 00000000..7cbbf076 --- /dev/null +++ b/docs/source/tutorials/getting-started.nblink @@ -0,0 +1,3 @@ +{ + "path": "../../../examples/getting-started.ipynb" +} diff --git a/docs/source/tutorials/index.rst b/docs/source/tutorials/index.rst index 3999307a..bfc22864 100644 --- a/docs/source/tutorials/index.rst +++ b/docs/source/tutorials/index.rst @@ -3,7 +3,7 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Tutorials +Jupyter notebooks ===================================================== This chapter contains Jupyter notebook tutorials for the `neo4j-viz` package. @@ -14,4 +14,5 @@ This chapter contains Jupyter notebook tutorials for the `neo4j-viz` package. :maxdepth: 1 :caption: Contents: + getting-started * diff --git a/docs/extra/getting-started.ipynb b/examples/getting-started.ipynb similarity index 99% rename from docs/extra/getting-started.ipynb rename to examples/getting-started.ipynb index 6e0d4560..d30d9f06 100644 --- a/docs/extra/getting-started.ipynb +++ b/examples/getting-started.ipynb @@ -16,13 +16,11 @@ "In this section, we will cover the very basics of creating a visualization graph using the `neo4j-viz` library.\n", "We will use a small toy graph representing the purchase history of a few people and products.\n", "\n", - "To follow along with the example below, please make sure you have [installed the library](installation.rst) first.\n", - "\n", - "We start by instantiating the [Nodes](./api-reference/node.rst) and [Relationships](./api-reference/relationship.rst) we want in our graph.\n", + "We start by instantiating the `Nodes` and `Relationships` we want in our graph.\n", "The only mandatory fields for a node are the \"id\", and \"source\" and \"target\" for a relationship.\n", "But the other fields can optionally be used to customize the appearance of the nodes and relationships in the visualization.\n", "\n", - "Lastly we create a [VisualizationGraph](./api-reference/visualization-graph.rst) object with the nodes and relationships we created, and call its `render` method to display the graph.\n" + "Lastly we create a `VisualizationGraph` object with the nodes and relationships we created, and call its `render` method to display the graph.\n" ] }, { @@ -335,23 +333,6 @@ "source": [ "We are now easily able to distinguish between the different types of nodes in the graph." ] - }, - { - "cell_type": "markdown", - "id": "b3bb58465761e57a", - "metadata": {}, - "source": [ - "## Next steps\n", - "\n", - "Now that we have covered the basics of creating and rendering a visualization graph, we can move on to more advanced topics.\n", - "\n", - "Here are some suggestions for what to do next:\n", - "\n", - "* Browse the [notebook examples](./tutorials/index.rst) for more examples and use cases\n", - "* Learn about the [integrations](./integration.rst) with other libraries such as Neo4j GDS and Pandas \n", - "* Check out the [rendering](./rendering.rst) options and convenience methods of `VisualizationGraph`\n", - "* Read about our [API reference](./api-reference/index.rst)\n" - ] } ], "metadata": { diff --git a/scripts/render_and_host_docs.sh b/scripts/render_and_host_docs.sh index 808196c8..78392d06 100755 --- a/scripts/render_and_host_docs.sh +++ b/scripts/render_and_host_docs.sh @@ -13,4 +13,4 @@ GIT_ROOT=$(git rev-parse --show-toplevel) make clean html ) -python3 -m http.server 8000 -d "${GIT_ROOT}/docs/build/html" +python3 -m http.server 9000 -d "${GIT_ROOT}/docs/build"