2011-05-24 12 views
7

Jak widać tutaj: http://www.evanmiller.org/how-not-to-sort-by-average-rating.htmlJaki jest odpowiednik statystyki statystyki pseudo-rubla w Haskell?

Oto kod Ruby sama, realizowany w bibliotece Statistics2:

# inverse of normal distribution ([2]) 
# Pr((-\infty, x]) = qn -> x 
def pnormaldist(qn) 
    b = [1.570796288, 0.03706987906, -0.8364353589e-3, 
     -0.2250947176e-3, 0.6841218299e-5, 0.5824238515e-5, 
     -0.104527497e-5, 0.8360937017e-7, -0.3231081277e-8, 
     0.3657763036e-10, 0.6936233982e-12] 

    if(qn < 0.0 || 1.0 < qn) 
    $stderr.printf("Error : qn <= 0 or qn >= 1 in pnorm()!\n") 
    return 0.0; 
    end 
    qn == 0.5 and return 0.0 

    w1 = qn 
    qn > 0.5 and w1 = 1.0 - w1 
    w3 = -Math.log(4.0 * w1 * (1.0 - w1)) 
    w1 = b[0] 
    1.upto 10 do |i| 
    w1 += b[i] * w3**i; 
    end 
    qn > 0.5 and return Math.sqrt(w1 * w3) 
    -Math.sqrt(w1 * w3) 
end 

Odpowiedz

5

Jest to dość proste do przetłumaczenia:

module PNormalDist where 

pnormaldist :: (Ord a, Floating a) => a -> Either String a 
pnormaldist qn 
    | qn < 0 || 1 < qn = Left "Error: qn must be in [0,1]" 
    | qn == 0.5  = Right 0.0 
    | otherwise  = Right $ 
     let w3 = negate . log $ 4 * qn * (1 - qn) 
      b = [ 1.570796288, 0.03706987906, -0.8364353589e-3, 
       -0.2250947176e-3, 0.6841218299e-5, 0.5824238515e-5, 
       -0.104527497e-5, 0.8360937017e-7, -0.3231081277e-8, 
       0.3657763036e-10, 0.6936233982e-12] 
      w1 = sum . zipWith (*) b $ iterate (*w3) 1 
     in (signum $ qn - 0.5) * sqrt (w1 * w3) 

Po pierwsze, spójrzmy na rubin - zwraca wartość, ale czasami drukuje komunikat o błędzie (jeśli podano niewłaściwy argument). Nie jest to zbyt zabawne, więc przyjmijmy z powrotem wartość Either String a - gdzie zwrócimy Left String z komunikatem o błędzie, jeśli zostanie podany niewłaściwy argument, a inaczej: Right a.

Teraz możemy sprawdzić dwa przypadki u góry:

  • qn < 0 || 1 < qn = Left "Error: qn must be in [0,1]" - jest to warunek błędu, gdy qn jest poza zasięgiem.
  • qn == 0.5 = Right 0.0 - Czy kontrola ta Ruby qn == 0.5 and return * 0.0

Następna w kolejce, definiujemy w1 w kodzie Ruby. Ale zredukowaliśmy to kilka linii później, co nie jest bardzo rubinowe. Wartość, którą przechowujemy w w1 po raz pierwszy jest używana bezpośrednio w definicji w3, więc dlaczego nie pominiemy jej przechowywania w w1? Nie musimy nawet wykonywać kroku qn > 0.5 and w1 = 1.0 - w1, ponieważ używamy produktu w1 * (1.0 - w1) w definicji w3.

Pomijamy to wszystko i przechodzimy od razu do definicji w3 = negate . log $ 4 * qn * (1 - qn).

Następna jest definicja b, która jest linią prostą z kodu ruby ​​(składnia ruby ​​dla literału tablicowego to składnia haskella dla listy).

Oto najtrudniejszy bit - definiujący ostateczną wartość w3. Jaki kod Ruby robi w

w1 = b[0] 
1.upto 10 do |i| 
    w1 += b[i] * w3**i; 
end 

jest to, co nazywa się fałd - zmniejszenie zbiór wartości (przechowywany w tablicy ruby) do pojedynczej wartości. Możemy przekształcić to bardziej użyciu Array#reduce funkcjonalnie (ale nadal w Ruby):

w1 = b.zip(0..10).reduce(0) do |accum, (bval,i)| 
    accum + bval * w3^i 
end 

Uwaga jak podsunąłem b[0] do pętli przy użyciu tożsamości b[0] == b[0] * w3^0.

Teraz możemy portu to bezpośrednio do Haskell, ale to trochę brzydki

w1 = foldl 0 (\accum (bval,i) -> accum + bval * w3**i) $ zip b [0..10] 

Zamiast Złamałem go na kilka etapów - Po pierwsze, tak naprawdę nie potrzebują i, po prostu potrzebne moce w3 (począwszy od w3^0 == 1), więc obliczyć te z iterate (*w3) 1.

Następnie, zamiast skompresować je parami z elementami b, ostatecznie potrzebujemy ich produktów, abyśmy mogli je zamapować na produkty każdej pary przy użyciu zipWith (*) b.

