Précédent   Forum des professionnels en informatique > Bases de données > PostgreSQL > Requêtes
Requêtes Forum d'entraide sur les requêtes SQL spécifiques à PostgreSQL, les triggers, les vues, etc.
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 11/01/2011, 18h00   #1
Invité régulier
 
Inscription : février 2003
Messages : 26
Détails du profil
Informations forums :
Inscription : février 2003
Messages : 26
Points : 8
Points : 8
Par défaut Sélection aléatoire pondérée d'une ligne

Bonjour

Je me demandais s'il était possible de réaliser une sélection aléatoire en pondérant les chances de sélections selon une expression.

J'ai trouvé qu'il était possible de choisir aléatoire une ligne avec :
Code :
1
2
3
4
5
 
SELECT col
FROM tab
ORDER BY random()
LIMIT 1;
Mais la sélection est alors strictement aléatoire. J'aurais aimé pondérer les chances de sélection de chaque ligne. Une idée?

Merci d'avance...
Babcool est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 11/01/2011, 18h48   #2
Modérateur
 
Avatar de CinePhil
 
Homme Philippe Leménager
Ingénieur d'études en informatique
Inscription : août 2006
Messages : 10 985
Détails du profil
Informations personnelles :
Nom : Homme Philippe Leménager
Âge : 48
Localisation : France, Haute Garonne (Midi Pyrénées)

Informations professionnelles :
Activité : Ingénieur d'études en informatique
Secteur : Enseignement

Informations forums :
Inscription : août 2006
Messages : 10 985
Points : 18 232
Points : 18 232
Envoyer un message via MSN à CinePhil
Quelles seraient les règles de cette pondération ?
__________________
Philippe Leménager. Ingénieur d'étude à l'École Nationale de Formation Agronomique.
Mon blog sur la conception des BDD, le langage SQL, le PHP avec Zend Framework...
« Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
À la maison comme au bureau, j'utilise Mandriva Linux ou Mageïa ! Soutenons l'industrie logicielle française !
Linuxiens, comptez-vous !
CinePhil est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 12/01/2011, 02h03   #3
Invité régulier
 
Inscription : février 2003
Messages : 26
Détails du profil
Informations forums :
Inscription : février 2003
Messages : 26
Points : 8
Points : 8
En appellant score la valeur de pondération, la probabilité de sélections d'une ligne sera son score / somme des scores.

La manière habituelle de procéder (mais dans du code, pas dans le cadre d'un accès à une base) est de normaliser la somme des scores, puis de générer un nombre aléatoire entre 0 et 1 et de réaliser le cumul des scores jusqu'au moment ou l'on atteint le nombre aléatoire tiré. Je ne sais pas si cela peut être réalisé en SQL ...
Babcool est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 12/01/2011, 10h27   #4
Modérateur
 
Avatar de CinePhil
 
Homme Philippe Leménager
Ingénieur d'études en informatique
Inscription : août 2006
Messages : 10 985
Détails du profil
Informations personnelles :
Nom : Homme Philippe Leménager
Âge : 48
Localisation : France, Haute Garonne (Midi Pyrénées)

Informations professionnelles :
Activité : Ingénieur d'études en informatique
Secteur : Enseignement

Informations forums :
Inscription : août 2006
Messages : 10 985
Points : 18 232
Points : 18 232
Envoyer un message via MSN à CinePhil
Et où est stocké ce score ? Une colonne de la table ?
__________________
Philippe Leménager. Ingénieur d'étude à l'École Nationale de Formation Agronomique.
Mon blog sur la conception des BDD, le langage SQL, le PHP avec Zend Framework...
« Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
À la maison comme au bureau, j'utilise Mandriva Linux ou Mageïa ! Soutenons l'industrie logicielle française !
Linuxiens, comptez-vous !
CinePhil est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 12/01/2011, 12h42   #5
Invité régulier
 
Inscription : février 2003
Messages : 26
Détails du profil
Informations forums :
Inscription : février 2003
Messages : 26
Points : 8
Points : 8
Soit c'est une colonne de la table, soit une expression composée de plusieurs colonnes (si possible, mais une colonne de la table me convient aussi).
Babcool est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 12/01/2011, 13h43   #6
Modérateur
 
Avatar de CinePhil
 
Homme Philippe Leménager
Ingénieur d'études en informatique
Inscription : août 2006
Messages : 10 985
Détails du profil
Informations personnelles :
Nom : Homme Philippe Leménager
Âge : 48
Localisation : France, Haute Garonne (Midi Pyrénées)

Informations professionnelles :
Activité : Ingénieur d'études en informatique
Secteur : Enseignement

Informations forums :
Inscription : août 2006
Messages : 10 985
Points : 18 232
Points : 18 232
Envoyer un message via MSN à CinePhil
Si je comprends bien, ce que tu souhaites, c'est ne garder que A lignes prises aléatoirement parmi les P premières classées par score ?
__________________
Philippe Leménager. Ingénieur d'étude à l'École Nationale de Formation Agronomique.
Mon blog sur la conception des BDD, le langage SQL, le PHP avec Zend Framework...
« Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
À la maison comme au bureau, j'utilise Mandriva Linux ou Mageïa ! Soutenons l'industrie logicielle française !
Linuxiens, comptez-vous !
CinePhil est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 12/01/2011, 15h04   #7
Invité régulier
 
Inscription : février 2003
Messages : 26
Détails du profil
Informations forums :
Inscription : février 2003
Messages : 26
Points : 8
Points : 8
Nan, pas exactement. C'est peut-être cette solution que je garderais au final, mais mon objectif est de ne sélectionner qu'une ligne pour l'instant.

Je vais prendre un petit exemple pour expliquer un peu mieux ma situation:

J'ai 5 entrées
id score
1 5
2 3
3 2
4 3
5 4

On a somme des scores = 17
Je voudrais faire un tirage aléatoire avec la loi de proba suivante
P(1) = 5/ 17
P(2) = 3 / 17
P(3) = 2 / 17
...

En général, dans mon code, quand je dois faire une telle sélection, je fais ainsi :
1. Je définis un score normalisé :
S_n(1) = 5/17
S_n(2) = 3/17
...

2. Je tire un nombre aléatoire en 0 et 1, par exemple 9/17

3. Je réalise un cumul des scores jusqu'a atteindre mon nombre
C_s(1) = 5/17
C_s(2) = 7/17
C_s(3) = 10/17
Je m'arrête dès que le nombre aléatoire tiré est atteint, la ligne sélectionnée serait alors ici la 3.

Il est possible de trier les lignes en ordre de score décroissant pour optimiser la dernière étape, mais ce n'est pas une priorité pour l'instant.

Mon objectif serait de réaliser ce tirage aléatoire directement dans la bdd plutot que de charger de grandes quantitiés d'objets pour réaliser le tirage ensuite en local...
Babcool est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 12/01/2011, 15h38   #8
Modérateur
 
Avatar de CinePhil
 
Homme Philippe Leménager
Ingénieur d'études en informatique
Inscription : août 2006
Messages : 10 985
Détails du profil
Informations personnelles :
Nom : Homme Philippe Leménager
Âge : 48
Localisation : France, Haute Garonne (Midi Pyrénées)

Informations professionnelles :
Activité : Ingénieur d'études en informatique
Secteur : Enseignement

Informations forums :
Inscription : août 2006
Messages : 10 985
Points : 18 232
Points : 18 232
Envoyer un message via MSN à CinePhil
En une requête, tu peux facilement obtenir la probabilité P pour chaque id :
Code :
1
2
3
4
5
6
SELECT id, score, 
    score / (
        SELECT SUM(score)
        FROM la_table
    ) AS P
FROM la_table
Ensuite tu peux obtenir l'écart des probabilités de la table par rapport à une valeur donnée et il suffit de trier ces écarts dans l'ordre et de ne conserver que la première ligne résultat :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SELECT id,
    ABS(
        valeur_donnee - (
            score / (
                SELECT SUM(score)
                FROM la_table
            )
        )
    ) AS ecart
FROM la_table
ORDER BY 
    ABS(
        valeur_donnee - (
            score / (
                SELECT SUM(score)
                FROM la_table
            )
        )
    ) AS ecart
LIMIT 1
__________________
Philippe Leménager. Ingénieur d'étude à l'École Nationale de Formation Agronomique.
Mon blog sur la conception des BDD, le langage SQL, le PHP avec Zend Framework...
« Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
À la maison comme au bureau, j'utilise Mandriva Linux ou Mageïa ! Soutenons l'industrie logicielle française !
Linuxiens, comptez-vous !
CinePhil est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 12/01/2011, 15h49   #9
Invité régulier
 
Inscription : février 2003
Messages : 26
Détails du profil
Informations forums :
Inscription : février 2003
Messages : 26
Points : 8
Points : 8
Effectivement, il y a une bonne piste ici. Par contre, il faut modifier un peu la première requete que tu propose. En effet, au lieu de stocker le score pondéré, je peux stocker le cumul de tous les scores supérieurs ou égal au score considéré. Pour cela, au lieu de diviser le score de la ligne par la somme des scores, on diviser la somme des scores supérieurs par la somme de tous les scores.

Par contre, ça va douiller en cout de calcul, sauf si le moteur arrive a le gérer très bien (genre, faire une seule fois la somme, et entre chaque ajout, faire la vérification). Car j'ai l'impression que je vais être en n^2 (pour chaque ligne, je réalise la somme des scores supérieurs ou égaux a cette ligne). J'essayerai de limiter l'amplitude de la sélection initiale dans ce cas...

Je peux aussi me passer du abs ensuite, pour ne prendre que la ligne qui a le cumul des scores minimal tout en étant supérieur au nombre tiré au hasard.

Je viendrais poster la requete dès que ca sera fait
Babcool est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 12/01/2011, 15h59   #10
Modérateur
 
Avatar de CinePhil
 
Homme Philippe Leménager
Ingénieur d'études en informatique
Inscription : août 2006
Messages : 10 985
Détails du profil
Informations personnelles :
Nom : Homme Philippe Leménager
Âge : 48
Localisation : France, Haute Garonne (Midi Pyrénées)

Informations professionnelles :
Activité : Ingénieur d'études en informatique
Secteur : Enseignement

Informations forums :
Inscription : août 2006
Messages : 10 985
Points : 18 232
Points : 18 232
Envoyer un message via MSN à CinePhil
Citation:
Envoyé par Babcool Voir le message
Effectivement, il y a une bonne piste ici. Par contre, il faut modifier un peu la première requete que tu propose. En effet, au lieu de stocker le score pondéré, je peux stocker le cumul de tous les scores supérieurs ou égal au score considéré.
Tu fais comme tu veux, je suis juste parti de ton exemple.

Citation:
Pour cela, au lieu de diviser le score de la ligne par la somme des scores, on diviser la somme des scores supérieurs par la somme de tous les scores.
Euh... si tu le dis...

Citation:
Par contre, ça va douiller en cout de calcul, sauf si le moteur arrive a le gérer très bien (genre, faire une seule fois la somme, et entre chaque ajout, faire la vérification). Car j'ai l'impression que je vais être en n^2 (pour chaque ligne, je réalise la somme des scores supérieurs ou égaux a cette ligne). J'essayerai de limiter l'amplitude de la sélection initiale dans ce cas...
Tu réfléchis en développeur qui boucle sur un tableau alors qu'un SGBD travaille sur une logique ensembliste.

Le SGBD va se débrouiller tout seul pour optimiser tout ça :
- Calcul de la somme des scores en une seule fois (la petite requête dans le SELECT) et mémorisation du résultat ;
- Calcul de l'écart pour chaque ligne en utilisant le résultat mémorisé (donc il ne fait pas la petite requête à chaque ligne) ;
- Tri de l'ensemble des lignes résultat.

Le calcul n'est pas compliqué, je pense qu'il peut faire ça très vite, même avec quelques centaines de milliers de lignes.

Le seul truc qu'il faut peut être faire et que je n'ai pas inclus dans ma requête, c'est du trasntypage en FLOAT pour que la division donne bien 0,x et ne soit pas arrondie à 0 ou à 1.
Je n'ai pas Postgresql sous la main pour vérifier.

Bonne chance !
__________________
Philippe Leménager. Ingénieur d'étude à l'École Nationale de Formation Agronomique.
Mon blog sur la conception des BDD, le langage SQL, le PHP avec Zend Framework...
« Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
À la maison comme au bureau, j'utilise Mandriva Linux ou Mageïa ! Soutenons l'industrie logicielle française !
Linuxiens, comptez-vous !
CinePhil est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 19h43.


 
 
 
 
Partenaires

Hébergement Web