介绍
NetworkX 是对网络的动态,功能和结构的创建,操纵和研究的包装。它允许我们使用复杂的图形算法来解决与网络相关的问题。
尽管NetworkX是一个非常强大且通用的软件包,但由于其Python实施和缺乏质量存储,它的速度和效率有所限制。虽然这不是较小的数据集的问题,但是在大型网络中,这可能很麻烦。使用 memgraph ,一个内存图数据库,因为存储解决方案为NetworkX提供了其他好处和功能。
在这篇文章中,我们将重点介绍如何使用查询模块和NetworkX实施自定义的密码>。我们还将讨论开源项目 MAGE 您可以在其中找到已经实现的模块并贡献自己的。
先决条件
本教程的先决条件是:
-
Memgraph Platform:完整的流式图应用程序平台,其中包括:
- memgraphdb-保存您的数据的数据库
- memgraph Lab-用于导入数据的集成开发环境, 开发,调试和配置文件数据库查询并可视化查询结果
- mgconsole-运行查询的命令行接口
- 法师 - 图形算法和模块库
或
- Memgraph Cloud:一项托管且完全管理的服务,利用所有孟加拉产品,例如法师和浏览器内备忘录实验室
了解NetworkX基础知识
要充分利用图形的力量,您首先需要在使用NetworkX软件包时对图理论及其相应术语中的基本概念进行基本理解。
您将在本和其他教程中看到的四种主要图形类型是:
- 图形 - 一个无向图。
- digraph - 带有自loop的有向图。
- Multigraph - 一个无向图,带有自动和平行边缘。
- Multidigraph - 带有自动宽和平行边缘的有向图。
a selfloop (也称为环或扣)是将顶点连接到自身的边缘,而平行边缘(也称为多个边缘或多边 - 边缘)是两个或多个边缘,这些边缘出现在相同的两个节点上。
memgraph围绕网络包装中存在的大多数算法和对象提供了一组薄薄的包装器。包装器函数具有创建一个兼容图形的对象的功能,该对象可以直接在存储使用上直接流式传输本机数据库图。例如,NetworkX类MultiDiGraph
具有MemgraphMultiDiGraph
包装器,而DiGraph
类使用MemgraphDiGraph
包装器类。
在memgraph中设置NetworkX
memgraph支持使用用户编写的过程扩展查询语言。这些过程分为模块,如果用Python编写,则可以直接从Memgraph Lab开发。我们将创建这样的过程来使用NetworkX软件包。
在Memgraph Lab中,查询模块写在查询模块部分中,您还可以在其中找到与Memgraph Platform和Cloud预先包装的内置查询模块。要了解有关查询模块的更多信息,请查看Memgraph documentation on the subject。
编写有关Cypher的自定义程序
首先,我们需要一个基本数据集。让我们创建一个非常简单的图形,其中有两个通过道路连接的城市。
城市是 nodes ,而道路是连接这些节点的 edge 。 OPEN Memgraph Lab ,在查询执行中部分运行以下查询:
CREATE (c1:City { name: 'Boston' }), (c2:City { name: 'New York' })
CREATE (c1)-[r:ROAD_TO { distance: 100}]->(c2)
RETURN c1, c2, r;
这已经创建了两个标签City
和一个name
属性的节点。
它们之间的边缘是ROAD_TO
类型,并具有代表以公里为单位的距离的属性distance
。
我们希望一个更复杂的网络与NetworkX一起使用,因此让我们通过以下查询进行扩展:
MATCH (c1:City { name: 'Boston' })
CREATE (c2:City { name: 'Philadelphia' }), (c3:City { name: 'Seatle' }), (c4:City { name: 'Los Angeles' }), (c5:City { name: 'San Francisco' })
CREATE (c1)-[:ROAD_TO { distance: 110}]->(c2)
CREATE (c3)-[:ROAD_TO { distance: 150}]->(c2)
CREATE (c4)-[:ROAD_TO { distance: 200}]->(c5)
这是一个简单的有向图,在使用NetworkX时,它将被存储为DiGraph
。如果我们要复制所有边缘并逆转重复项的方向,则由于平行边缘,我们最终会得到一个MultiDiGraph
。这可以通过以下查询来完成:
MATCH (c0:City { name: 'New York' }), (c1:City { name: 'Boston' }),
(c2:City { name: 'Philadelphia' }), (c3:City { name: 'Seatle' }),
(c4:City { name: 'Los Angeles' }), (c5:City { name: 'San Francisco' })
CREATE (c1)<-[:ROAD_TO { distance: 100}]-(c0)
CREATE (c1)<-[:ROAD_TO { distance: 110}]-(c2)
CREATE (c3)<-[:ROAD_TO { distance: 150}]-(c2)
CREATE (c4)<-[:ROAD_TO { distance: 200}]-(c5)
现在,让我们在NetworkX中使用我们的图表。打开查询模块部分,然后单击+新模块。给出模块名称hello_world
。将使用示例过程创建一个新的查询模块。随意删除它们并将以下代码复制到其中:
import mgp
import networkx as nx
from mgp_networkx import (MemgraphMultiDiGraph, MemgraphDiGraph,
MemgraphMultiGraph, MemgraphGraph,
PropertiesDictionary)
@mgp.read_proc
def procedure(ctx: mgp.ProcCtx) -> mgp.Record(nodes=mgp.List[mgp.Vertex]):
g = MemgraphMultiDiGraph(ctx=ctx)
return mgp.Record(nodes=list(g.nodes()))
前三行是基本的Python导入,我们所有使用NetworkX都需要的查询模块。
因为我们正在使用存储在memgraph中的数据,所以包装器用于引用存储的图形。 LINE g = MemgraphMultiDiGraph(ctx=ctx)
意味着我们是从当前上下文ctx
创建此引用(在所有过程调用中自动传递),并且代表nx.MultiDiGraph
类。
该过程返回图表中的节点列表。如果您需要对自定义对象和包装器的更详细的了解,请查看查询模块Reference Guide。
保存并关闭窗口,然后移动到查询执行部分以使用该过程。
致电过程以返回结果:
CALL hello_world.procedure() YIELD nodes RETURN nodes;
我们得到的是图中的节点列表。
让我们更改过程,以还返回图表中的节点数量和边缘数。返回到查询模块部分,查找 hello_world 查询模块,单击右侧的箭头查看其详细信息,然后通过修改代码进行编辑:
@mgp.read_proc
def procedure(ctx: mgp.ProcCtx) -> mgp.Record(nodes=mgp.List[mgp.Vertex],
number_of_nodes=int,
number_of_edges=int):
g = MemgraphMultiDiGraph(ctx=ctx)
list_of_nodes = list(g.nodes())
num_of_nodes = g.number_of_nodes()
num_of_edges = g.number_of_edges()
return mgp.Record(nodes=list_of_nodes,
number_of_nodes=num_of_nodes,
number_of_edges=num_of_edges)
在调用该过程之前,请不要忘记保存模块。
CALL mg.load_all();
CALL hello_world.procedure() YIELD * RETURN *;
使用YIELD *
,我们确保该过程返回所有结果,并且使用RETURN *
我们将它们全部打印出来。
使用NetworkX编辑图
重要的是要注意,您不能直接从查询模块直接执行任何数据库写操作。否则,无法保证数据库的完整性。但是,您可以保存整个图形或仅作为一个全新对象的子图,然后在返回结果之前执行编辑。假设我们想找出如果我们从我们的网络中删除了波士顿和纽约市的城市,我们的自定义过程将返回什么。在同一模块中创建一个新过程,并将其命名为remove_node_procedure()
:
@mgp.read_proc
def remove_node_procedure(ctx: mgp.ProcCtx) -> mgp.Record(nodes=mgp.List[mgp.Vertex],
number_of_nodes=int):
g = MemgraphMultiDiGraph(ctx=ctx)
g_copy = nx.MultiDiGraph(g)
for node in list(g_copy.nodes):
if node.properties.get('name') == 'Boston':
g_copy.remove_node(node)
list_of_nodes = list(g_copy.nodes)
num_of_nodes = g_copy.number_of_nodes()
return mgp.Record(nodes=list_of_nodes,
number_of_nodes=num_of_nodes)
您可以看到,返回的列表不包含城市波士顿。
将论点传递给自定义程序
让我们制定一个使用nx.is_simple_path()
算法的新过程。图中的一个简单路径是一个非空的节点序列,其中没有节点在序列中不止一个节点出现,并且序列中的每个相邻的节点对在图中均相邻。 simple_path_procedure()
看起来像这样:
@mgp.read_proc
def simple_path_procedure(ctx: mgp.ProcCtx, nodes: mgp.List[mgp.Vertex]
) -> mgp.Record(is_simple_path=bool):
g = MemgraphMultiDiGraph(ctx=ctx)
simple_path = nx.is_simple_path(g, nodes)
return mgp.Record(is_simple_path=simple_path)
您可以通过从 memgraph Lab 执行以下查询来调用该过程:
MATCH p=((c1:City {name:'Boston'})-[:ROAD_TO]->(c2:City {name:'New York'}))
CALL hello_world.simple_path_procedure(nodes(p)) YIELD is_simple_path RETURN is_simple_path;
使用此查询,我们创建了一个从路径 Boston-New York 的节点列表,而simple_path_procedure()
应该返回True
,因为这是一个简单的路径。
法师 - 介绍Memgraph Advanced Graph Extensions
MAGE 是Memgraph提供的开源存储库,其中包含由我们的工程师编写或开发人员社区贡献的查询模块的实现。
在其中,您可以在多种编程语言中找到各种算法的实现,所有这些都可以在memgraph内运行。法师解决的一些可用算法和问题包括Pagerank,弱连接组件(Union Find),Jaccard相似性系数,旅行推销员问题...等等。
,如果您认为他们会为其他开发人员提供帮助,请随时贡献您想出的任何自定义模块。我们很乐意看看并将它们添加到存储库中。
结论和下一步
虽然功能有限,但Memgraph提供了一种在类似图形的对象上使用NetworkX软件包的简单方法,该方法可以直接流式传输本机数据库图。这种实现可显着改善内存使用量,并为NetworkX算法增加内存效率。
有关如何创建自己的自定义密码程序的更深入的解释,请查看我们的How to Implement Query Modules?指南或通过MAGE中已经实现的算法。