Neo4J:如何根据现有路径的数量计算相似度得分?

ohfgkhjo  于 12个月前  发布在  其他
关注(0)|答案(3)|浏览(118)

我有一个简单的图,节点bgcprotein,其中(:bgc)-[:ENCODES]->(:protein)。蛋白质可以彼此相似,并且用单独的关系表示,例如(:protein)-[:MMSEQS_90]->(:protein)(90%相似性,但也可以是MMSEQS_80MMSEQS_70MMSEQS_50等)。
对于两个bgc节点,图可以看起来像这样:
x1c 0d1x的数据
bgc节点之间的路径可以是直接的(共享完全相同的protein)或间接的(通过相似的蛋白质)。
现在,我想计算bgc节点之间的相似性得分,定义为:
score = 100% * pathsAll/(pathsAll + notCommon)其中

  • pathsAll是直接和间接路径的总数(在上面的示例中,1 + 2 = 3)
  • notCommon是不被任何路径共享的节点(3 + 2 = 5)
  • score是相似性得分:100% * 3 /(3 + 5)= 37.5%

对所有bgc配对计算分数,并保存为关系(:bgc)-[:SIMILAR_TO {score: <int>})-(:bgc)
我已经设法解决了这个确切的问题与查询如下:

MATCH (bgc1:bgc)-[:ENCODING]->(p1:protein)
MATCH (bgc2:bgc)-[:ENCODING]->(p2:protein)
WHERE elementId(bgc1) < elementId(bgc2)

OPTIONAL MATCH pathIndirect = (bgc1)-[:ENCODING]->(:protein)-[:MMSEQS_90*1..5]-(:protein)<-[:ENCODING]-(bgc2)
OPTIONAL MATCH pathDirect = (bgc1)-[:ENCODING]->(:protein)<-[:ENCODING]-(bgc2)

WITH bgc1, bgc2, COUNT(DISTINCT pathIndirect) + COUNT(DISTINCT pathDirect) AS pathsAll,
              COUNT(DISTINCT p1) + COUNT(DISTINCT p2) - 2 * (COUNT(DISTINCT pathIndirect) + COUNT(DISTINCT pathDirect)) AS notCommon

WITH bgc1, bgc2, 100 * pathsAll / (pathsAll + notCommon) AS similarity_score

WITH bgc1, bgc2, similarity_score
WHERE similarity_score > 0.0  

MERGE (bgc1)-[:IS_SIMILAR {score: similarity_score}]->(bgc2)
RETURN bgc1.id AS BGC1, bgc2.id AS BGC2, similarity_score
ORDER BY similarity_score DESC, BGC1, BGC2;

字符串
导致:

╒═══════╤═══════╤════════════════╕
│BGC1   │BGC2   │similarity_score│
╞═══════╪═══════╪════════════════╡
│"BGC03"│"BGC01"│83              │
├───────┼───────┼────────────────┤
│"BGC01"│"BGC02"│37              │
├───────┼───────┼────────────────┤
│"BGC03"│"BGC02"│25              │
└───────┴───────┴────────────────┘


然而,这会立即使大量节点(几个1000)的数据库崩溃(内存不足),使其无法执行(尽管有合理的内存配置)。
我注意到第一行可以立即执行,但添加第二行已经足以导致崩溃(所以可能是第一个问题?):

// this executes instantly
MATCH rs1=(bgc1:bgc)-[:ENCODING]->(p1:protein)
RETURN rs1;
// this goes forever and crashes
MATCH rs1=(bgc1:bgc)-[:ENCODING]->(p1:protein)
MATCH rs2=(bgc2:bgc)-[:ENCODING]->(p2:protein)
RETURN rs1, rs2


非常感谢您对为什么会崩溃以及如何构建更好的查询做同样的事情的任何建议。也许我在这里做了一些完全错误的事情-我对Neo4j非常陌生。
示例数据:

MERGE (b1:bgc {id: 'BGC01'})
MERGE (b2:bgc {id: 'BGC02'})
MERGE (b3:bgc {id: 'BGC03'})
MERGE (p1:protein {id: 'PROT01'})
MERGE (p2:protein {id: 'PROT02'})
MERGE (p3a:protein {id: 'PROT03a'})
MERGE (p3b:protein {id: 'PROT03b'})
MERGE (p4a:protein {id: 'PROT04a'})
MERGE (p4b:protein {id: 'PROT04b'})
MERGE (p5:protein {id: 'PROT05'})
MERGE (p6:protein {id: 'PROT06'})
MERGE (p7:protein {id: 'PROT07'})
MERGE (p8:protein {id: 'PROT08'})
MERGE (b1)-[:ENCODING]->(p1)
MERGE (b1)-[:ENCODING]->(p2)
MERGE (b1)-[:ENCODING]->(p3a)
MERGE (b1)-[:ENCODING]->(p4a)
MERGE (b1)-[:ENCODING]->(p5)
MERGE (b1)-[:ENCODING]->(p6)
MERGE (b2)-[:ENCODING]->(p2)
MERGE (b2)-[:ENCODING]->(p3b)
MERGE (b2)-[:ENCODING]->(p4b)
MERGE (b2)-[:ENCODING]->(p7)
MERGE (b2)-[:ENCODING]->(p8)
MERGE (b3)-[:ENCODING]->(p1)
MERGE (b3)-[:ENCODING]->(p2)
MERGE (b3)-[:ENCODING]->(p5)
MERGE (b3)-[:ENCODING]->(p6)
MERGE (b3)-[:ENCODING]->(p4b)
MERGE (p3a)-[:MMSEQS_90]->(p3b)
MERGE (p4a)<-[:MMSEQS_90]-(p4b)

