[spip-dev] a mon tour sur la popularite

Bon, maintenant que vous m'avez donné à réfléchir aux "popularités", j'ai
aussi mon idée. J'explique : à chaque visite d'un article, on lui ajoute un
certain nombre de points ; ces points ont une période de demi-vie donnée. Et
c'est tout.

* on passe le champ popularite en "double" ; le fait de se fixer sur des
entiers est contre-productif, puisque ça implique des concepts comme
l'"efficacité de la discrimination" et des coupages de cheveux en 4.

* on affecte 1 point si le referer est dans le site ou vide, 2 sinon (par
exemple) ;

* la formule est super simple, à chaque hit sur un article on fait
    if (referer extérieur)
        $points = 2;
    else
        $points = 1;

    UPDATE spip_articles
    SET popularite = popularite * POWER ((1-a), (NOW()-maj)) + b * $points
    WHERE id_article = $id_article;

* reste à trouver la valeur de a et b pour que, dans un scénario uniforme,
la "popularité" ait une signification intelligible... je détaille un peu
pour être clair et pour ceux qui n'auraient plus trop de souvenirs de maths.

Supposons donc qu'on ait de façon régulière un hit par minute valant 1
point. On veut avoir au final popularite = 1/minute, qu'on notera 60 * 24
(nombre "estimé si ce rythme est maintenu de points par jour").

"NOW()-maj" vaut donc toujours 60 secondes. L'équation est alors
    (60 * 24) * (1 - (1-a)^60) = b * 1

* Il faut donc fixer la période de demi-vie (qui donne a) et en déduire b.

Si la demi-vie est de 3 jours, on part avec (1-a)^(3*24*60*60) = 1/2

D'où, sortez vos calculettes, et si je ne me suis pas trompé,
    a = 1 - (1/2)^(-3*24*60*60) = 2,67418 E-06;
    b = 0,231030525;