Teraz nasza funkcja składania jest naprawdę łatwa - wystarczy podsumować produkty, które możemy wykonać za pomocą sum.

Wreszcie możemy zdecydować, czy powrócić plus lub minus sqrt (w1 * w3), zależnie od tego czy qn jest większa lub mniejsza niż 0,5 (my już wiem, że to nie jest równe). Więc zamiast obliczać pierwiastek kwadratowy w dwóch oddzielnych miejscach, jak w kodzie ruby, przeliczyłem go jeden raz i pomnożyłem przez +1 lub -1 według oznaczenia (signum).

5

Kopanie wokół na Hackage, istnieje wiele bibliotek do statystyk:

Chcesz wersję pnormaldist, która "Zwraca wartość P normalizmu (x)".

Może coś tam zapewnia to, czego potrzebujesz?

+0

Ja naprawdę nic nie wiem o statystykach: P. Czy wiesz, która z tych funkcji jest odpowiednikiem pnormaldist? –

+0

Nie sądzę, że żadna z tych funkcji jest dokładnie tym, czego potrzebujesz. Potrzebujesz odwrotności funkcji erf, jeśli się nie mylę. – augustss

0

Krótkie spojrzenie na hackage nie ujawniło niczego, więc sugeruję przetłumaczyć kod ruby ​​na Haskell. To dość proste.

3

Funkcja, którą chcesz, jest teraz dostępna w pakiecie erf podczas włamań. Nazywa się invnormcdf.

1

tutaj jest mój przedział ufności wynik Wilsona dla Bernoulliego parametru w node.js

wilson.normaldist = function(qn) { 
    var b = [1.570796288, 0.03706987906, -0.0008364353589, -0.0002250947176, 0.000006841218299, 0.000005824238515, -0.00000104527497, 0.00000008360937017, -0.000000003231081277, 
     0.00000000003657763036, 0.0000000000006936233982 
    ]; 
    if (qn < 0.0 || 1.0 < qn) return 0; 
    if (qn == 0.5) return 0; 
    var w1 = qn; 
    if (qn > 0.5) w1 = 1.0 - w1; 
    var w3 = -Math.log(4.0 * w1 * (1.0 - w1)); 
    w1 = b[0]; 

    function loop(i) { 
     w1 += b[i] * Math.pow(w3, i); 
     if (i < b.length - 1) loop(++i); 
    }; 
    loop(1); 
    if (qn > 0.5) return Math.sqrt(w1 * w3); 
    else return -Math.sqrt(w1 * w3); 
} 

wilson.rank = function(up_votes, down_votes) { 
    var confidence = 0.95; 
    var pos = up_votes; 
    var n = up_votes + down_votes; 
    if (n == 0) return 0; 
    var z = this.normaldist(1 - (1 - confidence)/2); 
    var phat = 1.0 * pos/n; 
    return ((phat + z * z/(2 * n) - z * Math.sqrt((phat * (1 - phat) + z * z/(4 * n))/n))/(1 + z * z/n)) * 10000; 
} 
0

Kod Ruby jest nieudokumentowane; nie ma specyfikacji tego, co ta funkcja ma robić. Jak ktoś wie, czy robi to, co jest zamierzone?

Nie ślepo skopiowałbym i wkleiłam tę arytmetykę z jednej implementacji do drugiej (tak jak zrobił to autor pakietu Ruby).

Cytat jest podany jako ([2]) w komentarzu, ale jest zwisający. Znajdujemy go w bloku komentarza natywnego kodu C w pliku _statistics2.c.

/* 
    statistics2.c 
    distributions of statistics2 
    by Shin-ichiro HARA 
    2003.09.25 
    Ref: 
    [1] http://www.matsusaka-u.ac.jp/~okumura/algo/ 
    [2] http://www5.airnet.ne.jp/tomy/cpro/sslib11.htm 
*/ 

bardzo niechlujny pracy tylko wymienić kod źródłowy C gdzie żłobienia współczynniki, a nie oryginalne źródło wzoru.

Łącze [1] już nie działa; nie znaleziono serwera. Na szczęście chcemy uzyskać [2]. Jest to strona w języku japońskim z kodem C dla różnych funkcji. Referencje są podane. Ten, który chcemy, to pnorm. W tabeli algorytm przypisuje się 戸 田 の 近似 式, co oznacza "Zbliżanie Tody".

Toda to popularne nazwisko w Japonii; potrzeba więcej pracy detektywistycznej, aby dowiedzieć się, kto to jest.

Po długim wysiłku, oto: papier (japoński): The Minimax Approximation for Percentage Points of the Standard Normal Distribution (1993) przez Hideo Toda i Harumi Ono.

algorytm przypisuje się Toda (jestem zakładając ten sam, który jest gazety współautor), datowany na 1967 na str 19.

Wydaje się dość niejasne; prawdopodobnym uzasadnieniem użycia go w pakiecie Ruby jest to, że został znaleziony w kodzie źródłowym pochodzenia domowego, powołując się na nazwę krajowego akademika.