【问题标题】:How can I use neo4j cypher query to create a histogram of nodes bucketed by number of relationships?如何使用 neo4j 密码查询来创建按关系数分桶的节点直方图?
【发布时间】:2018-05-05 07:17:08
【问题描述】:

我有一大堆与以下密码匹配的节点:

(:Word)<-[:Searched]-(:Session)

我想制作每个搜索关系频率下 Word 节点数的直方图。

我想做这样的图表:

Searches Words
0        100
1-5      200
6-10     150
11-15    50
16-20    25

我刚开始使用 neo4j,我不确定如何解决这个问题,或者即使有办法在 cypher 中指定它。我最接近的是计算关系并获得平均值。

MATCH (n:Word) 
RETURN
DISTINCT labels(n),
count(*) AS NumofNodes,
avg(size((n)<-[:Searched]-())) AS AvgNumOfRelationships,
min(size((n)<-[:Searched]-())) AS MinNumOfRelationships,
max(size((n)<-[:Searched]-())) AS MaxNumOfRelationships

这是基于此处的示例:https://neo4j.com/developer/kb/how-do-i-produce-an-inventory-of-statistics-on-nodes-relationships-properties/

我还看到使用模数运算符进行分组以获取存储桶,但我不确定如何在引用计数时使用它:Neo4j cypher time interval histogram query of time tree

有没有“最好”的方法来做到这一点?

【问题讨论】:

    标签: neo4j statistics cypher histogram


    【解决方案1】:

    以下应该有效:

    WITH 5 AS gSize
    MATCH (w:Word)
    OPTIONAL MATCH (w)<-[s:Searched]-()
    WITH gSize, w, TOINT((COUNT(s) + (gSize-1))/gSize * gSize) AS m
    RETURN
      CASE m WHEN 0 THEN '0' ELSE (m-gSize+1)+'-'+m END AS range,
      COUNT(*) AS ct
    ORDER BY range;
    

    使用@GaborSzarnyas 提供的样本数据,输出为:

    +-------------+
    | range  | ct |
    +-------------+
    | "0"    | 1  |
    | "1-5"  | 1  |
    | "6-10" | 1  |
    +-------------+
    

    【讨论】:

      【解决方案2】:

      我能够找出一个我认为可以得到我想要的数据的查询:

      MATCH (n:Word) 
      WITH n, 5 AS bucketsize
      WITH (FLOOR(SIZE( (n)<-[:Searched]-() ) / bucketsize) * bucketsize) AS numRels
      RETURN numRels, COUNT(*)
      ORDER BY numRels ASC
      

      它没有得到我想要的零行,但它似乎可以正常工作。希望其他人有更好的解决方案。

      【讨论】:

        【解决方案3】:

        我创建了一个包含三个单词的简单示例数据集:w1 没有搜索,w2 有 3 个搜索,w3 有 6 个搜索。

        CREATE (w1:Word {w: '1'})
        WITH count(*) AS dummy
        
        CREATE (w2:Word {w: '2'}) WITH w2
        UNWIND range(1, 3) AS i
        CREATE (w2)<-[:Searched]-(:Session)
        WITH count(*) AS dummy
        
        CREATE (w3:Word {w: '3'}) WITH w3
        UNWIND range(1, 6) AS i
        CREATE (w3)<-[:Searched]-(:Session)
        

        我会这样处理:首先,让我们创建一个列表,其中包含每个存储桶的上限:

        RETURN [i IN range(0, 4) | i*5] AS upperLimits
        
        ╒══════════════╕
        │"upperLimits" │
        ╞══════════════╡
        │[0,5,10,15,20]│
        └──────────────┘
        

        其次,将其与list comprehension 一起使用,该list comprehension 从列表中选择具有足够大上限的元素。其中第一个是我们的存储桶,因此我们使用[0] 列表索引器选择它。剩下的只是计算下限和排序行:

        WITH [i IN range(0, 4) | i*5] AS upperLimits
        MATCH (n:Word) 
        WITH upperLimits, ID(n) AS n, size((n)<-[:Searched]-()) AS numOfRelationships
        WITH
          [upperLimit IN upperLimits WHERE numOfRelationships <= upperLimit][0] AS upperLimit,
          count(n) AS count
        RETURN
          upperLimit - 4 AS lowerLimit,
          upperLimit,
          count
        ORDER BY lowerLimit
        

        查询给出以下结果:

        ╒════════════╤════════════╤═══════╕
        │"lowerLimit"│"upperLimit"│"count"│
        ╞════════════╪════════════╪═══════╡
        │-4          │0           │1      │
        ├────────────┼────────────┼───────┤
        │1           │5           │1      │
        ├────────────┼────────────┼───────┤
        │6           │10          │1      │
        └────────────┴────────────┴───────┘
        

        潜在的改进:

        (1)如果numOfRelationships的值大于最大上限,则上面的查询将返回一个空列表的第一个元素,即null。为避免这种情况,1)设置足够大的上限,例如

        MATCH (n:Word) 
        WITH max(size((n)<-[:Searched]-())) AS maxNumberOfRelationShips
        WITH [i IN range(-1, maxNumberOfRelationShips/5+1) | {lower: i*5-4, upper: i*5}] AS limits
        RETURN *
        

        您可以通过coalesce 使用具有“16 或更大”语义的顶部存储桶。

        (2)-4作为下限不是很好,可以用CASE去掉。

        把所有这些放在一起,我们得到了这个:

        MATCH (n:Word) 
        WITH max(size((n)<-[:Searched]-())) AS maxNumberOfRelationShips
        WITH [i IN range(0, maxNumberOfRelationShips/5+1) | i*5] AS upperLimits
        MATCH (n:Word) 
        WITH upperLimits, ID(n) AS n, size((n)<-[:Searched]-()) AS numOfRelationships
        WITH
          [upperLimit IN upperLimits WHERE numOfRelationships <= upperLimit][0] AS upperLimit,
          count(n) AS count
        RETURN 
          CASE WHEN upperLimit - 4 < 0 THEN 0 ELSE upperLimit - 4 END AS lowerLimit,
          upperLimit,
          count
        ORDER BY lowerLimit
        

        结果:

        ╒════════════╤════════════╤═══════╕
        │"lowerLimit"│"upperLimit"│"count"│
        ╞════════════╪════════════╪═══════╡
        │0           │0           │1      │
        ├────────────┼────────────┼───────┤
        │1           │5           │1      │
        ├────────────┼────────────┼───────┤
        │6           │10          │1      │
        └────────────┴────────────┴───────┘
        

        【讨论】:

        • 好的,这太长了。我会压缩一下。
        【解决方案4】:

        在这种情况下,我通常会使用 neo4j 中的设置,即如果将一个整数除以一个整数,则会得到一个整数。这大大简化了查询。我们为 0 添加了一个特殊情况,它都适合一行。

        WITH [0,1,5,7,9,11] as list
        UNWIND list as x
        WITH CASE WHEN x = 0 THEN -1 ELSE  (x / 5) * 5 END as results
        return results
        

        这会返回

        -1, 0, 5, 5, 5, 10

        这并不理想,因为您想将 1-5 人组合在一起,但我想这已经足够了。

        【讨论】:

        • 哦,你做了非常相似的事情...... :)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-25
        • 2018-11-15
        相关资源
        最近更新 更多