图对象
1. 投影图对象
有多种方法可以投影图对象。最简单的方法是进行本机投影
# 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.beta.graph.subgraph
-
gds.beta.graph.generate
-
gds.graph.sample.rwr
-
gds.graph.sample.cnarw
它们的 Cypher 签名以与上面gds.graph.project
非常相似的方式映射到 Python。
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. 从 DataFrame 构造图
除了从 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 Server 支持
如果启用了construct
方法可以利用 GDS 的Apache Arrow Flight Server。这尤其意味着
-
图的构建速度大大加快,
-
可以提供多个数据帧,节点和关系均可。如果使用多个节点数据帧,则它们需要在所有节点数据帧中包含不同的节点 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 Server,则速度会大大加快。
此外,为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
方法将数据库专用的name
属性的值与投影的population
属性的值一起检索。
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 Server,gds.beta.graph.relationships.stream
也会加速。