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

时间:2017-01-02 08:05:22

标签: python mysql algorithm math probability

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

每个关系称为装袋,具有源(文本)和权重(0到100之间的int)。同一对(主题 - 术语)可以有几个装袋(差异源),每个都有不同的权重。

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

  • 权重为100表示​​此项目最大
  • 同一对(不同来源)的几个重量超过单对
  • 没有'减'重量

这是数据库架构:

| 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   |
+----------+-------------+---------+-----------------------+--------+--------+

现在,如何创建一个评分函数,该函数将在下面考虑这个特定的例子:

  • Altruism是无与伦比的,只能等于(= 100)
  • Truist显然会受到15 / 100权重的惩罚,但有两个因素也应该被考虑,尤其是因为第二个是95
  • Altruist (Philosophy)的重量应该超过所有其他值(Altruism除外,只能等于。)即使95两次看起来大于100。

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

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

Results computed on spreadsheets to try. 例如,如何为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 个答案:

答案 0 :(得分:1)

您应该从制定一些您想要的配方应具有的属性开始。一些可能的假设可能如下:

  1. 重量100的装袋总是导致最大分数(1或100)
  2. 添加零重量的装袋不会改变任何内容
  3. 添加非零重量的套袋会增加该对的分数,除非分数已经达到最大值
  4. 如果两对每个都有一个套袋,那么更大的重量会导致更高的分数
  5. 条件2和3表示类似添加的内容。但条件1告诉你,你不能只是添加,因为这样做会超过分数。查看此内容的一种方法是使用物理模拟。把你的重量想象成速度。在日常生活中,您可以简单地添加速度。但是在非常高的速度下,狭义相对论告诉我们,我们永远不能超过光速的极限。增加两个低于光速的速度会导致速度更高但仍然低于光速。在你的设置中,“光速”是100的最大值。

    请查看the formula for the addition of speeds并根据您的使用情况进行调整。如果你有两个重量 v w 的袋子,那么总重量将是

    (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的分数。因此,您可以使用NULL或类似内容对非NULL之前的SORT BY (score IS NULL) DESC分数进行排序。或者以正确处理零的方式将上面的内容转回 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(…)。要解决(c-s)/(c+s)=p的{​​{1}}(s作为我刚刚提到的产品),您需要计算p。因此,通过省略s=c*(1-p)/(1+p),您可以获得0到1而不是0到100的分数,类似于您到目前为止的计算结果。 c*案例会导致MAX(weight) = 100进行整个计算LOG(0),因此必须单独处理。

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