图对象
为了使用 GDS 中的大多数功能,您必须首先将图投影到 GDS 图目录 (Graph Catalog) 中。当使用 Python 客户端投影图时,会返回一个指向已投影图的客户端引用。我们将这些引用称为 Graph 对象。
一旦创建,Graph 对象就可以作为参数传递给 Python 客户端中的其他方法,例如用于运行算法或训练机器学习模型。此外,Graph 对象还具有便捷方法,允许在不显式涉及图目录的情况下检查所代表的投影图。
在下面的示例中,我们假设已经实例化了一个名为 gds 的 GraphDataScience 对象。请在 入门指南 中阅读更多相关信息。
1. 投影图对象
投影图对象有多种方法。最简单的方法是执行 原生投影 (native projection)
# We put this simple graph in our database
gds.run_cypher(
"""
CREATE
(m: City {name: "Malmö"}),
(l: City {name: "London"}),
(s: City {name: "San Mateo"}),
(m)-[:FLY_TO]->(l),
(l)-[:FLY_TO]->(m),
(l)-[:FLY_TO]->(s),
(s)-[:FLY_TO]->(l)
"""
)
# We estimate required memory of the operation
res = gds.graph.project.estimate(
["City"], # Node projection
"FLY_TO", # Relationship projection
readConcurrency=4 # Configuration parameters
)
assert res["bytesMax"] < 1e12
G, result = gds.graph.project(
"offices", # Graph name
["City"], # Node projection
"FLY_TO", # Relationship projection
readConcurrency=4 # Configuration parameters
)
assert G.node_count() == result["nodeCount"]
其中 G 是一个 Graph 对象,result 是一个包含底层过程调用元数据的 pandas Series。
请注意,通过为节点和关系投影参数指定 Python dict 或 list,可以支持所有投影语法变体。要指定对应于过程 configuration 映射键的配置参数,我们使用命名关键字参数,例如上面的 readConcurrency=4。在 GDS 手册 中阅读有关语法的更多信息。
与 Cypher 类似,也有一个相应的 gds.graph.project.estimate 方法,可以以类似的方式调用。
要获取一个表示已投影到图目录中的图的图对象,可以调用仅限客户端的 get 方法并传递一个名称
G = gds.graph.get("offices")
对于身为 GDS 管理员 的用户,即使提供的名称是指其他用户的图投影,gds.graph.get 也会将图名称解析为 Graph 对象。
除了上述方法外,还有五种方法可以创建图对象
-
gds.graph.project.cypher(这是旧版 Cypher 投影,请参阅 使用 Cypher 投影投影图 以获取新的 Cypher 投影) -
gds.graph.filter -
gds.graph.generate -
gds.graph.sample.rwr -
gds.graph.sample.cnarw
它们的 Cypher 签名映射到 Python 的方式与上面的 gds.graph.project 大致相同。
2. 使用 Cypher 投影投影图
方法 gds.graph.cypher.project 允许使用 Cypher 投影来投影图。Cypher 投影不是一个专用的过程;相反,它需要编写一个调用 gds.graph.project 聚合函数的 Cypher 查询。
在 GDS 手册 中阅读有关 Cypher 投影的更多信息。
方法 gds.graph.cypher.project 弥合了 gds.run_cypher 与后续必须使用 gds.graph.get 之间的差距。
2.1. 语法
| 名称 | 类型 | 默认 | 描述 |
|---|---|---|---|
|
|
|
要执行的 Cypher 查询。必须以 |
|
|
|
覆盖目标数据库。默认使用连接中的数据库。 |
|
|
|
作为关键字参数的查询参数。 |
与 gds.run_cypher 不同,但与 gds.graph.project 非常相似,它返回一个 Graph 对象和包含 Cypher 执行元数据的 pandas Series 的元组。
该方法不会以任何方式修改 Cypher 查询,所有投影配置都必须在查询本身中完成。但是,该方法会验证查询是否仅包含一个 RETURN gds.graph.project(…) 子句,并且该子句出现在查询末尾。这意味着此方法不能用于将图与其他 Cypher 聚合一起投影,也不能重命名结果行。如果提供的查询验证失败,则回退方案是使用 gds.run_cypher 结合 gds.graph.get 来实现相同的结果。
# We put this simple graph in our database
gds.run_cypher(
"""
CREATE
(m: City {name: "Malmö"}),
(l: City {name: "London"}),
(s: City {name: "San Mateo"}),
(m)-[:FLY_TO]->(l),
(l)-[:FLY_TO]->(m),
(l)-[:FLY_TO]->(s),
(s)-[:FLY_TO]->(l)
"""
)
G, result = gds.graph.cypher.project(
"""
MATCH (n)-->(m)
RETURN gds.graph.project($graph_name, n, m, {
sourceNodeLabels: $label,
targetNodeLabels: $label,
relationshipType: $rel_type
})
""", # Cypher query
database="neo4j", # Target database
graph_name="offices", # Query parameter
label="City", # Query parameter
rel_type="FLY_TO" # Query parameter
)
assert G.node_count() == result["nodeCount"]
3. 从 DataFrames 构建图
除了从 Neo4j 数据库投影图外,还可以直接从 pandas DataFrame 对象创建图。
3.1. 语法
| 名称 | 类型 | 默认 | 描述 |
|---|---|---|---|
|
|
|
要构建的图的名称。 |
|
|
|
一个或多个包含节点数据的数据框。 |
|
|
|
一个或多个包含关系数据的数据框。 |
|
|
|
用于构建图的线程数。 |
|
|
|
要作为无向投影的关系类型列表。 |
3.2. 示例
nodes = pandas.DataFrame(
{
"nodeId": [0, 1, 2, 3],
"labels": ["A", "B", "C", "A"],
"prop1": [42, 1337, 8, 0],
"otherProperty": [0.1, 0.2, 0.3, 0.4]
}
)
relationships = pandas.DataFrame(
{
"sourceNodeId": [0, 1, 2, 3],
"targetNodeId": [1, 2, 3, 0],
"relationshipType": ["REL", "REL", "REL", "REL"],
"weight": [0.0, 0.0, 0.1, 42.0]
}
)
G = gds.graph.construct(
"my-graph", # Graph name
nodes, # One or more dataframes containing node data
relationships # One or more dataframes containing relationship data
)
assert "REL" in G.relationship_types()
上面的示例从两个 DataFrame 对象创建一个图,一个用于节点,一个用于关系。投影的图等同于以下 Cypher 查询在 Neo4j 数据库中创建的图
CREATE
(a:A {prop1: 42, otherProperty: 0.1),
(b:B {prop1: 1337, otherProperty: 0.2),
(c:C {prop1: 8, otherProperty: 0.3),
(d:A {prop1: 0, otherProperty: 0.4),
(a)-[:REL {weight: 0.0}]->(b),
(b)-[:REL {weight: 0.0}]->(c),
(c)-[:REL {weight: 0.1}]->(d),
(d)-[:REL {weight: 42.0}]->(a),
节点数据框支持的格式在 Arrow 节点模式 中描述,关系数据框的格式在 Arrow 关系模式 中描述。
3.3. Apache Arrow Flight 服务器支持
如果启用了 GDS 的 Apache Arrow Flight 服务器,construct 方法可以利用它。这特别意味着
-
图的构建速度大大加快,
-
可以提供多个数据框,无论是节点还是关系。如果使用多个节点数据框,它们需要在所有节点数据框中包含不同的节点 ID。
-
在调用
construct之前,必须先调用GraphDataScience.set_database以明确指定应针对哪个 Neo4j 数据库。
3.4. 社区版限制
对于 GDS 社区版用户,大型图的性能可能会受到影响。数据库的套接字连接可能会超时。如果发生这种情况,一种可能的解决方法是修改服务器配置 server.bolt.connection_keep_alive 或 server.bolt.connection_keep_alive_probes。但是,请注意副作用,例如真正的连接问题现在需要更长时间才能被检测到。
4. 加载 NetworkX 图
从客户端数据构建图的另一种方法是使用该库方便的 NetworkX 加载方法。为了使用此方法,必须为 graphdatascience 库安装 NetworkX 支持
pip install graphdatascience[networkx]
公开 NetworkX 数据集加载功能的方法称为 gds.graph.networkx.load。它返回 Graph 对象,并接受三个参数
| 名称 | 类型 | |
|---|---|---|
|
|
NetworX 格式的图 |
|
|
创建的 GDS 图的名称 |
|
|
可选使用的线程数 |
networkx.Graph nx_G 如何映射到 GDS Graph 投影的具体细节将在下文中概述。
4.1. 示例
让我们看一个加载最小异构玩具 NetworkX 图的示例。
import networkx as nx
# Construct a directed NetworkX graph
nx_G = nx.DiGraph()
nx_G.add_node(1, labels=["Person"], age=52)
nx_G.add_node(42, labels=["Product", "Item"], cost=17.2)
nx_G.add_edge(1, 42, relationshipType="BUYS", quantity=4)
# Load the graph into GDS
G = gds.graph.networkx.load(nx_G, "purchases")
# Verify that the projection is what we expect
assert G.name() == "purchases"
assert G.node_count() == 2
assert set(G.node_labels()) == {"Person", "Product", "Item"}
assert G.node_properties("Person") == ["age"]
assert G.node_properties("Product") == ["cost"]
# Count rel not being = 2 indicates the graph is indeed directed
assert G.relationship_count() == 1
assert G.relationship_types() == ["BUYS"]
assert G.relationship_properties("BUYS") == ["quantity"]
|
结合 NetworkX 读取各种图格式的功能,可以轻松地将边缘列表和 GML 等流行图格式加载到 GDS 中。 |
|
结合 NetworkX 生成各种图的功能,可以轻松地将文献中流行的图类型加载到 GDS 中,例如扩展图、棒棒糖图、完全图等。 |
4.2. NetworkX 模式到 GDS 模式
关于 NetworkX 图如何映射到 GDS 中投影的 Graph,有一些规则。它们遵循与 从 DataFrames 构建图 类似的原则,并在此部分中有详细概述。
4.2.1. 节点标签
投影的 GDS 图的节点标签取自 networkx.Graph 节点的属性。节点属性键 labels 的值将决定节点在投影中被赋予什么标签。这些值应该是字符串或字符串列表。
networkx.Graph 的所有节点必须具有有效的 labels 属性,或者可以从图中完全省略它们。也就是说,也允许没有任何 labels 节点属性的 networx.Graph。在后一种情况下,投影的 Graph 中的节点都将具有节点标签 N。
4.2.2. 节点属性
投影的 GDS 图的节点属性取自 networkx.Graph 节点的属性。属性的键将映射到属性名称,允许的值必须遵循 GDS 中节点属性值的常规准则。但请注意,节点属性键 labels 保留用于节点标签(上一节),不会转换为投影中的节点属性。
4.2.3. 关系类型
投影的 GDS 图的关系类型取自 networkx.Graph 边的属性。边属性键 relationshipType 的值将决定关系在投影中被赋予什么类型。这些值应该是字符串或被省略。
networkx.Graph 的所有边必须具有有效的 relationshipType 属性,或者可以从图中完全省略它们。也就是说,也允许没有任何 relationshipType 边属性的 networx.Graph。在后一种情况下,投影的 Graph 中的关系都将具有关系类型 R。
4.3. 社区版限制
对于 GDS 社区版用户,大型图的性能可能会受到影响。数据库的套接字连接可能会超时。如果发生这种情况,一种可能的解决方法是修改服务器配置 server.bolt.connection_keep_alive 或 server.bolt.connection_keep_alive_probes。但是,请注意副作用,例如真正的连接问题现在需要更长时间才能被检测到。
5. 检查图对象
图对象上有便捷方法,让我们能够提取有关我们投影的图的信息。
| 名称 | 参数 | 返回类型 | 描述 |
|---|---|---|---|
|
|
|
投影图的名称。 |
|
|
|
图已投影到的数据库名称。 |
|
|
|
投影图的节点数。 |
|
|
|
投影图的关系数。 |
|
|
|
图中存在的节点标签列表。 |
|
|
|
图中存在的关系类型列表。 |
|
|
|
如果给定了 label 参数,则返回具有提供的节点标签的节点上存在的属性列表。否则,返回一个 |
|
|
|
如果给定了 type 参数,则返回具有提供的关系类型的关系上存在的属性列表。否则,返回一个 |
|
|
|
生成节点的平均出度。 |
|
|
|
图的密度。 |
|
|
|
Java 堆中用于存储图的字节数。 |
|
|
|
|
|
|
|
如果图存在于 GDS 图目录中,则返回 |
|
|
|
|
|
|
|
用于在内存中投影图的配置。 |
|
|
|
图投影的时间。 |
|
|
|
图最后一次被修改的时间。 |
例如,要获取图 G 的节点数和节点属性,我们可以执行以下操作
n = G.node_count()
props = G.node_properties("City")
6. 上下文管理
图对象还实现了上下文管理协议,即可以在 with 子句中使用。在退出 with 块时,图投影将自动在服务器端被删除。
# We use the example graph from the `Projecting a graph object` section
with gds.graph.project(
"tmp_offices", # Graph name
["City"], # Node projection
"FLY_TO", # Relationship projection
readConcurrency=4 # Configuration parameters
)[0] as G_tmp:
assert G_tmp.exists()
# Outside of the with block the Graph does not exist
assert not gds.graph.exists("tmp_offices")["exists"]
7. 使用图对象
图对象的主要用例是将它传递给算法,但它也是 GDS 图目录 大多数方法的输入。
7.1. 算法的输入
使用 Graph 作为算法输入的 Python 客户端语法遵循 GDS Cypher 过程 API,其中图是传递给算法的第一个参数。
result = gds[.<tier>].<algorithm>.<execution-mode>[.<estimate>](
G: Graph,
**configuration: dict[str, any]
)
在此示例中,我们在图 G 上运行度中心性算法
result = gds.degree.mutate(G, mutateProperty="degree")
assert "centralityDistribution" in result
7.2. 图目录
GDS 图目录 的所有过程在客户端中都有相应的 Python 方法。在那些接受图名称字符串作为输入的过程目录中,它们的 Python 客户端对应项改为接受 Graph 对象,但 gds.graph.exists 除外,它仍然接受图名称字符串。
以下是一些如何通过客户端使用 GDS 图目录的示例,假设我们从上面的示例中检查图 G
# List graphs in the catalog
list_result = gds.graph.list()
# Check for existence of a graph in the catalog
exists_result = gds.graph.exists("offices")
assert exists_result["exists"]
# Stream the node property 'degree'
result = gds.graph.nodeProperty.stream(G, node_property="degree")
# Drop a graph; same as G.drop()
gds.graph.drop(G)
7.2.1. 流式传输属性
客户端方法
-
gds.graph.nodeProperty.stream(之前是gds.graph.streamNodeProperty) -
gds.graph.nodeProperties.stream(之前是gds.graph.streamNodeProperties) -
gds.graph.relationshipProperty.stream(之前是gds.graph.streamRelationshipProperty) -
gds.graph.relationshipProperties.stream(之前是gds.graph.streamRelationshipProperties)
如果启用了 GDS 的 Apache Arrow Flight 服务器,这些方法的速度将大大加快。
此外,为 gds.graph.streamNodeProperties 和 gds.graph.streamRelationshipProperties 设置仅客户端的可选关键字参数 separate_property_columns=True(默认为 False),将返回一个 pandas DataFrame,其中请求的每个属性都有自己的列。请注意,这不同于默认行为,默认行为中只有一个名为 propertyValue 的列,其中包含为每个节点或关系交错请求的所有属性。
7.2.2. 包含来自 Neo4j 的节点属性
诸如名称和描述之类的节点属性对于理解算法的输出非常有用,即使在运行算法本身时不需要它们。要直接从 Neo4j 数据库获取额外的节点属性,您可以使用 gds.graph.nodeProperty.stream 和 gds.graph.nodeProperties.stream 方法的 db_node_properties 仅客户端参数。
在以下示例中,City 节点既有数值属性,也有 String 属性。stream 方法在检索投影的 population 属性的值的同时,检索数据库专属 name 属性的值。
gds.run_cypher(
"""
CREATE
(m: City {name: "Malmö", population: 360000}),
(l: City {name: "London", population: 8800000}),
(s: City {name: "San Mateo", population: 105000}),
(m)-[:FLY_TO]->(l),
(l)-[:FLY_TO]->(m),
(l)-[:FLY_TO]->(s),
(s)-[:FLY_TO]->(l)
"""
)
G, result = gds.graph.project(
"offices",
{
"City": {
"properties": ["population"]
}
},
"FLY_TO"
)
gds.graph.nodeProperties.stream(G, node_properties=["population"], db_node_properties=["name"])
7.2.3. 按关系类型流式传输拓扑
从对应于 gds.beta.graph.relationships.stream 的 Python 客户端方法返回的类型称为 TopologyDataFrame,它继承自标准 pandas DataFrame。TopologyDataFrame 带有一个名为 by_rel_type 的附加便捷方法,该方法不接受任何参数,并返回 Dict[str, List[List[int]]] 形式的字典。该字典将关系类型作为字符串映射到 2 x m 矩阵,其中 m 在此处表示给定类型的关系数。每个此类矩阵的第一行是关系的源节点 ID,第二行是相应的目标节点 ID。
我们可以使用来自上面的构建示例的图 G 的示例来说明这种转换
topology_by_rel_type = gds.beta.graph.relationships.stream(G).by_rel_type()
assert list(topology_by_rel_type.keys()) == ["REL"]
assert topology_by_rel_type["REL"][0] == [0, 1, 2, 3]
assert topology_by_rel_type["REL"][1] == [1, 2, 3, 0]
与 流式传输属性 方法一样,如果启用了 GDS Apache Arrow Flight 服务器,gds.beta.graph.relationships.stream 也会得到加速。