[Si on veut quelque chose de plus "réactif", on peut partir sur
(1-a)^(12*60*60) = 1/2 ; soit une demi-vie d'une demi-journée]

Avec ça on a le système de popularité permet tous les classements, est mis à
jour **en continu**, et a une signification "absolue" (le nombre de hits
dans la journée si "ce rythme de consultation se maintient", sachant qu'un
accès direct vaut deux hits) et, par rebond, une signification relative
(puisqu'on travaille sur des double et pas sur des entiers, on peut même
classer entre eux des articles pas lus depuis un mois !).

Si on veut conserver la notion de pourcentage (idiote mais pour garder la
compatibilité), il suffit de faire, une fois par heure par exemple,
ecrire_meta('max_popularite') = "SELECT MAX(popularite) FROM
spip_articles"...
et donner #POPULARITE = MAX(100, popularite/lire_meta('max_popularite'));
et #POPULARITE_PARJOUR = popularite
à quoi on peut ajouter #POPULARITE_SITE = SOMME(popularite); ce dernier
concept a aussi une signification absolue, qui pourrait se mettre dans un
champ du "backend" (sans tricher!) et qu'un "hit parade des spip" pourrait
aller chercher automatiquement.

-- Fil

Deux choses comme ça :

- il vaudrait mieux ne pas faire d'écriture dans spip_articles à chaque
requête ; sur un gros site ça pourrait (?) faire mal. (accessoirement,
je ne sais pas si la précision des flottants est suffisante pour accumuler
une série de power() avec un exposant très proche de 1)

- si un article n'est pas visité depuis X heures ou jours, il va garder
la popularité d'alors au lieu de la voir baisser progressivement.

Si tu veux une demi-vie de quelques jours, les tables spip_visites_temp
et spip_referers_temp actuelles peuvent constituer une approximation sympa
pour un recalcul quotidien. En plus, ça supprime la possibilité de
pourcentages supérieurs à 100 (cas où le popularite_max est calculé après
une vague de visites).

@ Antoine Pitrou <antoine@rezo.net> :

Deux choses comme ça :

- il vaudrait mieux ne pas faire d'écriture dans spip_articles à chaque
requête ; sur un gros site ça pourrait (?) faire mal.

OK. Je doute que ça fasse mal, mais... on a prévu n'est-ce pas de
fractionner spip_articles en deux parties ; ça pourra aller dans la partie
la plus 'fine'.

(accessoirement, je ne sais pas si la précision des flottants est
suffisante pour accumuler une série de power() avec un exposant très
proche de 1)

Ah bon? Je n'ai besoin que de 8 chiffres de précision... Mais si on veut
être sûrs il suffira de compter en minutes, et pas en secondes, dans ce
cas-là. Ca fait juste
    a = 1 - (1/2)^(-3*24*60) et
    UPDATE ... (1-a)^((NOW()-maj)/60)

- si un article n'est pas visité depuis X heures ou jours, il va garder
la popularité d'alors au lieu de la voir baisser progressivement.

Oui, j'ai oublié de mentionner, une fois par jour ou par heure, un UPDATE
général sur tous les spip_articles, avec la même formule (mais $points=0)

Si tu veux une demi-vie de quelques jours, les tables spip_visites_temp
et spip_referers_temp actuelles peuvent constituer une approximation sympa
pour un recalcul quotidien. En plus, ça supprime la possibilité de
pourcentages supérieurs à 100 (cas où le popularite_max est calculé après
une vague de visites).

C'est géré par le MAX(100, xxx) dans ma propale.

> D'où, sortez vos calculettes, et si je ne me suis pas trompé,
> a = 1 - (1/2)^(-3*24*60*60) = 2,67418 E-06;
> b = 0,231030525;
>
> [Si on veut quelque chose de plus "réactif", on peut partir sur
> (1-a)^(12*60*60) = 1/2 ; soit une demi-vie d'une demi-journée]

On peut même mettre la demi-vie et les points pour entrées directes dans un
meta quelconque -- à la réflexion 1 jour ce serait mieux que 3.

L'intérêt de mon truc, me semble-t-il, c'est que c'est plus juste pour ce
qu'on veut faire, d'une part, et explicable, d'autre part (moins "arbitraire")

-- Fil

@ Fil <fil@rezo.net> :

> - il vaudrait mieux ne pas faire d'écriture dans spip_articles à chaque
> requête ; sur un gros site ça pourrait (?) faire mal.

OK. Je doute que ça fasse mal, mais... on a prévu n'est-ce pas de
fractionner spip_articles en deux parties ; ça pourra aller dans la partie
la plus 'fine'.

Comment savoir si ça va faire ramer ? Je peux tester sur le Diplo, mais on
sait d'expérience que le serveur est assez costaud pour que ça ne se
remarque pas... je peux aussi créer une nouvelle table
spip_articles_popularite en attendant... !

Au fait : si vous pensez que votre système (ou l'un des deux) est meilleur
que le mien, dites-le moi que j'arrête tout de suite.. Au fond, ça m'est un
peu équilatéral, c'est juste que ça m'amuse.

-- Fil

Dans ce cas, on peut très simplement adapter mon système, puisqu'il fournit déjà (avant la boucle finale qui ramène à une valeur max) un nombre d'entrées pondérées, puisque le principe est déjà que 1 visite hier = 1 point de popularité (1 visite avant hier = 1/2 point, etc.); et plusieurs points ($coeff_referer) pour une entrée directe.

Il suffit de travailler sur un grand nombre (ne plus se ramener à un pourcentage en fin de calcul), et d'ajouter à chaque visite 1 point de popularité, plusieurs points si entrée directe. Et toutes les 24 heures, on recalcule le tout avec optimiser_referers pour normaliser l'ensemble sur une base temporelle et sucrer les hits multiples dans le décompte de popularité.

ARNO*

Non, je ne crois pas : ce que je propose est beaucoup plus simple et mis à
jour **en continu**. Le truc toutes les heures est là juste pour éviter
qu'un article jamais consulté (par ex. supprimé) conserve une popularité
élevée faute de màj. Et il n'y a rien à normaliser ni à sucrer ni à
recalculer toutes les 24 heures.

-- Fil

OK. Je doute que ça fasse mal, mais... on a prévu n'est-ce pas de
fractionner spip_articles en deux parties ; ça pourra aller dans la
partie la plus 'fine'.

Heu, prévu, c'est surtout une idée (très intéressante). Ca nécessite
un nombre impressionnant de changements dans le code (et de complications).

Comment savoir si ça va faire ramer ? Je peux tester sur le Diplo, mais
on sait d'expérience que le serveur est assez costaud pour que ça ne se
remarque pas...

Oui :wink:

Au fait : si vous pensez que votre système (ou l'un des deux) est
meilleur que le mien, dites-le moi que j'arrête tout de suite..

Ah ben, j'aime bien le mien, pour sûr :))

Le truc : si on veut garder l'historique des visites et les derniers
referers, il faut de toute façon garder les tables actuelles. Donc pourquoi
pas mettre à jour la popularité toutes les heures avec un algo ressemblant
au tien ? La granularité sera moins grande (temps en heures et non en
seconde lors de l'"amortissement"), mais ça reste acceptable.

a+

Antoine.

Le truc : si on veut garder l'historique des visites et les derniers
referers, il faut de toute façon garder les tables actuelles. Donc pourquoi
pas mettre à jour la popularité toutes les heures avec un algo ressemblant
au tien ? La granularité sera moins grande (temps en heures et non en
seconde lors de l'"amortissement"), mais ça reste acceptable.

Euh, oui, ça peut se défendre, je ne sais pas très bien ce que fait le code
actuel en fait.

-- Fil

Euh, oui, ça peut se défendre, je ne sais pas très bien ce que fait le
code actuel en fait.

Je parlais du code dans inc-stats : il stocke visites et referers dans
des tables temporaires (spip_visites_temp et spip_referers_temp) dont les
infos sont compilées une fois par jour (dans inc-statistiques).