packages feed

nerf 0.5.0 → 0.5.1

raw patch · 4 files changed

+121/−87 lines, 4 filesdep +IntervalMapdep ~cmdargsdep ~directoryPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: IntervalMap

Dependency ranges changed: cmdargs, directory

API changes (from Hackage documentation)

- NLP.Nerf.Tokenize: moveNEs :: (Word b, Word c) => NeForest a b -> [c] -> NeForest a c
+ NLP.Nerf.Tokenize: sync :: (Word b, Word c) => NeForest a b -> [c] -> NeForest a c

Files

nerf.cabal view
@@ -1,5 +1,5 @@ name:               nerf-version:            0.5.0+version:            0.5.1 synopsis:           Nerf, the named entity recognition tool based on linear-chain CRFs description:     The package provides the named entity recognition (NER) tool divided into a@@ -47,7 +47,8 @@       , tokenize            == 0.1.3       , mtl                 >= 2.1      && < 2.2       , network             >= 2.3      && < 2.4-      , cmdargs+      , cmdargs             >= 0.10     && < 0.11+      , IntervalMap         >= 0.3      && < 0.4      exposed-modules:         NLP.Nerf@@ -74,7 +75,7 @@   hs-source-dirs: src, tools   build-depends:     filepath            >= 1.3      && < 1.4,-    directory           >= 1.2      && < 1.3,+    directory           >= 1.1      && < 1.3,     temporary           >= 1.1      && < 1.2   main-is: nerf.hs   ghc-options: -Wall -O2 -threaded -rtsopts
src/NLP/Nerf.hs view
@@ -26,7 +26,7 @@ import qualified Data.CRF.Chain1 as CRF  import NLP.Nerf.Types-import NLP.Nerf.Tokenize (tokenize, moveNEs)+import NLP.Nerf.Tokenize (tokenize, sync) import NLP.Nerf.Schema (SchemaConf, Schema, fromConf, schematize)  -- | A Nerf consists of the observation schema configuration and the CRF model.@@ -50,7 +50,7 @@ -- | Tokenize sentence with the Nerf tokenizer. reTokenize :: N.NeForest NE Word -> N.NeForest NE Word reTokenize ft = -    moveNEs ft ((doTok . leaves) ft)+    sync ft ((doTok . leaves) ft)   where      doTok  = map T.pack . tokenize . intercalate " "  . map T.unpack     leaves = concatMap $ foldMap (either (const []) (:[]))
src/NLP/Nerf/Tokenize.hs view
@@ -1,20 +1,23 @@ {-# LANGUAGE TypeSynonymInstances #-} {-# LANGUAGE FlexibleInstances #-} + -- | The module implements the tokenization used within Nerf -- and some other tokenization-related stuff. + module NLP.Nerf.Tokenize ( -- * Tokenization   tokenize -- * Synchronization , Word (..)-, moveNEs+, sync ) where -import Control.Monad ((>=>))-import Data.Foldable (foldMap)++import           Control.Arrow (second)+import           Control.Monad ((>=>)) import qualified Data.Char as Char import qualified Data.List as L import qualified Data.Tree as T@@ -22,13 +25,16 @@ import qualified Data.Text as Text import qualified Data.Text.Lazy as LazyText import qualified NLP.Tokenize as Tok+import qualified Data.IntervalMap.Strict as I -import Data.Named.Tree (NeForest, NeTree, groupForestLeaves)+import           Data.Named.Tree (NeForest, NeTree) ------------------------------- Tokenization definition.---------------------------- +-------------------------------------+-- Tokenization definition+-------------------------------------++ -- | Default tokenizator. defaultTokenizer :: Tok.Tokenizer defaultTokenizer@@ -36,115 +42,142 @@     >=> Tok.uris     >=> Tok.punctuation + -- | Tokenize sentence using the default tokenizer. tokenize :: String -> [String] tokenize = Tok.run defaultTokenizer ------------------------------------------------------------------- Synchronizing named entities with new sentence tokenization.---------------------------------------------------------------- +-------------------------------------+-- Word+-------------------------------------++ -- | A class of objects which can be converted to `String`. class Word a where     word :: a -> String + instance Word String where     word = id + instance Word Text.Text where     word = Text.unpack + instance Word LazyText.Text where     word = LazyText.unpack + essence :: Word a => a -> Int essence = length . filter (not . Char.isSpace) . word {-# INLINE essence #-} --- | Syncronization between two sentences.  Each (xs, ys) pair represents--- tokens from the two input sentences which corresponds to each other.-type Sync a b = [([a], [b])] --- | Synchronize two tokenizations of the sentence.-sync :: (Word a, Word b) => [a] -> [b] -> Sync a b-sync = sync' 0+-------------------------------------+-- Grouping leaves+------------------------------------- -sync' :: (Word a, Word b) => Int -> [a] -> [b] -> Sync a b-sync' r (x:xs) (y:ys)-    | n + r == m    = ([x], [y])    : sync' 0       xs    ys-    | n + r  < m    = join x        $ sync' (n + r) xs (y:ys)-    | otherwise     = swap . join y $ sync' (m - r) ys (x:xs)-  where-    n = essence x-    m = essence y-    join l ((ls, rs) : ps)  = (l:ls, rs) : ps-    join _ []               = error "sync'.join: bad arguments"-    swap ((ls, rs) : ps)    = (rs, ls) : swap ps-    swap []                 = []-sync' 0 [] [] = []-sync' _ _  _  = error "sync': bad arguments" --- | Match the `Sync` with the given list, return the matching result--- (snd elements of the `Sync` list) and the rest of the `Sync` list.-match :: (Word a, Word b) => [a] -> Sync a b -> ([b], Sync a b)-match xs ss =-    let (sl, sr) = splitAcc isMatch 0 ss-    in  (concatMap snd sl, sr)-  where-    n = sum (map essence xs)-    isMatch r (ys, _)-        | m + r < n     = (m + r, False)-        | m + r == n    = (m + r, True)-        | otherwise     = error "match.isMatch: no match"-      where-        m = sum (map essence ys)---- | Split the list with the help of the accumulating function.-splitAcc :: (acc -> a -> (acc, Bool)) -> acc -> [a] -> ([a], [a])-splitAcc _ _ [] = ([], [])-splitAcc f acc (x:xs)-    | cond      = ([x], xs)-    | otherwise = join x (splitAcc f acc' xs)-  where-    (acc', cond) = f acc x-    join y (ys, zs) = (y:ys, zs)---- | List forest leaves.-leaves :: NeForest a b -> [b]-leaves = concatMap $ foldMap (either (const []) (:[]))- unGroupLeaves :: NeForest a [b] -> NeForest a b unGroupLeaves = concatMap unGroupLeavesT + unGroupLeavesT :: NeTree a [b] -> [NeTree a b] unGroupLeavesT (T.Node (Left v) xs)     =     [T.Node (Left v) (unGroupLeaves xs)] unGroupLeavesT (T.Node (Right vs) _)   =     [T.Node (Right v) [] | v <- vs] -substGroups :: (Word b, Word c) => NeForest a [b] -> Sync b c -> NeForest a [c]-substGroups fs ss = snd $ L.mapAccumL substGroupsT ss fs -substGroupsT-    :: (Word b, Word c)-    => Sync b c -> NeTree a [b]-    -> (Sync b c, NeTree a [c])-substGroupsT =-    Tr.mapAccumL f+---------------------------------------------------------------+-- Identifying ranges+---------------------------------------------------------------+++type Range = I.Interval Int+++-- | Range computation step.+ranged :: Word a => Int -> a -> (Int, (Range, a))+ranged p w =+    (q, (i, w))   where-    f s (Left v)  = (s, Left v)-    f s (Right v) =-        let (v', s') = match v s-        in  (s', Right v')+    q = p + essence w+    i = I.IntervalCO p q --- | Synchronize named entities with tokenization represented--- by the second function argument.  Of course, both arguments--- should relate to the same sentence.-moveNEs :: (Word b, Word c) => NeForest a b -> [c] -> NeForest a c-moveNEs ft ys-    = unGroupLeaves-    $ substGroups-        (groupForestLeaves true ft)-        (sync (leaves ft) ys)++-- | Compute ranges of individual tokens.+rangedList :: Word a => [a] -> [(Range, a)]+rangedList = snd . L.mapAccumL ranged 0+++-- | Compute ranges of individual tokens.+rangedForest :: Word b => NeForest a b -> NeForest a (Range, b)+rangedForest = +    snd . L.mapAccumL (Tr.mapAccumL f) 0   where-    true _ _ = True+    f acc (Left x)  = (acc, Left x)+    f acc (Right x) =+        let (acc', y) = ranged acc x+        in  (acc', Right y)+        ++---------------------------------------------------------------+-- Synchronizing named entities with new sentence tokenization+---------------------------------------------------------------+++-- | Replace leaves in the NE forest with corresponding tokens.+replaceToks+    :: I.IntervalMap Int c+    -> NeForest a (Range, b)+    -> ( I.IntervalMap Int c+       , NeForest a (Range, c) )+replaceToks ivMap nes+    = second unGroupLeaves+    $ L.mapAccumL (Tr.mapAccumL replace) ivMap nes+  where+    replace im (Left x) = (im, Left x)+    replace im (Right (ran, _)) =+        let rsXs = I.intersecting im ran+            im'  = L.foldl' (flip I.delete) im (map fst rsXs)+        in  (im', Right rsXs)+++-- | Lift the first range of a tree to the top.+liftRange :: NeTree a (Range, b) -> (Range, NeTree a b)+liftRange (T.Node (Left v) xs) =+    (ran, T.Node (Left v) (map snd ys))+  where+    ys = map liftRange xs+    ran = maybeHead $ map fst ys+    maybeHead (x:_) = x+    maybeHead []    = error "liftRange: invalid NE tree"+liftRange (T.Node (Right (ran, v)) _) = (ran, T.Node (Right v) [])+++-- | Synchronize the list of NE trees with the new tokenization.+sync+    :: (Word b, Word c)+    => NeForest a b     -- ^ NE forest+    -> [c]              -- ^ New tokenization+    -> NeForest a c     -- ^ Resulting NE forest+sync nes0 xs0+    = map snd . I.toList . I.fromList+    $ map (second mkLeaf) (I.toList ivMap')+    ++ map liftRange nes'+  where+    -- Interval map of the new tokenization+    ivMap = I.fromList $ rangedList xs0+    -- NE non-leaf trees with ranges+    nes = filter internal $ rangedForest nes0+    -- Replace tokens...+    (ivMap', nes') = replaceToks ivMap nes+    -- Is it an internal node?+    internal x = case T.rootLabel x of+        Left _  -> True+        Right _ -> False+    -- Make a leaf tree+    mkLeaf x = T.Node (Right x) []
src/NLP/Nerf/XCES.hs view
@@ -340,7 +340,7 @@ -- nerSent :: Nerf.Nerf -> Sent [] -> Sent Ann nerSent :: (String -> NeForest NE Word) -> Sent [] -> Sent Ann nerSent nerFun s@Sent{..} = s-    { sentCon = Ann $ Tok.moveNEs+    { sentCon = Ann $ Tok.sync         (nerFun $ restoreOrigSent sentCon)         sentCon }