Aura Graph Analytics: FastPath embeddings on temporal data
This Jupyter notebook is hosted here in the Neo4j Graph Data Science Client Github repository.
The notebook shows how to use the graphdatascience Python library to
run the FastPath node embedding algorithm on an Aura Graph Analytics
(AGA) Session.
FastPath is a lightweight path-embedding algorithm designed for temporal graphs. It computes vector embeddings for base nodes (e.g. customers, entities) by aggregating the feature vectors of their associated event nodes (e.g. transactions, interactions) weighted by how recently each event occurred.
Note: FastPath is a preview feature and may change or be removed in future releases. It requires a session backed by the new Python runtime.
We model a small temporal graph of Entity nodes and Event nodes
where each event carries a numerical feature vector and a timestamp. We
load this graph from Pandas DataFrames, run FastPath in stream mode, and
inspect the resulting embeddings.
If you are using AuraDB, follow this example. If you are using a self-managed Neo4j instance, follow this example.
1. Prerequisites
This notebook requires having the Aura Graph Analytics feature enabled for your Neo4j Aura project, and a session backed by the Python runtime (required for FastPath support).
You also need to have the graphdatascience Python library installed,
version 2.0a1 or later.
%pip install "graphdatascience>=2.0a1" python-dotenv
from dotenv import load_dotenv
# This allows to load required secrets from `.env` file in local directory
# This can include Aura API Credentials. If file does not exist this is a noop.
load_dotenv(".env")
2. Aura API credentials
The entry point for managing GDS Sessions is the GdsSessions object,
which requires creating
Aura API credentials.
import os
from graphdatascience.session import AuraAPICredentials, GdsSessions
# you can also use AuraAPICredentials.from_env() to load credentials from environment variables
api_credentials = AuraAPICredentials(
client_id=os.environ["CLIENT_ID"],
client_secret=os.environ["CLIENT_SECRET"],
# If your account is a member of several project, you must also specify the project ID to use
project_id=os.environ.get("PROJECT_ID", None),
)
sessions = GdsSessions(api_credentials=api_credentials)
3. Creating a new session
A new session is created by calling sessions.get_or_create()
with the following parameters:
-
A session name, which lets you reconnect to an existing session by calling
get_or_createagain. -
The session memory.
-
The cloud location.
-
A time-to-live (TTL), which ensures that the session is automatically deleted after being unused for the set time, to avoid incurring costs.
See the API reference documentation or the manual for more details on the parameters.
from graphdatascience.session import AlgorithmCategory, CloudLocation, SessionMemory
# Estimate the memory needed for the GDS session
memory = sessions.estimate(
node_count=20,
relationship_count=10,
algorithm_categories=[AlgorithmCategory.NODE_EMBEDDING],
)
print(f"Estimated memory: {memory}")
# Explicitly define the size of the session
memory = SessionMemory.m_2GB
# Specify your cloud location
cloud_location = CloudLocation("gcp", "europe-west1")
# You can find available cloud locations by calling
cloud_locations = sessions.available_cloud_locations()
print(f"Available locations: {cloud_locations}")
from datetime import timedelta
# Create a GDS session!
gds = sessions.get_or_create(
# we give it a representative name
session_name="fastpath-temporal",
memory=memory,
ttl=timedelta(minutes=30),
cloud_location=cloud_location,
)
# Verify the connectivity. Hints towards TLS or firewall issues if this fails directly after get_or_create
gds.verify_connectivity()
4. Listing sessions
You can use sessions.list() to see the details for each created
session.
from pandas import DataFrame
gds_sessions = sessions.list()
# for better visualization
DataFrame(gds_sessions)
5. Projecting a temporal dataset
FastPath operates on a bipartite temporal graph consisting of two node types:
-
Entity nodes — the base nodes for which embeddings are computed. Each Entity has an
output_timeproperty that defines the reference timestamp up to which events are considered. -
Event nodes — the event nodes linked to each Entity. Each Event has an
event_timetimestamp and anevent_featuresvector of numerical features.
Events are connected to their Entity via HAS_EVENT relationships.
AGA sessions always start empty, with no data. So our first step will be to project data into the session. In this example, we will illustrate how to do this using Pandas DataFrames.
import pandas as pd
nodes = [
pd.DataFrame(
[
{"nodeId": 1, "labels": "Entity", "output_time": 120},
{"nodeId": 2, "labels": "Entity", "output_time": 240},
{"nodeId": 3, "labels": "Entity", "output_time": 300},
]
),
pd.DataFrame(
[
{"nodeId": 101, "labels": "Event", "event_time": 10, "event_features": [0.1, 0.0, 0.3, 0.2]},
{"nodeId": 102, "labels": "Event", "event_time": 30, "event_features": [0.0, 0.2, 0.1, 0.4]},
{"nodeId": 103, "labels": "Event", "event_time": 60, "event_features": [0.3, 0.1, 0.0, 0.2]},
{"nodeId": 104, "labels": "Event", "event_time": 90, "event_features": [0.2, 0.3, 0.2, 0.1]},
{"nodeId": 105, "labels": "Event", "event_time": 150, "event_features": [0.4, 0.1, 0.2, 0.0]},
{"nodeId": 106, "labels": "Event", "event_time": 210, "event_features": [0.1, 0.4, 0.2, 0.3]},
]
),
]
relationships = [
pd.DataFrame(
[
{"sourceNodeId": 1, "targetNodeId": 101, "relationshipType": "HAS_EVENT"},
{"sourceNodeId": 1, "targetNodeId": 102, "relationshipType": "HAS_EVENT"},
{"sourceNodeId": 2, "targetNodeId": 103, "relationshipType": "HAS_EVENT"},
{"sourceNodeId": 2, "targetNodeId": 104, "relationshipType": "HAS_EVENT"},
{"sourceNodeId": 3, "targetNodeId": 105, "relationshipType": "HAS_EVENT"},
{"sourceNodeId": 3, "targetNodeId": 106, "relationshipType": "HAS_EVENT"},
]
)
]
6. Construct Graph from DataFrames
With DataFrames in hand, the next step is to build a graph from them. We
do that by using the gds.graph.construct() function.
After calling this function, we will get a Graph Object back, representing the graph that now exists within the AGA session. We will use it as input to the FastPath algorithm.
G = gds.graph.construct("temporal-events", nodes, relationships)
str(G)
7. Running FastPath
FastPath computes embeddings for the Entity nodes by aggregating the
event_features vectors of their linked Event nodes, weighted by
the elapsed time between each event and the entity’s output_time.
We run it in stream mode, which returns the embeddings as a DataFrame without storing them in the session.
Note: You may see a
UserWarningthat FastPath is a preview feature — this is expected.
embedding_df = gds.fast_path.stream(
G,
base_node_label="Entity",
event_node_label="Event",
dimension=8,
max_elapsed_time=365 * 2,
num_elapsed_times=30,
event_features="event_features",
output_time_property="output_time",
time_node_property="event_time",
smoothing_rate=0.9,
smoothing_window=2,
decay_factor=0.0,
)
embedding_df
The result is a DataFrame with one row per Entity node. The
embeddings column contains the computed embedding vector for each
entity.
8. Deleting the session
After the analysis is done, you can delete the session. As this example is not connected to a Neo4j DB, you need to make sure the algorithm results are persisted on your own.
Deleting the session will release all resources associated with it, and stop incurring costs.
# or gds.delete()
sessions.delete(session_name="fastpath-temporal")
# let's also make sure the deleted session is truly gone:
sessions.list()