packages feed

nlp-scores 0.4.0 → 0.5.2

raw patch · 3 files changed

+105/−30 lines, 3 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- NLP.Scores: instance (Eq a, Eq b) => Eq (P a b)
- NLP.Scores: instance (Ord a, Ord b) => Ord (P a b)
+ NLP.Scores: countFst :: Ord k => k -> Counts k b -> Count
+ NLP.Scores: countJoint :: (Ord a, Ord b) => a -> b -> Counts a b -> Count
+ NLP.Scores: countSnd :: Ord k => k -> Counts a k -> Count
+ NLP.Scores: fstElems :: Counts k b -> [k]
+ NLP.Scores: histogram :: (Num a, Ord k, Foldable t) => t k -> Map k a
+ NLP.Scores: sndElems :: Counts a k -> [k]
+ NLP.Scores.Internals: Counts :: !Map (P a b) Count -> !Map a Count -> !Map b Count -> Counts a b
+ NLP.Scores.Internals: P :: !a -> !b -> P a b
+ NLP.Scores.Internals: data Counts a b
+ NLP.Scores.Internals: data P a b
+ NLP.Scores.Internals: empty :: (Ord a, Ord b) => Counts a b
+ NLP.Scores.Internals: instance (Eq a, Eq b) => Eq (P a b)
+ NLP.Scores.Internals: instance (Ord a, Ord b) => Monoid (Counts a b)
+ NLP.Scores.Internals: instance (Ord a, Ord b) => Ord (P a b)
+ NLP.Scores.Internals: joint :: Counts a b -> !Map (P a b) Count
+ NLP.Scores.Internals: marginalFst :: Counts a b -> !Map a Count
+ NLP.Scores.Internals: marginalSnd :: Counts a b -> !Map b Count
+ NLP.Scores.Internals: type Count = Double
+ NLP.Scores.Internals: unionPlus :: (Num a, Ord k) => Map k a -> Map k a -> Map k a
- NLP.Scores: accuracy :: (Eq a, Fractional c, Foldable t) => t a -> t a -> c
+ NLP.Scores: accuracy :: (Eq a, Fractional c, Traversable t, Foldable s) => t a -> s a -> c
- NLP.Scores: counts :: (Ord a, Ord b, Foldable t) => t (a, b) -> Counts a b
+ NLP.Scores: counts :: (Ord a, Ord b, Traversable t, Foldable s) => t a -> s b -> Counts a b

Files

