packages feed

data-named (empty) → 0.1.0

raw patch · 6 files changed

+403/−0 lines, 6 filesdep +attoparsecdep +basedep +containerssetup-changed

Dependencies added: attoparsec, base, containers, text

Files

+ Data/Named/Graph.hs view
@@ -0,0 +1,99 @@+-- | Implementation of a graph with each node identified by a unique key.+-- It is a provisional module and it might be replaced by the standard+-- graph from containers package in the future.++module Data.Named.Graph+( Graph (..)+, mkGraph+, node+, edges+, roots+, toTree+, toKeyTree+, toForestWith+, toForest+) where++import qualified Data.Set as S+import qualified Data.Map as M+import qualified Data.Tree as T+import Data.List (mapAccumL, foldl', sortBy)+import Data.Maybe (mapMaybe, fromJust)+import Data.Ord (comparing)++-- | A graph.+data Graph k v = Graph+    { nodeMap :: M.Map k v+    , edgeMap :: M.Map k [k] }++-- | Make a graph from a list of (key, value, [children keys]) tuples.+mkGraph :: Ord k => [(k, v, [k])] -> Graph k v+mkGraph xs = +    Graph ns es+  where+    ns = M.fromList [(k, v)  | (k, v, _)  <- xs]+    es = M.fromList [(k, ks) | (k, _, ks) <- xs]++-- | Get node with the given key.+node :: (Show k, Ord k) => Graph k v -> k -> v+node g k = case M.lookup k (nodeMap g) of+    Nothing -> error $ "node: key " ++ show k ++ " not in the nodes map"+    Just v  -> v+{-# INLINE node #-}++-- | Get keys of adjacent nodes for the given node key.+edges :: (Show k, Ord k) => Graph k v -> k -> [k]+edges g k = case M.lookup k (edgeMap g) of+    Nothing -> error $ "edges: key " ++ show k ++ " not in the edges map"+    Just v  -> v+{-# INLINE edges #-}++-- | Return all graph roots (i.e. nodes with no parents).+roots :: Ord k => Graph k v -> [k]+roots g =+    [ k+    | (k, _) <- M.assocs (nodeMap g)+    , not (k `S.member` desc) ]+  where+    desc = S.fromList . concat . M.elems $ edgeMap g++-- | Make a tree rooted in the node with respect to the graph.+toTree :: (Show k, Ord k) => Graph k v -> k -> T.Tree v+toTree g = fmap (node g) . toKeyTree g++-- | Make a key tree rooted in the node with respect to the graph.+toKeyTree :: (Show k, Ord k) => Graph k v -> k -> T.Tree k+toKeyTree g k = T.Node k+    [ toKeyTree g k'+    | k' <- edges g k ]++-- | Transform graph into a forest given the priority function.+-- That is, trees with higher priorities will be taken first,+-- while those with lower priorities might be trimmed down+-- (since we don't want to have nodes with multiple parents in+-- the resulting forest).+toForestWith :: (Show k, Ord k, Ord a)+             => (T.Tree v -> a) -> Graph k v -> T.Forest v+toForestWith pr g = map valTr . snd $+    mapAccumL trim S.empty sortedTrees+  where+    valTr = fmap (node g) -- Make value tree from a key tree+    trees = map (toKeyTree g) (roots g)+    sortedTrees =+        let f = pr . valTr+        in  sortBy (comparing f) trees++-- | Transform graph into a forest. It removes duplicate+-- nodes from trees chosing trees in an arbitrary order.+toForest :: (Show k, Ord k) => Graph k v -> T.Forest v+toForest = toForestWith $ const (0 :: Int)++trim :: Ord k => S.Set k -> T.Tree k -> (S.Set k, T.Tree k)+trim visited tree =+    (visited', tree')+  where+    tree'    = fromJust (doIt tree)+    visited' = foldl' (flip S.insert) visited (T.flatten tree')+    doIt (T.Node x ts)+        | x `S.member` visited = Nothing+        | otherwise = Just $ T.Node x (mapMaybe doIt ts)
+ Data/Named/IOB.hs view
@@ -0,0 +1,127 @@+{- |+    IOB encoding method extended to forests.++    Example:++>>> :m Data.Tree Data.Text Text.Named.Enamex Data.Named.IOB+>>> let enamex = pack "<x>w1.1\\ w1.2</x> w2 <y><z>w3</z> w4</y>"+>>> let parseIt = fmap (mapTwo id id . fmap unpack) . parseForest++>>> putStr . drawForest . fmap (fmap show) . parseIt $ enamex+Left "x"+|+`- Right "w1.1 w1.2"+,+Right "w2"+,+Left "y"+|++- Left "z"+|  |+|  `- Right "w3"+|+`- Right "w4"++>>> mapM_ print . encodeForest . parseIt $ enamex+IOB {word = "w1.1 w1.2", label = [B "x"]}+IOB {word = "w2", label = []}+IOB {word = "w3", label = [B "y",B "z"]}+IOB {word = "w4", label = [I "y"]}+-}++module Data.Named.IOB+( IOB (..)+, Label+, Atom (..)+, encodeForest+, decodeForest+) where++import Control.Applicative ((<$>))+import Data.Maybe (fromJust)+import Data.Tree++-- | An 'IOB' data structure consists of a word with a corresponding+-- compound label.+data IOB w a = IOB+    { word  :: w+    , label :: Label a+    } deriving (Show)++-- | A 'Label' consists of a list of atomic 'Atom' labels.+type Label a = [Atom a]++-- | An 'Atom' is the atomic label with additional marker.+data Atom a  = B a      -- ^ Beginning marker+             | I a      -- ^ Inside marker +             deriving (Show, Eq, Ord)++push :: Atom a -> IOB w a -> IOB w a+push x (IOB w xs) = IOB w (x:xs)++popMaybe :: IOB w a -> Maybe (Atom a, IOB w a)+popMaybe (IOB w (x:xs)) = Just (x, IOB w xs)+popMaybe (IOB _ [])     = Nothing++topMaybe :: IOB w a -> Maybe (Atom a)+topMaybe iob = fst <$> popMaybe iob++pop :: IOB w a -> (Atom a, IOB w a)+pop = fromJust . popMaybe++-- top :: IOB w a -> Atom a+-- top = fromJust . topMaybe++raw :: Atom a -> a+raw (B x) = x+raw (I x) = x++-- isB :: Atom a -> Bool+-- isB (B _) = True+-- isB _     = False++isI :: Atom a -> Bool+isI (I _) = True+isI _     = False++-- | Encode the forest with the IOB method.+encodeForest :: Forest (Either a w) -> [IOB w a]+encodeForest [] = []+encodeForest (x:xs) = encodeTree x ++ encodeForest xs++-- | Encode the tree using the IOB method.+encodeTree :: Tree (Either a w) -> [IOB w a]++encodeTree (Node (Left _) []) =+    error "encodeTree: label node with no children"+encodeTree (Node (Left e) forest) =+    let addLayer (x:xs) = push (B e) x : map (push $ I e) xs+        addLayer []     = []+    in  addLayer (encodeForest forest)++encodeTree (Node (Right _) (_:_)) =+    error "encodeTree: word node with children"+encodeTree (Node (Right w) _) = [IOB w []]++-- | Decode the forest using the IOB method.+decodeForest :: Eq a => [IOB w a] -> Forest (Either a w)+decodeForest [] = []+decodeForest xs =+    tree : decodeForest xs'+  where+    (chunk, xs') = followTop xs+    tree = case topMaybe $ head chunk of+        Nothing -> Node (Right . word $ head chunk) []+        Just e  -> Node (Left $ raw e) (decodeForest $ map rmTop chunk)+    rmTop = snd . pop++-- | Take iob elements as long as the top label doesn't change.  +-- Return obtained part together with the rest of iob.+followTop :: Eq a => [IOB w a] -> ([IOB w a], [IOB w a])+followTop [] = error "followTop: empty iob"+followTop (x:xs) =+    (x:chunk, rest)+  where+    (chunk, rest) = span (cond (topMaybe x) . topMaybe) xs+    cond (Just a) (Just b) = raw a == raw b && isI b+    cond _ _ = False
+ LICENSE view
@@ -0,0 +1,26 @@+Copyright (c) 2012, IPI PAN+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.lhs view
@@ -0,0 +1,4 @@+#! /usr/bin/env runhaskell++> import Distribution.Simple+> main = defaultMain
+ Text/Named/Enamex.hs view
@@ -0,0 +1,102 @@+{-# LANGUAGE OverloadedStrings #-}++{- |+    Parsing text in the Enamex data format.  Each node is enclosed between+    opening and closing tags with tag name representing the label and contents+    representing children of the node.  Both leaf and label values should be+    escaped by prepending the '\' character before special ' ' (space), '>',+    '<' and '\' characters.++    Example:++>>> :m Data.Tree Data.Text Text.Named.Enamex+>>> let drawIt = putStr . drawForest . fmap (fmap unpack) . parseForest+>>> drawIt $ pack "<x>w1.1\\ w1.2</x> <y><z>w2</z> w3</y>"+x+|+`- w1.1 w1.2+,+y+|++- z+|  |+|  `- w2+|+`- w3+-}++module Text.Named.Enamex+( parseForest+, parseEnamex+, mapTwo+) where++import Control.Applicative+import Control.Monad+import Data.Attoparsec.Text+import qualified Data.Text as T+import qualified Data.Text.Lazy as L+import qualified Data.Tree as Tree+++type Tree = Tree.Tree T.Text+type Forest = Tree.Forest T.Text++-- | Map the first function over internal nodes+-- and the second one over leaves.+mapTwo :: (a -> b) -> (a -> c) -> Tree.Tree a -> Tree.Tree (Either b c)+mapTwo _ g (Tree.Node x [])   = Tree.Node (Right $ g x) []+mapTwo f g (Tree.Node x kids) = Tree.Node (Left $ f x) (map (mapTwo f g) kids)++pForest :: Parser Forest+pForest = pTree `sepBy` (space *> skipSpace)++pTree :: Parser Tree+pTree = pNode+    <|> pLeaf++pLeaf :: Parser Tree+pLeaf = Tree.Node <$> pWord <*> pure []++pNode :: Parser Tree+pNode = do+    x    <- pOpenTag+    kids <- pForest+    x'   <- pCloseTag+    when (x /= x') (fail "Tag start/end mismatch")+    return $ Tree.Node x kids++pOpenTag :: Parser T.Text+pOpenTag = "<" .*> pWord <*. ">"++pCloseTag :: Parser T.Text+pCloseTag = "</" .*> pWord <*. ">"++pWord :: Parser T.Text+pWord =+    unEscape <$> scan False special+  where+    special False c =+      case c == ' ' || c == '<' || c == '>' of+        True    -> Nothing+        False   -> if c == '\\'+            then Just True+            else Just False+    special True _  = Just False++-- | TODO: Use lazy text builder to avoid slowness in the pessimistic case.+unEscape :: T.Text -> T.Text+unEscape xs = x `T.append` case drop1 rest of+    Just (y, ys) -> y `T.cons` unEscape ys+    Nothing      -> ""+  where+    drop1 = T.uncons <=< return . snd <=< T.uncons+    (x, rest) = T.breakOn "\\" xs ++-- | Parse the enamex forest.+parseForest :: T.Text -> Forest+parseForest = either error id . parseOnly (pForest <* endOfInput)++-- | Parse the enamex file.+parseEnamex :: L.Text -> [Forest]+parseEnamex = map (parseForest . L.toStrict) . L.lines
+ data-named.cabal view
@@ -0,0 +1,45 @@+name:               data-named+version:            0.1.0+synopsis:           Data types for named entities+description:+    The library provides data types which can be used to represent+    forest structures with labels stored in internal nodes and+    words kept in leaves.  In particular, those types are well suited+    for representing the layer of named entities (NEs).+    .+    The IOB method is implemented in the Data.Named.IOB module and can+    be used to translate between a forest of entities and a sequence+    of compound IOB labels.  This method can be used together with a+    sequence classifier to indirectly model forest structures.+    .+    The Data.Named.Graph module can be used to represent more general,+    graph structures of entities.  The module provides also a lossy+    conversion from a DAG to a forest of entities.+license:            BSD3+license-file:       LICENSE+cabal-version:      >= 1.6+copyright:          Copyright (c) 2012 IPI PAN+author:             Jakub Waszczuk+maintainer:         waszczuk.kuba@gmail.com+stability:          experimental+category:           Natural Language Processing+homepage:           https://github.com/kawu/data-named+build-type:         Simple++library+    build-depends:+        base >= 4 && < 5+      , containers+      , text+      , attoparsec++    exposed-modules:+        Data.Named.Graph+      , Data.Named.IOB+      , Text.Named.Enamex++    ghc-options: -Wall++source-repository head+    type: git+    location: git://github.com/kawu/data-named.git