packages feed

nlp-scores 0.2.1 → 0.2.2

raw patch · 2 files changed

+107/−21 lines, 2 filesdep ~containersPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: containers

API changes (from Hackage documentation)

+ NLP.Scores: ari :: (Ord a, Ord b) => Counts a b -> Double
+ NLP.Scores: data Counts a b
+ NLP.Scores: entropy :: [Count] -> Double
+ NLP.Scores: instance (Eq a, Eq b) => Eq (P a b)
+ NLP.Scores: instance (Ord a, Ord b) => Ord (P a b)
+ NLP.Scores: mi :: (Ord a, Ord b) => Counts a b -> Double
+ NLP.Scores: type Count = Double
+ NLP.Scores: vi :: (Ord a, Ord b) => Counts a b -> Double

Files

NLP/Scores.hs view
@@ -1,42 +1,53 @@ {-# LANGUAGE BangPatterns #-}+-- | Scoring functions commonly used for evaluation of NLP+-- systems. Most functions in this module work on lists, but some take+-- a precomputed table of 'Counts'. This will give a speedup if you+-- want to compute multiple scores on the same data. For example to+-- compute the Mutual Information, Variation of Information and the+-- Adujusted Rand Index on the same pair of clusterings:+--+-- >>> let cs = counts $ zip "abcabc" "abaaba"+-- >>> mapM_ (print . ($ cs)) [mi, ari, vi]+-- module NLP.Scores      ( -      sum-    , mean-    , accuracy+      -- * Scores for classification and ranking+      accuracy     , recipRank     , avgPrecision+      -- * Scores for clustering+    , ari+    , mi+    , vi+      -- * Auxiliary types and functions+    , Count+    , Counts+    , sum+    , mean     , jaccard+    , entropy     ) where import Data.List hiding (sum) import qualified Data.Set as Set+import qualified Data.Map as Map import Prelude hiding (sum) --- | The sum of a list of numbers (without overflowing stack, --- unlike 'Prelude.sum').-sum :: (Num a) => [a] -> a-sum = foldl' (+) 0---- | The mean of a list of numbers.-mean :: (Fractional n, Real a) => [a] -> n-mean xs = -    let (sum,len) = foldl' (\(!s,!l) x -> (s+x,l+1)) (0,0) xs-    in realToFrac sum/len- -- | Accuracy: the proportion of elements in the first list equal to  -- elements at corresponding positions in second list. Lists should be -- of equal lengths. accuracy :: (Eq a, Fractional n) => [a] -> [a] -> n accuracy xs = mean . map fromEnum . zipWith (==) xs- +{-# SPECIALIZE accuracy :: [Double] -> [Double] -> Double #-}+ -- | Reciprocal rank: the reciprocal of the rank at which the first arguments -- occurs in the list given as the second argument. recipRank :: (Eq a, Fractional n) => a -> [a] -> n recipRank y ys = -    case [ r | (r,y') <- zip [1..] ys , y' == y ] of+    case [ r | (r,y') <- zip [1::Int ..] ys , y' == y ] of       []  -> 0       r:_ -> 1/fromIntegral r+{-# SPECIALIZE recipRank :: Double -> [Double] -> Double #-}  -- | Average precision.  -- <http://en.wikipedia.org/wiki/Information_retrieval#Average_precision>@@ -51,8 +62,69 @@     . takeWhile (\(_,_,cum) -> cum <= Set.size gold)      . snd      . mapAccumL (\z (r,rel) -> (z+rel,(r,rel,z+rel))) 0-    $ [ (r,fromEnum $ x `Set.member` gold) | (x,r) <- zip xs [1..]]+    $ [ (r,fromEnum $ x `Set.member` gold) | (x,r) <- zip xs [1::Int ..]]+{-# SPECIALIZE avgPrecision :: (Ord a) => Set.Set a -> [a] -> Double #-} +-- | Mutual information: MI(X,Y) = H(X) - H(X|Y) = H(Y) - H(Y|X). Also+-- known as information gain.+mi :: (Ord a, Ord b) => Counts a b -> Double+mi (Counts cxy cx cy) =+  let n = Map.foldl' (+) 0 cxy+      cell (P x y) nxy = +        let nx = cx Map.! x+            ny = cy Map.! y+        in  nxy / n * logBase 2 (nxy * n / nx / ny)+  in sum [ cell (P x y) nxy | (P x y, nxy) <- Map.toList cxy ]++-- | Variation of information: VI(X,Y) = H(X) + H(Y) - 2 MI(X,Y)+vi :: (Ord a, Ord b) => Counts a b -> Double+vi cs@(Counts cxy cx cy) = entropy (elems cx) + entropy (elems cy) - 2 * mi cs+  where elems = Map.elems++-- | Adjusted Rand Index: <http://en.wikipedia.org/wiki/Rand_index>+ari :: (Ord a, Ord b) => Counts a b -> Double+ari (Counts cxy cx cy) =  (sum1 - sum2*sum3/choicen2) +                        / (1/2 * (sum2+sum3) - (sum2*sum3) / choicen2)+  where choicen2 = choice (sum . Map.elems $ cx) 2+        sum1 = sum [ choice nij 2 | nij <- Map.elems cxy ]+        sum2 = sum [ choice ni 2 | ni <- Map.elems cx ]+        sum3 = sum [ choice nj 2 | nj <- Map.elems cy ]++-- | A count+type Count = Double+-- | Count table+data Counts a b = +  Counts +  { joint :: !(Map.Map (P a b) Count)   -- ^ Counts of both components+  , marginalFst :: !(Map.Map a Count) -- ^ Counts of the first component+  , marginalSnd :: !(Map.Map b Count) -- ^ Counts of the second component+  }+data P a b = P !a !b deriving (Eq, Ord)++-- | The empty count table+empty :: (Ord a, Ord b) => Counts a b+empty = Counts Map.empty Map.empty Map.empty++-- | The sum of a list of numbers (without overflowing stack, +-- unlike 'Prelude.sum').+sum :: (Num a) => [a] -> a+sum = foldl' (+) 0+{-# SPECIALIZE sum :: [Double] -> Double #-}+{-# SPECIALIZE sum :: [Int] -> Int #-}+{-# INLINE sum #-}++-- | The mean of a list of numbers.+mean :: (Fractional n, Real a) => [a] -> n+mean xs = +    let (P tot len) = foldl' (\(P s l) x -> (P (s+x) (l+1))) (P 0 0) xs+    in realToFrac tot/len+{-# SPECIALIZE mean :: [Double] -> Double #-}++-- | The binomial coefficient: C^n_k = PROD^k_i=1 (n-k-i)/i+choice :: (Enum b, Fractional b) => b -> b -> b+choice n k = foldl' (*) 1 [n-k+1 .. n] / foldl' (*) 1 [1 .. k]+{-# SPECIALIZE choice :: Double -> Double -> Double #-}+ -- | Jaccard coefficient -- J(A,B) = |AB| / |A union B| jaccard :: (Fractional n, Ord a) => Set.Set a -> Set.Set a -> n@@ -60,4 +132,19 @@   fromIntegral (Set.size (Set.intersection a b))   /    fromIntegral (Set.size (Set.union a b))-  +{-# SPECIALIZE jaccard :: (Ord a) => Set.Set a -> Set.Set a -> Double #-}  ++-- | Entropy: H(X) = -SUM_i P(X=i) log_2(P(X=i))+entropy :: [Count] -> Double+entropy cx = negate $ sum [ f nx | nx <- cx ]+    where n    = sum cx+          logn = logBase 2 n+          f nx = nx / n * (logBase 2 nx - logn)++counts :: (Ord a, Ord b) => [(a,b)] -> Counts a b+counts xys = foldl' f empty xys+    where f cs@(Counts cxy cx cy) (!x,!y) = +            cs { joint       = Map.insertWith' (+) (P x y) 1 cxy+               , marginalFst = Map.insertWith' (+) x 1 cx+               , marginalSnd = Map.insertWith' (+) y 1 cy }+            
nlp-scores.cabal view
@@ -7,7 +7,7 @@ -- The package version. See the Haskell package versioning policy -- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for -- standards guiding when and how versions should be incremented.-Version:             0.2.1+Version:             0.2.2  -- A short (one-line) description of the package. Synopsis:            Scoring functions commonly used for evaluation in NLP and IR@@ -51,8 +51,7 @@   Exposed-modules:     NLP.Scores      -- Packages needed in order to build this package.-  Build-depends:  base >= 3 && < 5 ,  containers >= 0.4 -  +  Build-depends:  base >= 3 && < 5 ,  containers >= 0.4.2   -- Modules not exported by this package.   -- Other-modules: