Sparql如何对这种数据进行分组

Sparql how to group this kind of data

因为担心你不明白我的情况,特意做了这张视觉图给你看(点击图片看高质量版本)。

我知道一个用户(不管是谁,我们不关心)喜欢一个项目 (i1)

我们想推荐其他项目:

i1 类似于 i2 取决于特定的标准(所以有一个相似度值,我们称之为 s1

i1也和同一个i2相似,但是要看另外一个标准(所以有一个相似度值,暂且称它为s2

i1也和同一个i2相似,但是要看第三个标准(所以有一个相似度值,暂且称它为s3

现在i2属于两个class,并且它们中的每一个都通过特定的权重影响相似度

我的问题

我想计算 i1i2 之间的最终相似度,除了特定 class.

的权重外,我几乎完成了所有计算

我的问题是这个权重不应该应用于导致 selection of i2 的标准。换句话说,如果 i2 使用 1000 个标准 select 1000 次,并且 i2 属于特定的 class,那么 class 的权重将为只应用一次,而不是 1000 次,如果 i2 属于两个 classes,这两个 classes 的两个权重将只应用一次,这取决于有多少标准导致 select i2

现在

为了让您更容易地帮助我,我做了这个查询(好吧很长,但它必须很长才能向您展示),但我也通过查询 select 只是必需的信息,因此您可以在其上方添加另一层 select。

    prefix : <http://example.org/rs#>
prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>


select  ?item ?similarityValue ?finalWeight where {
  values ?i1 {:i1}
  ?i1 ?similaryTo ?item .
  ?similaryTo :hasValue ?similarityValue .
  optional{
    ?item :hasContextValue ?weight .
  }
  bind (if(bound(?weight), ?weight, 1) as ?finalWeight)
}

所以该查询的结果是(查看项目 i2)它重复 6 次(如预期的那样)具有三个不同的相似性(如预期的那样,因为三个不同的标准),并且 finalWeight,这是权重,针对每个标准重复:

终于

这是数据

@prefix : <http://example.org/rs#>
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>

:i1 :similaryTo1 :i2 .
:similaryTo1 :hasValue 0.5 .
:i1 :similaryTo2 :i2 .
:similaryTo2 :hasValue 0.6 .
:i1 :similaryTo3 :i2 .
:similaryTo3 :hasValue 0.7 .
:i2 :hasContextValue 0.1 .
:i2 :hasContextValue 0.4 .
:i1 :similaryTo4 :i3 .
:similaryTo4 :hasValue 0.5 .

我希望你能帮助我,我真的很感激

所以我想做什么

假设根本没有重量,那么我的查询将是:

prefix : <http://example.org/rs#>
prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
select  ?item ?similarityValue  where {
  values ?i1 {:i1}
  ?i1 ?similaryTo ?item .
  ?similaryTo :hasValue ?similarityValue .

}

结果将是:

然后我对具有相似度总和的项目进行聚合,如下所示:

prefix : <http://example.org/rs#>
prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
select  ?item (SUM(?similarityValue) as ?sumSimilarities)  where {
  values ?i1 {:i1}
  ?i1 ?similaryTo ?item .
  ?similaryTo :hasValue ?similarityValue .
}
group by ?item

结果是:

我想要的是将此结果的每一行乘以与 ?item 关联的 两个权重 的总和,它们是 (0.1 * 0.4) i2 和 (1) 对于 i3

请注意,有些项目没有两个权重,有些只有一个,有些什么都没有,请注意,即使对于那些有两个权重的项目,这两个值也可能相同,所以如果您在此处使用 distinct,请小心。

最后,我总是说两个只是作为一个例子,但在现实生活中,这个数字来自动态系统。

<3>更新 @Joshua Taylor回答后,我理解他的样本数据为:

一些数据

首先,我们可以使用一些数据。 item :a 有一堆相似度连接,每一个都指定了一个item和一个原因。 :a 可以出于几个不同的原因与一个项目相似,甚至可以有相同项目和原因的重复相似性。到目前为止,我认为这符合您的用例。 (问题中的示例数据可以使这一点更清楚,但我认为这与您所拥有的一致)。然后,每个项目都有上下文值,每个原因都有一个可选的权重。

@prefix : <urn:ex:>

:a :similarTo [ :item :b ; :reason :p ] ,
              [ :item :b ; :reason :p ] , # a duplicate
              [ :item :b ; :reason :q ] ,
              [ :item :b ; :reason :r ] ,
              [ :item :c ; :reason :p ] ,
              [ :item :c ; :reason :q ] ,
              [ :item :d ; :reason :r ] ,
              [ :item :d ; :reason :s ] .

:b :context 0.01 .
:b :context 0.02 .
:c :context 0.04 .
:d :context 0.05 .
:e :context 0.06 . # not used

:p :weight 0.1 .
:q :weight 0.3 .
:r :weight 0.5 .
# no weight for :s
:t :weight 0.9 . # not used

听起来你想要做的是计算相似项目的上下文值的总和,包括每次出现的上下文值,但只对不同的事件计算原因权重的总和。如果那是正确的理解,那么我认为您想要如下内容。

为原因获取权重

第一步是能够得到每个相似项目的不同原因的权重总和。

prefix : <urn:ex:>

select * where {
  values ?i { :a }

  #-- get the sum of weights of distinct reasons
  #-- for each item that is similar to ?i.
  { select ?item (sum(?weight) as ?propertyWeight) {
      #-- get the distinct properties for each ?item
      #-- along with their weights.
      { select distinct ?item ?property ?weight {
          ?i :similarTo [ :item ?item ; :reason ?property ] .
          optional { ?property :weight ?weight_ }
          bind(if(bound(?weight_), ?weight_, 0.0) as ?weight)
        } }
    }
    group by ?item
  }
}
------------------------------
| i  | item | propertyWeight |
==============================
| :a | :b   | 0.9            |
| :a | :c   | 0.4            |
| :a | :d   | 0.5            |
------------------------------

获取项目的权重

现在,您仍然需要计算每个项目的值的总和,计算每次出现的重量。所以我们扩展查询:

select * where {
  values ?i { :a }

  #-- get the sum of weights of distinct reasons
  #-- for each item that is similar to ?i.
  { select ?item (sum(?weight) as ?propertyWeight) {
      #-- get the distinct properties for each ?item
      #-- along with their weights.
      { select distinct ?item ?property ?weight {
          ?i :similarTo [ :item ?item ; :reason ?property ] .
          optional { ?property :weight ?weight_ }
          bind(if(bound(?weight_), ?weight_, 0.0) as ?weight)
        } }
    }
    group by ?item
  }

  #-- get the sum of the context values
  #-- for each item.
  { select ?item (sum(?context_) as ?context) {
      ?item :context ?context_ .
    }
    group by ?item
  }
}
----------------------------------------
| i  | item | propertyWeight | context |
========================================
| :a | :b   | 0.9            | 0.03    |
| :a | :c   | 0.4            | 0.04    |
| :a | :d   | 0.5            | 0.05    |
----------------------------------------

请注意,在第二个子查询中搜索 ?item :context ?context_ . 是可以的,甚至不确保 ?item 是类似的项目之一。由于两个子查询的结果是合并的,我们只会得到第一个子查询也返回的 ?item 值的结果。

将它们放在一起

现在您可以只加、乘或做任何您想做的事情来将原因权重的总和与上下文值的总和相结合。例如,如果您对它们求和:

select ?i ?item ((?propertyWeight + ?context) as ?similarity) where {
  values ?i { :a }

  #-- get the sum of weights of distinct reasons
  #-- for each item that is similar to ?i.
  { select ?item (sum(?weight) as ?propertyWeight) {
      #-- get the distinct properties for each ?item
      #-- along with their weights.
      { select distinct ?item ?property ?weight {
          ?i :similarTo [ :item ?item ; :reason ?property ] .
          optional { ?property :weight ?weight_ }
          bind(if(bound(?weight_), ?weight_, 0.0) as ?weight)
        } }
    }
    group by ?item
  }

  #-- get the sum of the context values
  #-- for each item.
  { select ?item (sum(?context_) as ?context) {
      ?item :context ?context_ .
    }
    group by ?item
  }
}
--------------------------
| i  | item | similarity |
==========================
| :a | :b   | 0.93       |
| :a | :c   | 0.44       |
| :a | :d   | 0.55       |
--------------------------

最后清理

查看最后的查询,有两件事让我有些困扰。第一个是我们在内部子查询中检索每个解决方案的原因权重,而我们只需要为每个 属性 每个项目检索一次。也就是说,我们可以将 optional 部分移到外部、内部子查询中。然后,我们有一个 bind 设置一个我们只在聚合中使用的变量。我们可以通过求和 coalesce(?weight,0.0) 来替换它以使用 ?weight 如果它被绑定,并且 0.0 否则。进行这些更改后,我们最终得到:

select ?i ?item ((?propertyWeight + ?context) as ?similarity) where {
  values ?i { :a }

  #-- get the sum of weights of distinct properties
  #-- using 0.0 as the weight for a property that doesn't
  #-- actually specify a weight.
  { select ?item (sum(coalesce(?weight,0.0)) as ?propertyWeight) {

      #-- get the distinct properties for each ?item.
      { select distinct ?item ?property {
          ?i :similarTo [ :item ?item ; :reason ?property ] .
        } }

       #-- then get each property's optional weight.
       optional { ?property :weight ?weight }
    }
    group by ?item
  }

  #-- get the sum of the context values
  #-- for each item.
  { select ?item (sum(?context_) as ?context) {
      ?item :context ?context_ .
    }
    group by ?item
  }
}

这不是一个巨大的变化,但我认为它让事情变得更清晰,也更容易理解。

评论

这几乎成了我的口头禅,但如果提供样本数据,这类问题就更容易回答了。在这种情况下,大多数 如何 您首先获得这些值的实际机制并不重要。这就是你之后聚合它们的方式。这就是为什么我们可以使用非常简单的数据,比如我在这个答案开头从头开始创建的数据。

不过,我认为从中得到的最大收获是使用 SPARQL(我希望其他查询语言也一样)的重要技术之一是具有单独的子查询并连接它们的结果。在这种情况下,我们最终得到了几个子查询,因为我们确实需要以几种不同的方式进行分组。如果 SPARQL 提供 distinct by 运算符,这可能会更简单,这样我们就可以说

sum(distinct by(?property) ?weight)

但这有一个问题,如果不同的 属性 可以有多个权重,您会选择其中的哪个权重?所以解决方案似乎真的是几个子查询,这样我们就可以进行几种不同类型的分组。这就是为什么我要询问您要计算的实际公式。