2012-02-24 19 views
5

Mam tabeli z powiedzmy 250 URL:losowy rozkład Gaussa w PostgreSQL

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

Adresy te odpowiadają każdy na stronie internetowej. Każda z witryn ma inną popularność. Załóżmy, że najbardziej popularna jest wersja id=125 (jedna z nich jest skoncentrowana na gausku), najpopularniejsze to id=1 lub id=250.

Chcę wypełnić tabelę "logów", taką jak poniższa, z wartością url spośród tych podanych w tabeli "URL", ale biorąc pod uwagę, że różne adresy URL mogą pojawiać się częściej (na przykład URL, którego identyfikator 125 będzie najbardziej popularne).

create table log (
    id serial, 
    url_id integer 
) 

Chcę uniknąć używania random(), ponieważ jest jednolity i niezbyt "prawdziwy".

Jak można to osiągnąć dzięki Postgresql?

+2

Dlaczego zakładasz, że popularność lub ranking ma rozkład Gaussiona? – wildplasser

+2

Możesz obliczyć jakąkolwiek dystrybucję za pomocą pliku PDF tej dystrybucji za pomocą RAND (która wytwarza wartości między 0 a 1, prawda?). Dla dystrybucji gaussowskiej będzie to 1/2 (1 + erf (x-mu)/sqrt (2sigma^2)) - patrz http://en.wikipedia.org/wiki/Normal_distribution –

+0

@wildplasser: ponieważ to prawo wydaje się całkiem dobre dla tego, co próbuję wymodelować. Przyznaję, że mógł to być jakikolwiek inny! – SCO

Odpowiedz

6

Suma 12 rozkładów równomiernych w zakresie [0, 1) jest dobrym przybliżeniem do rozkładu Gaussa ograniczonego w zakresie [0, 12]. Następnie można go łatwo przeskalować przez pomnożenie przez stałą, a następnie dodanie/odjęcie stałej.

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

Zaakceptowałem to, ponieważ uznałem to za najprostszy i najbardziej elegancki sposób, niezależnie od używanego języka. Podziękowania dla wszystkich innych współpracowników. – SCO

1

Prostym faktem jest to, że chcesz stworzyć własną funkcję, która opakowuje rand() w coś, co zapewnia dystrybucję gaussowską albo niejawnie, albo jawnie.

Nie mam tła statystycznego, aby powiedzieć, jak przekształcić jednolitą dystrybucję w gaussowską, ale trzeba by napisać konwerter. Coś jak pod warunkiem na http://www.perlmonks.org/?node_id=26889 (jeśli nie lubisz Perla, prawdopodobnie mógłbyś przepisać to w pl/pgsql lub nawet w zwykłym SQL).

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

Szukałem sposobu na generowanie liczb zgodnie z rozkładem gaussowskim i najpierw znalazłem ten wpis. Dlatego właśnie udostępniłem to, co znalazłem zaraz po:

Istnieje, od co najmniej PostGreSQL 8.4, dodatkowy moduł o nazwie tablefunc (http://www.postgresql.org/docs/9.2/static/tablefunc.html).

Proponuje funkcję normal_rand (n, mean, stddev) generującą n liczb pseudolosowych przy użyciu rozkładu gaussowskiego (więc ta funkcja zwraca zestaw wartości, zazwyczaj używany w klauzuli FROM). Jeśli jednak ustawisz n na 1, może on być użyty jako funkcja zwracająca wartość, a nie zbiór wartości.

Rozpatrując NB10 tabeli zawierającej 10 płyt, dwa następujące pytania zwraca zestaw 10 pseudolosowych liczb wyniku standardowego rozkładu gaussowskiego (średnia = 0, OdchStd = 1)

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

i

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

mam nadzieję, że to może pomóc każdemu w przyszłości ... :-)

Aby odpowiedzieć na to pytanie szczegółowo, można użyć coś takiego:

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

Niestety, możliwe jest uzyskanie odpowiedzi w zakresie [0, 249] za pomocą tego zapytania.Można by na przykład:

  • użyć kwerendy cyklicznej, co znajdę trochę overkill dla odrzucając wartości nie w przedziale [0, 249] lub
  • zrobić swoją select into pętli w języku gospodarza , akceptując wartość tylko wtedy, gdy jej w zakresie [0, 249] lub
  • użyć operator modulo pozostać w [0, 250 [zakres, myślę, że jest to najlepsze rozwiązanie, chociaż nieznacznie zmienia Gaussa krzywa. Oto ostateczna zapytania Proponuję użyć (modulo/+/modulo sztuczki dlatego -x modulo y z xa liczby dodatniej daje liczbę ujemną w PostgreSQL, która nie jest złe: P):

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

Moduł tablefunc zapewnia funkcji losowej z rozkładu normalnego. Można sprawdzić, czy jest zainstalowana przy użyciu:

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

Zapytanie powyżej powinny generować jedną wartość w normalnym rozkładzie

Jeśli nie masz go zainstalowanego, spróbuj tego:

CREATE EXTENSION "tablefunc"; 

W przeciwnym razie musisz zalogować się jako a super user and install the module.

+0

To też jest niezwykle interesujące i otwiera teraz horyzonty na przykład na tabele przestawne. Dziękuję Ci bardzo ! – SCO