pgky5nke

pgky5nke1#

你的前两个子句创建了一个cartesian product,这是应该避免的:

MATCH (bgc1:bgc)-[:ENCODING]->(p1:protein)
MATCH (bgc2:bgc)-[:ENCODING]->(p2:protein)

字符串
在你的用例中,我怀疑绝大多数bcg对(如果你有数千个编码,可能会有数百万个)最终都会被你的查询丢弃(如果它完成的话)。
更好的方法是使用单个MATCH来查找bcg节点之间想要的路径(直接和间接)。这样,您不仅简化了查询,而且自然地只得到想要的bcg对:

MATCH (bgc1:bgc)-[:ENCODING]->()-[:MMSEQS_90*0..5]-()<-[:ENCODING]-(bgc2:bgc)
WHERE elementId(bgc1) < elementId(bgc2)

WITH bgc1, bgc2,
  100 * COUNT(*) / (COUNT {(b)-[:ENCODING]->() WHERE b IN [bgc1, bgc2]} - COUNT(*)) AS similarity_score

MERGE (bgc1)-[:IS_SIMILAR {score: similarity_score}]->(bgc2)
RETURN bgc1.id AS BGC1, bgc2.id AS BGC2, similarity_score
ORDER BY similarity_score DESC, BGC1, BGC2;


备注:
1.我修复了你的查询中的一个错误。在notCommon计算中,我使用了COUNT subquery(在neo4j 5.0中引入)来计算bgc1bgc2ENCODING关系的数量。这产生了你预期的结果。
1.可变长度路径模式0..5中的0下限允许匹配“直接”路径。
1.如果score在一对节点之间发生了变化,那么上面的MERGE子句将创建一个额外的IS_SIMILAR关系。如果这是一个问题,您可以通过将MERGE子句替换为:

MERGE (bgc1)-[s:IS_SIMILAR]->(bgc2)
SET s.score = similarity_score

2exbekwf

2exbekwf2#

开始,当你在一行中执行两个类似的MATCH语句时,你将创建一个carrying产品,这在Cypher中总是一个危险。结果将是数据库中两个:ENCODING关系的每一个组合都有一行。如果你有1000个:ENCODING关系,你将得到1000000行。你可以用WHERE elementId(bgc1)< elementId(bgc2)来限制这一点,但你已经创建了carrying产品。
所以我们需要重写它来消除这种需要。
其次,我觉得notCommon的计算是不正确的。你基于路径的数量,而不是节点的数量。所以它在你显示的图像中是正确的,但在你的示例数据中不是(至少如果我正确理解你的公式)。
最后,你匹配两条路径,直接路径和间接路径。但是在公式中,你只使用了两条路径的和,即所有路径。所以你并不真正关心什么是直接和间接。因此,我们只需要一个路径匹配,通过将1..5改为0..5来覆盖直接和间接。这也意味着我们可以使用它作为基本MATCH,而不是carlycan乘积。
我做了一个看起来像这样的版本。在这里,我首先计算图中蛋白质的数量,然后用它来计算notCommon:

MATCH (p:protein)
WITH COUNT(p) AS proteinCount

MATCH pathAll = (bgc1:bgc)-[:ENCODING]->(:protein)-[:MMSEQS_90*0..5]-(:protein)<-[:ENCODING]-(bgc2:bgc)
WHERE elementId(bgc1) < elementId(bgc2)

WITH bgc1, bgc2, SUM(SIZE([n IN nodes(pathAll) WHERE "protein" IN labels(n)])) AS commonProteins, COUNT(DISTINCT pathAll) AS pathsAll, proteinCount
WITH bgc1, bgc2, pathsAll, proteinCount-commonProteins AS notCommon

WITH bgc1, bgc2, 100 * pathsAll / (pathsAll + notCommon) AS similarity_score
WHERE similarity_score > 0.0  

MERGE (bgc1)-[:IS_SIMILAR {score: similarity_score}]->(bgc2)

RETURN bgc1.id AS BGC1, bgc2.id AS BGC2, similarity_score
ORDER BY similarity_score DESC, BGC1, BGC2

字符串
我得到的结果和你得到的不一样,但是当我尝试手动应用公式时,我得到的结果和我的版本是一样的。这可能是我误解了你的公式,但是希望你可以使用我的版本中的一些东西。
顺便说一下,你提供了所有的MERGE语句来创建你的示例图,这真的很好。这使得帮助你变得容易得多:)

9gm1akwq

9gm1akwq3#

好问题!!Kudos
除了其他建议,还有两个想法...
1.可以增加neo4j.conf文件内存设置以避免任何RAM问题。
1.我经常收集要迭代的项,然后使用call语句展开if。这通常在代码和CPU使用方面更有效。在您的情况下,您可能需要嵌套调用。
用w,collect(x)as y展开y as z call {用z.

相关问题