Find the two nearest neighbors of points

流过昼夜 提交于 2019-12-06 01:05:19

Queries that get the top n per something are really tricky in SPARQL, and there's really no great way to do it yet. It almost always comes down to some weird hack. First, the data with a prefix declaration:

@prefix : <urn:ex:>

:p1 :has_position 1 .
:p2 :has_position 2 .
:p3 :has_position 3 .
:p4 :has_position 4 .

Then the query. The select line has a long string concatenation in it, but that's just to strip off the prefixes like you described in the question. The "hack" in this case is recognizing that the two closest points q and r will minimize the quantity |p − q| + |p − r|, so we can compute that quantity and take the values of q and r that gave it to us. You'll also need to make sure that you impose some ordering on q and r, or else you'll get duplicated results (since you could just swap q and r).

prefix : <urn:ex:>

select ?p (concat(strafter(str(?q),str(:)),", ",strafter(str(?r),str(:))) as ?neighbors) {
  ?p :has_position ?pos1 .
  ?q :has_position ?pos2 .
  ?r :has_position ?pos3 .
  filter(?p != ?q && ?p != ?r)
  filter(str(?q) < str(?r))

  filter not exists {
    ?qq :has_position ?pos22 .
    ?rr :has_position ?pos33 .
    filter(?p != ?qq && ?p != ?rr)
    filter(str(?qq) < str(?rr))
    filter((abs(?pos1 - ?pos22) + abs(?pos1 - ?pos33)) < 
           (abs(?pos1 - ?pos2)  + abs(?pos1 - ?pos3)))
  }
}
-------------------
| p   | neighbors |
===================
| :p1 | "p2, p3"  |
| :p2 | "p1, p3"  |
| :p3 | "p2, p4"  |
| :p4 | "p2, p3"  |
-------------------

Now, you could also do this with a subquery that finds the minimal quantity for each p, and then, in the outer query, finds the q and r values that produce it:

prefix : <urn:ex:>

select ?p (concat(strafter(str(?q), str(:)), ", ", strafter(str(?r), str(:))) as ?neighbors) {
  { select ?p (min(abs(?pos1 - ?pos2) + abs(?pos1 - ?pos3)) as ?d) {
      ?p :has_position ?pos1 .
      ?q :has_position ?pos2 .
      ?r :has_position ?pos3 .
      filter(?p != ?q && ?p != ?r)
      filter(str(?q) < str(?r))
    }
    group by ?p
  }

  ?p :has_position ?pos1 .
  ?q :has_position ?pos2 .
  ?r :has_position ?pos3 .
  filter(?p != ?q && ?p != ?r)
  filter(str(?q) < str(?r))
  filter(abs(?pos1 - ?pos2) + abs(?pos1 - ?pos3) = ?d)
}
-------------------
| p   | neighbors |
===================
| :p1 | "p2, p3"  |
| :p2 | "p1, p3"  |
| :p3 | "p2, p4"  |
| :p4 | "p2, p3"  |
-------------------
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!