从 MySQL 数据计算加权分数的函数?

Function to calculate a weighed score from MySQL data?

我有两个 table(主题和术语),第三个 table 用于两个实体之间的多对多关系。

每个称为装袋的关系都有一个源(文本)和一个权重(0 到 100 之间的整数)。同一对 (topic-term) 可以有多个 bagging (difference sources),每个都有不同的权重。

现在,当我查询一个主题以找出它的最佳术语(更多权重)时,理想情况下我想要具有计算权重的唯一值:

这是数据库架构:

| TOPIC
+-------+------------------+------+-----+---------+----------------+
| Field | Type             | Null | Key | Default | Extra          |
+-------+------------------+------+-----+---------+----------------+
| id    | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| label | varchar(255)     | NO   | UNI | NULL    |                |
| wtext | varchar(40)      | YES  |     | NULL    |                |
+-------+------------------+------+-----+---------+----------------+

| TERM
+-------+---------------------+------+-----+---------+----------------+
| Field | Type                | Null | Key | Default | Extra          |
+-------+---------------------+------+-----+---------+----------------+
| id    | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| label | varchar(255)        | NO   | UNI | NULL    |                |
| slug  | varchar(255)        | NO   |     | NULL    |                |
+-------+---------------------+------+-----+---------+----------------+

| BAGGING
+----------+---------------------+------+-----+---------+----------------+
| Field    | Type                | Null | Key | Default | Extra          |
+----------+---------------------+------+-----+---------+----------------+
| id       | int(10) unsigned    | NO   | PRI | NULL    | auto_increment |
| topic_id | int(11) unsigned    | NO   | MUL | NULL    |                |
| term_id  | bigint(11) unsigned | NO   | MUL | NULL    |                |
| weight   | tinyint(1) unsigned | NO   |     | NULL    |                |
| source   | varchar(8)          | YES  |     | GEN     |                |
+----------+---------------------+------+-----+---------+----------------+

这是我的简单查询:

SELECT 
    bagging.topic_id as topic_id,
    topic.label as topic_label,
    bagging.term_id as term_id,
    term.label as term_label,
    bagging.weight as weight,
    bagging.source as source
FROM
    bagging
JOIN term   ON term.id  = bagging.term_id
JOIN topic  ON topic.id = bagging.topic_id
WHERE
    bagging.topic_id = ( SELECT id FROM topic WHERE label = 'Altruism' )
ORDER BY
    bagging.weight DESC

这给了我以下结果:

+----------+-------------+---------+-----------------------+--------+--------+
| topic_id | topic_label | term_id | term_label            | weight | source |
+----------+-------------+---------+-----------------------+--------+--------+
|        8 | Altruism    |      83 | Altruism              |    100 | TOPIC  |
+----------+-------------+---------+-----------------------+--------+--------+
|        8 | Altruism    |     100 | Altruism (philosophy) |     95 | WPRD   |
|        8 | Altruism    |     100 | Altruism (philosophy) |     95 | MAN    |
|        8 | Altruism    |      84 | Truist                |     95 | MAN    |
|        8 | Altruism    |      84 | Truist                |     15 | WPRD   |
+----------+-------------+---------+-----------------------+--------+--------+
|        8 | Altruism    |      94 | Selfless action       |     95 | WPRD   |
|        8 | Altruism    |      95 | Alturism              |     95 | WPRD   |
|        8 | Altruism    |      96 | Digital altruism      |     95 | WPRD   |
|        8 | Altruism    |      97 | Selflessly            |     95 | WPRD   |
|        8 | Altruism    |      98 | Altruistical          |     95 | WPRD   |
|        8 | Altruism    |      99 | Law of mutual aid     |     95 | WPRD   |
|        8 | Altruism    |     101 | Altruistically        |     95 | WPRD   |
|        8 | Altruism    |      85 | Altruistic            |     95 | WPRD   |
|        8 | Altruism    |      86 | Altruist              |     95 | WPRD   |
|        8 | Altruism    |      87 | Otherism              |     95 | WPRD   |
|        8 | Altruism    |      88 | Unselfishness         |     95 | WPRD   |
|        8 | Altruism    |      89 | Altruistic behavior   |     95 | WPRD   |
|        8 | Altruism    |      90 | Altutrists            |     95 | WPRD   |
|        8 | Altruism    |      91 | Altruists             |     95 | WPRD   |
|        8 | Altruism    |     102 | Pathological altruism |     95 | WPRD   |
+----------+-------------+---------+-----------------------+--------+--------+

现在,如何为这个特定示例创建一个考虑以下因素的评分函数:

最终结果不必从 1 缩放到 100,它可以是考虑到这些限制的相对或抽象评级。