NLP/Scores.hs view
@@ -1,4 +1,7 @@-{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE +    BangPatterns +  , NoMonomorphismRestriction+ #-} -- | Scoring functions commonly used for evaluation of NLP -- systems. Most functions in this module work on sequences which are -- instances of 'Data.Foldable', but some take a precomputed table of@@ -7,7 +10,7 @@ -- Information, Variation of Information and the Adjusted Rand Index -- on the same pair of clusterings: ----- >>> let cs = counts $ zip "abcabc" "abaaba"+-- >>> let cs = counts "abcabc" "abaaba" -- >>> mapM_ (print . ($ cs)) [mi, ari, vi] -- >>> 0.9182958340544894 -- >>> 0.4444444444444445@@ -15,15 +18,15 @@  module NLP.Scores      ( -      -- * Scores for classification and ranking+    -- * Scores for classification and ranking       accuracy     , recipRank     , avgPrecision-      -- * Scores for clustering+    -- * Scores for clustering     , ari     , mi     , vi-      -- * Auxiliary types and functions+    -- * Auxiliary types and functions     , Count     , Counts     , counts@@ -31,20 +34,31 @@     , mean     , jaccard     , entropy+    , histogram+    -- * Extracting joint and marginal counts from 'Counts'+    , countJoint+    , countFst+    , countSnd+    -- * Extracting lists of values from 'Counts'+    , fstElems+    , sndElems     ) where import qualified Data.Foldable as F+import qualified Data.Traversable as T import Data.Monoid import Data.List hiding (sum) import qualified Data.Set as Set import qualified Data.Map as Map import Prelude hiding (sum) +import NLP.Scores.Internals+ -- | Accuracy: the proportion of elements in the first sequence equal -- to elements at corresponding positions in second -- sequence. Sequences should be of equal lengths.-accuracy :: (Eq a, Fractional c, F.Foldable t) =>  t a -> t a -> c-accuracy xs = mean . map fromEnum . zipWith (==) (F.toList xs) . F.toList+accuracy :: (Eq a, Fractional c, T.Traversable t, F.Foldable s) =>  t a -> s a -> c+accuracy xs = mean . fmap fromEnum . zipWithTF (==) xs . F.toList {-# SPECIALIZE accuracy :: [Double] -> [Double] -> Double #-}  -- | Reciprocal rank: the reciprocal of the rank at which the first arguments@@ -86,7 +100,7 @@  -- | 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+vi cs@(Counts _ 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>@@ -98,21 +112,6 @@         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 sequence of numbers sum :: (F.Foldable t, Num a) => t a -> a sum = F.foldl' (+) 0@@ -141,17 +140,54 @@   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: H(X) = -SUM_i P(X=i) log_2(P(X=i)). @entropy xs@ is the+-- entropy of the random variable represented by the sequence @xs@,+-- where each element of @xs@ is the count of the one particular +-- value the random variable can take. If you need to compute the +-- entropy from a sequence of outcomes, the following will work:+--+-- > entropy . elems . histogram+-- entropy :: (Floating c, F.Foldable t) => t c -> c entropy cx = negate . getSum . F.foldMap  (Sum . f)  $ cx     where n    = sum cx           logn = logBase 2 n           f nx = nx / n * (logBase 2 nx - logn) +-- | @histogram xs@ is returns the map of the frequency counts of the+-- elements in sequence @xs@+histogram :: (Num a, Ord k, F.Foldable t) => t k -> Map.Map k a+histogram = F.foldl' (\ z k -> Map.insertWith' (+) k 1 z) Map.empty+ -- | Creates count table 'Counts'-counts :: (Ord a, Ord b, F.Foldable t) => t (a, b) -> Counts a b-counts xys = F.foldl' f empty xys-    where f cs@(Counts cxy cx cy) (!x,!y) = -            cs { joint       = Map.insertWith' (+) (P x y) 1 cxy+counts :: (Ord a, Ord b, T.Traversable t, F.Foldable s) => t a -> s b -> Counts a b+counts xs = F.foldl' f empty . zipWithTF P xs . F.toList+    where f cs@(Counts cxy cx cy) p@(P x y) = +            cs { joint       = Map.insertWith' (+) p 1 cxy                , marginalFst = Map.insertWith' (+) x 1 cx                , marginalSnd = Map.insertWith' (+) y 1 cy }++-- | Joint count+countJoint :: (Ord a, Ord b) => a -> b -> Counts a b -> Count          +countJoint x y = Map.findWithDefault 0 (P x y) . joint+-- | Count of first element+countFst :: Ord k => k -> Counts k b -> Count+countFst x = Map.findWithDefault 0 x . marginalFst+-- | Count of second element+countSnd :: Ord k => k -> Counts a k -> Count+countSnd y = Map.findWithDefault 0 y . marginalSnd++-- | List of values of first element+fstElems :: Counts k b -> [k]+fstElems = Map.keys . marginalFst+-- | List of values of second element+sndElems :: Counts a k -> [k]+sndElems = Map.keys . marginalSnd++-- | @zipWithTF h t f@ zips the values from the traversable @t@ with+-- the values from the foldable @f@ using the function @h@.+zipWithTF :: (T.Traversable t, F.Foldable f) =>+             (a -> b -> c) -> t a -> f b -> t c+zipWithTF h t f = snd . T.mapAccumL map_one (F.toList f) $ t+  where map_one (x:xs) y = (xs, h y x)+        
+ NLP/Scores/Internals.hs view
@@ -0,0 +1,39 @@+module NLP.Scores.Internals+    ( Counts(..)+    , Count+    , P(..)+    , empty+    , unionPlus+    )+where+import qualified Data.Map as Map+import Data.Monoid++-- | 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++instance (Ord a, Ord b) => Monoid (Counts a b) where+    mempty = empty+    c `mappend` k = +        Counts { joint = unionPlus (joint c) (joint k)+               , marginalFst = unionPlus (marginalFst c) (marginalFst k)+               , marginalSnd = unionPlus (marginalSnd c) (marginalSnd k)+               }++unionPlus :: (Num a, Ord k) => Map.Map k a -> Map.Map k a -> Map.Map k a+unionPlus m = +    Map.foldlWithKey' (\z k v -> Map.insertWith' (+) k v z) m+{-# SPECIALIZE unionPlus :: (Ord k) => +  Map.Map k Count -> Map.Map k Count -> Map.Map k Count #-}
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.4.0+Version:             0.5.2  -- A short (one-line) description of the package. Synopsis:            Scoring functions commonly used for evaluation in NLP and IR@@ -48,7 +48,7 @@  Library   -- Modules exported by the library.-  Exposed-modules:     NLP.Scores+  Exposed-modules:     NLP.Scores, NLP.Scores.Internals      -- Packages needed in order to build this package.   Build-depends:  base >= 3 && < 5 ,  containers >= 0.4.2