2012-02-24 14 views
5

Tengo una tabla de digamos 250 URL:distribución aleatoria gaussiana en PostgreSQL

create table url (
    id serial, 
    url varchar(64) 
) 

Estas URLs corresponden cada una a un sitio web. Cada uno de los sitios web tiene una popularidad diferente. Digamos que el id=125 (el que está centrado en el gaussiano) es el más popular, los que están en id=1 o id=250 son los menos populares.

Quiero rellenar una tabla "log" como la siguiente con un valor de url entre los proporcionados en la tabla "url", pero teniendo en cuenta que URLs diferentes pueden aparecer con más frecuencia (por ejemplo URL cuya id. es 125 será el más popular).

create table log (
    id serial, 
    url_id integer 
) 

que desea evitar el uso random() ya que es uniforme y no muy "real".

¿Cómo se puede lograr esto con Postgresql?

+2

¿Por qué asumes que la popularidad o el ranking tienen una distribución de Gaussion? – wildplasser

+2

Puede calcular cualquier distribución usando el PDF de esa distribución usando RAND (que produce valores entre 0 y 1, ¿verdad?). Para la distribución gaussiana, eso sería 1/2 (1 + erf (x-mu)/sqrt (2sigma^2)) - ver http://en.wikipedia.org/wiki/Normal_distribution –

+0

@wildplasser: porque esa ley parece bastante bueno para lo que intento modelar. ¡Admito que podría haber sido cualquier otro! – SCO

Respuesta

6

La suma de 12 distribuciones uniformes en el rango [0, 1) es una buena aproximación a una distribución gaussiana limitada en el rango [0, 12]. Esto puede volver a escalarse fácilmente multiplicando por una constante y luego sumando/restando una constante.

select 
    random() + 
    random() + 
    random() + 
    random() + 
    random() + 
    random() + 
    random() + 
    random() + 
    random() + 
    random() + 
    random() + 
    random(); 

http://books.google.com/books?id=EKA-yeX2GVgC&pg=PA185&lpg=PA185&dq=%22sum+of+12+uniform+random+variables%22&source=bl&ots=YfwwE0fBB3&sig=HX9J9Oe6x316kVL8uamDU_GOsn4&hl=en&sa=X&ei=bJLZUur1GozaqwGHm4DQDQ&ved=0CEUQ6AEwAw#v=onepage&q=%22sum%20of%2012%20uniform%20random%20variables%22&f=false

+0

Acepté esto porque lo encontré de la manera más fácil y elegante, sea cual sea el idioma que se use. Gracias a todos los demás contribuyentes. – SCO

1

El hecho simple es que desea crear su propia función que envuelve rand() en algo que proporciona una distribución gaussiana implícita o explícitamente.

No tengo los antecedentes estadísticos para decirte cómo transformar una distribución uniforme en una gaussiana, pero tendrías que escribir un convertidor. Algo así como se proporciona en http://www.perlmonks.org/?node_id=26889 (si no te gusta Perl, probablemente puedas volver a escribir esto en pl/pgsql o incluso SQL simple).

CREATE OR REPLACE FUNCTION gaussian_rand() RETURNS numeric LANGUAGE PLPERL VOLATILE AS 
$$ 
    my ($u1, $u2); # uniformly distributed random numbers 
    my $w;   # variance, then a weight 
    my ($g1, $g2); # gaussian-distributed numbers 

    do { 
     $u1 = 2 * rand() - 1; 
     $u2 = 2 * rand() - 1; 
     $w = $u1*$u1 + $u2*$u2; 
    } while ($w >= 1); 

    $w = sqrt((-2 * log($w))/$w); 
    $g2 = $u1 * $w; 
    $g1 = $u2 * $w; 
    # return both if wanted, else just one 
    return $g1; 

$$; 
7

Estaba buscando una forma de generar números según una distribución gaussiana y primero encontré esta publicación. Es por eso que comparto lo que he encontrado justo después:

Hay, desde al menos PostGreSQL 8.4, un módulo adicional llamado tablefunc (http://www.postgresql.org/docs/9.2/static/tablefunc.html).

Propone una función normal_rand (n, mean, stddev) generando n números pseudoaleatorios utilizando una distribución gaussiana (por lo que esta función devuelve un conjunto de valores, normalmente utilizados en la cláusula FROM). Sin embargo, si configura n como 1, se puede usar como una función que devuelve un valor y no un conjunto de valores.

Considerando un NB10 tabla que contiene 10 registros, los dos siguientes consultas devuelven un conjunto de 10 números pseudo-aleatorios siguiendo una distribución gaussiana estándar (media = 0, STDDEV = 1)

SELECT normal_rand(1, 0, 1) FROM nb10; 

y

SELECT * from normal_rand(10, 0, 1); 

espero que esto podría ayudar a cualquier persona en el futuro ... :-)

Para responder a su pregunta en concreto, podría utilizar algo como:

SELECT floor(random_rand(1, 0, 1) * 250 + 125); 

Desafortunadamente, es posible obtener una respuesta que no esté en el rango [0, 249] con esta consulta.Usted podría, por ejemplo:

  • uso de una consulta recursiva, que me parece un poco exagerado, para descartar los valores no en el rango [0, 249], o
  • hacer su selecto en un bucle en su lengua de acogida , aceptando el valor solo si está en el rango [0, 249] o
  • use el operador de módulo para permanecer en el [0, 250 [rango, creo que esta es la mejor solución, aunque altera levemente el gaussiano curva. Aquí está la consulta final le sugiero que utilice (los/+/trucos de módulo de módulo es porque -x módulo y con xa número positivo da un número negativo en PostgreSQL, que no es una mala cosa: p):

    SELECT ((floor(normal_rand(1,0,1)*250 + 125)::int % 250) + 250) % 250 as v; 
    
1

El módulo de tablefunc proporciona una función aleatoria con una distribución normal. Puede probar si está instalado usando:

SELECT normal_rand(1, 0, 1); -- generates 1 single value with mean 0 and a standard deviation of 1 

La consulta anterior debe generar un valor único en una distribución normal

Si no lo tiene instalado, intente esto:

CREATE EXTENSION "tablefunc"; 

De lo contrario, deberá iniciar sesión como a super user and install the module.

+0

Oh, esto es muy interesante también, y abre ahora horizontes para tablas dinámicas, por ejemplo. Muchas gracias ! – SCO