我尝试为每一行计算 ( term_sum_weight * 100 / topic_weight_sum_of_all_terms ) 但看到下面的结果,它们的权重不够。

例如,如何为 95 到 96 赋予比 1 到 20 更大的权重?

公式比将要使用的语言更重要...MySQL 或在我的程序中 Python/PHP。

预期结果(沿着这些线...)

+----------+-------------+---------+-----------------------+-------+--------+
| topic_id | topic_label | term_id | term_label            | score | source |
+----------+-------------+---------+-----------------------+-------+--------+
|        8 | Altruism    |      83 | Altruism              |     1 | TOPIC  |
+----------+-------------+---------+-----------------------+-------+--------+
|        8 | Altruism    |     100 | Altruism (philosophy) |  0.98 | WPRD   |
|        8 | Altruism    |      84 | Truist                |  0.96 | MAN    |
+----------+-------------+---------+-----------------------+--------+-------+
|        8 | Altruism    |      94 | Selfless action       |  0.95 | MAN    |
|        8 | Altruism    |      95 | Alturism              |  0.95 | MAN    |
|        8 | Altruism    |      96 | Digital altruism      |  0.95 | MAN    |
                                ...........
|        8 | Altruism    |      97 | Selflessly            |  0.95 | MAN    |
|        8 | Altruism    |      90 | Altutrists            |  0.95 | MAN    |
|        8 | Altruism    |      91 | Altruists             |  0.95 | MAN    |
|        8 | Altruism    |     102 | Pathological altruism |  0.95 | MAN    |
+----------+-------------+---------+-----------------------+--------+-------+

您应该首先计算出您想要的公式应具有的一些属性。一些可能的假设可能如下:

  1. 重量为 100 的装袋总是会产生最高分数(无论是 1 还是 100)
  2. 添加权重为零的装袋不会改变任何事情
  3. 添加非零权重的装袋会增加对的分数,除非分数已经达到最大值
  4. 如果两对各有一个装袋,则权重越大得分越高

条件 2 和 3 建议类似加法。但是条件 1 告诉你,你不能只添加,因为这样做会超过分数。一种查看方式是使用物理类比。把你的重量想象成速度。在日常生活中,您可以简单地增加速度。但是在非常高的速度下,狭义相对论告诉我们,我们永远无法超过光速的极限。添加两个低于光速的速度确实会导致速度高于但仍低于光速。在您的设置中,“光速”是 100 的最大值。

因此请查找 the formula for the addition of speeds 并根据您的用例进行调整。如果你有两个重量 vw 的装袋,那么总重量将是

(v + w)/(1 + v*w/10000)

现在您需要制定一个公式,在单个公式中对任意数量的被加数执行上述操作,或者您编写一些应用程序代码以增量计算累加器与数据库中下一项之间的总和。或者您继续阅读维基百科,发现以下等式成立:

c-s   c-v   c-w
--- = --- * ---
c+s   c+v   c+w

这里s是速度总和,或者说你的世界得分。但是s越大,这个分数就会越小。因此,不是按 s 降序排序,而是按 (c-s)/ (c+s) 升序排列。不幸的是 MySQL doesn't have a PRODUCT aggregate function。但是你可以使用对数将乘积转化为和:

SUM(LOG((100-weight)/(100+weight))) AS score

不幸的是,MySQL 不像 IEEE 浮点运算那样处理无穷大(即 log(0) = -∞),而是将 100 的权重转换为分数NULL。因此,您可以使用 SORT BY (score IS NULL) DESC 或类似方法将 NULL 分数排在非 NULL 分数之前。或者您以正确处理零的方式将上面的值转换回值 s

SELECT …,
  IF(MAX(weight) = 100, 1,
    (1-EXP(SUM(LOG((100-weight)/(100+weight)))))/
    (1+EXP(SUM(LOG((100-weight)/(100+weight)))))) AS score

在这里你可以把EXP(SUM(LOG(…)))想成PRODUCT(…)。要为 s 求解 (c-s)/(c+s)=pp 作为我刚才提到的乘积),您需要计算 s=c*(1-p)/(1+p)。因此,通过省略 c*,您可以获得 0 到 1 范围内的分数,而不是 0 到 100,这与您到目前为止的计算相似。 MAX(weight) = 100 情况会导致 LOG(0) 进行整个计算 NULL,因此必须单独处理。

有关使用您的数据的示例,请参阅 http://sqlfiddle.com/#!9/1cd56/1。它 returns 利他主义为 1,利他主义(哲学)为 0.9987,真理主义为 0.9628,其他为 0.95。第二个分数比你在问题中的预期要大得多,但我没有一个好主意来调整公式使其更像你的预期。