packages feed

algebraic-graphs-io 0.2 → 0.3

raw patch · 8 files changed

+360/−159 lines, 8 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Algebra.Graph.IO.Datasets.LINQS.Citeseer: CRow :: String -> Seq Int16 -> DocClass -> ContentRow
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: [crClass] :: ContentRow -> DocClass
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: [crFeatures] :: ContentRow -> Seq Int16
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: [crId] :: ContentRow -> String
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: citeseerGraph :: FilePath -> IO (Graph ContentRow)
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: citeseerGraphEdges :: (MonadResource m, MonadThrow m) => FilePath -> Map String (Seq Int16, DocClass) -> ConduitT i (Maybe (Graph ContentRow)) m ()
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: data ContentRow
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: data DocClass
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance Data.Binary.Class.Binary Algebra.Graph.IO.Datasets.LINQS.Citeseer.ContentRow
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance Data.Binary.Class.Binary Algebra.Graph.IO.Datasets.LINQS.Citeseer.DocClass
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance Data.Binary.Class.Binary a => Data.Binary.Class.Binary (Algebra.Graph.IO.Datasets.LINQS.Citeseer.CitesRow a)
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance GHC.Classes.Eq Algebra.Graph.IO.Datasets.LINQS.Citeseer.ContentRow
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance GHC.Classes.Eq Algebra.Graph.IO.Datasets.LINQS.Citeseer.DocClass
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance GHC.Classes.Eq a => GHC.Classes.Eq (Algebra.Graph.IO.Datasets.LINQS.Citeseer.CitesRow a)
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance GHC.Classes.Ord Algebra.Graph.IO.Datasets.LINQS.Citeseer.ContentRow
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance GHC.Classes.Ord Algebra.Graph.IO.Datasets.LINQS.Citeseer.DocClass
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance GHC.Enum.Enum Algebra.Graph.IO.Datasets.LINQS.Citeseer.DocClass
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance GHC.Generics.Generic (Algebra.Graph.IO.Datasets.LINQS.Citeseer.CitesRow a)
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance GHC.Generics.Generic Algebra.Graph.IO.Datasets.LINQS.Citeseer.ContentRow
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance GHC.Generics.Generic Algebra.Graph.IO.Datasets.LINQS.Citeseer.DocClass
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance GHC.Show.Show Algebra.Graph.IO.Datasets.LINQS.Citeseer.ContentRow
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance GHC.Show.Show Algebra.Graph.IO.Datasets.LINQS.Citeseer.DocClass
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance GHC.Show.Show a => GHC.Show.Show (Algebra.Graph.IO.Datasets.LINQS.Citeseer.CitesRow a)
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: restoreContent :: FilePath -> IO (Map String (Seq Int16, DocClass))
+ Algebra.Graph.IO.Datasets.LINQS: CRow :: String -> Seq Int16 -> c -> ContentRow c
+ Algebra.Graph.IO.Datasets.LINQS: CitesRow :: a -> a -> CitesRow a
+ Algebra.Graph.IO.Datasets.LINQS: [cirFrom] :: CitesRow a -> a
+ Algebra.Graph.IO.Datasets.LINQS: [cirTo] :: CitesRow a -> a
+ Algebra.Graph.IO.Datasets.LINQS: [crClass] :: ContentRow c -> c
+ Algebra.Graph.IO.Datasets.LINQS: [crFeatures] :: ContentRow c -> Seq Int16
+ Algebra.Graph.IO.Datasets.LINQS: [crId] :: ContentRow c -> String
+ Algebra.Graph.IO.Datasets.LINQS: data CitesRow a
+ Algebra.Graph.IO.Datasets.LINQS: data ContentRow c
+ Algebra.Graph.IO.Datasets.LINQS: instance Data.Binary.Class.Binary a => Data.Binary.Class.Binary (Algebra.Graph.IO.Datasets.LINQS.CitesRow a)
+ Algebra.Graph.IO.Datasets.LINQS: instance Data.Binary.Class.Binary c => Data.Binary.Class.Binary (Algebra.Graph.IO.Datasets.LINQS.ContentRow c)
+ Algebra.Graph.IO.Datasets.LINQS: instance GHC.Classes.Eq a => GHC.Classes.Eq (Algebra.Graph.IO.Datasets.LINQS.CitesRow a)
+ Algebra.Graph.IO.Datasets.LINQS: instance GHC.Classes.Eq c => GHC.Classes.Eq (Algebra.Graph.IO.Datasets.LINQS.ContentRow c)
+ Algebra.Graph.IO.Datasets.LINQS: instance GHC.Classes.Ord c => GHC.Classes.Ord (Algebra.Graph.IO.Datasets.LINQS.ContentRow c)
+ Algebra.Graph.IO.Datasets.LINQS: instance GHC.Generics.Generic (Algebra.Graph.IO.Datasets.LINQS.CitesRow a)
+ Algebra.Graph.IO.Datasets.LINQS: instance GHC.Generics.Generic (Algebra.Graph.IO.Datasets.LINQS.ContentRow c)
+ Algebra.Graph.IO.Datasets.LINQS: instance GHC.Show.Show a => GHC.Show.Show (Algebra.Graph.IO.Datasets.LINQS.CitesRow a)
+ Algebra.Graph.IO.Datasets.LINQS: instance GHC.Show.Show c => GHC.Show.Show (Algebra.Graph.IO.Datasets.LINQS.ContentRow c)
+ Algebra.Graph.IO.Datasets.LINQS: loadGraph :: Binary c => FilePath -> IO (Graph (ContentRow c))
+ Algebra.Graph.IO.Datasets.LINQS: restoreContent :: Binary c => FilePath -> IO (Map String (Seq Int16, c))
+ Algebra.Graph.IO.Datasets.LINQS: sourceGraphEdges :: (MonadResource m, MonadThrow m) => FilePath -> Map String (Seq Int16, c) -> ConduitT i (Maybe (Graph (ContentRow c))) m ()
+ Algebra.Graph.IO.Datasets.LINQS: stash :: Binary c => FilePath -> String -> Int -> Parser c -> IO ()
+ Algebra.Graph.IO.Datasets.LINQS.Citeseer: data CiteSeerDoc
+ Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance Data.Binary.Class.Binary Algebra.Graph.IO.Datasets.LINQS.Citeseer.CiteSeerDoc
+ Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance GHC.Classes.Eq Algebra.Graph.IO.Datasets.LINQS.Citeseer.CiteSeerDoc
+ Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance GHC.Classes.Ord Algebra.Graph.IO.Datasets.LINQS.Citeseer.CiteSeerDoc
+ Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance GHC.Enum.Enum Algebra.Graph.IO.Datasets.LINQS.Citeseer.CiteSeerDoc
+ Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance GHC.Generics.Generic Algebra.Graph.IO.Datasets.LINQS.Citeseer.CiteSeerDoc
+ Algebra.Graph.IO.Datasets.LINQS.Citeseer: instance GHC.Show.Show Algebra.Graph.IO.Datasets.LINQS.Citeseer.CiteSeerDoc
+ Algebra.Graph.IO.Datasets.LINQS.Citeseer: loadCiteseerGraph :: FilePath -> IO (Graph (ContentRow CiteSeerDoc))
+ Algebra.Graph.IO.Datasets.LINQS.Citeseer: sourceCiteseerGraphEdges :: (MonadResource m, MonadThrow m) => FilePath -> Map String (Seq Int16, CiteSeerDoc) -> ConduitT i (Maybe (Graph (ContentRow CiteSeerDoc))) m ()
+ Algebra.Graph.IO.Datasets.LINQS.Cora: CB :: CoraDoc
+ Algebra.Graph.IO.Datasets.LINQS.Cora: GA :: CoraDoc
+ Algebra.Graph.IO.Datasets.LINQS.Cora: NN :: CoraDoc
+ Algebra.Graph.IO.Datasets.LINQS.Cora: PM :: CoraDoc
+ Algebra.Graph.IO.Datasets.LINQS.Cora: RL :: CoraDoc
+ Algebra.Graph.IO.Datasets.LINQS.Cora: RuL :: CoraDoc
+ Algebra.Graph.IO.Datasets.LINQS.Cora: Th :: CoraDoc
+ Algebra.Graph.IO.Datasets.LINQS.Cora: data CoraDoc
+ Algebra.Graph.IO.Datasets.LINQS.Cora: instance Data.Binary.Class.Binary Algebra.Graph.IO.Datasets.LINQS.Cora.CoraDoc
+ Algebra.Graph.IO.Datasets.LINQS.Cora: instance GHC.Classes.Eq Algebra.Graph.IO.Datasets.LINQS.Cora.CoraDoc
+ Algebra.Graph.IO.Datasets.LINQS.Cora: instance GHC.Classes.Ord Algebra.Graph.IO.Datasets.LINQS.Cora.CoraDoc
+ Algebra.Graph.IO.Datasets.LINQS.Cora: instance GHC.Enum.Enum Algebra.Graph.IO.Datasets.LINQS.Cora.CoraDoc
+ Algebra.Graph.IO.Datasets.LINQS.Cora: instance GHC.Generics.Generic Algebra.Graph.IO.Datasets.LINQS.Cora.CoraDoc
+ Algebra.Graph.IO.Datasets.LINQS.Cora: instance GHC.Show.Show Algebra.Graph.IO.Datasets.LINQS.Cora.CoraDoc
+ Algebra.Graph.IO.Datasets.LINQS.Cora: loadCoraGraph :: FilePath -> IO (Graph (ContentRow CoraDoc))
+ Algebra.Graph.IO.Datasets.LINQS.Cora: sourceCoraGraphEdges :: (MonadResource m, MonadThrow m) => FilePath -> Map String (Seq Int16, CoraDoc) -> ConduitT i (Maybe (Graph (ContentRow CoraDoc))) m ()
+ Algebra.Graph.IO.Datasets.LINQS.Cora: stash :: FilePath -> IO ()
+ Algebra.Graph.IO.Internal.Megaparsec: alphaNum :: Parser String
+ Algebra.Graph.IO.Internal.Megaparsec: anyString :: Parser String
+ Algebra.Graph.IO.Internal.Megaparsec: lexeme :: Parser a -> Parser a
+ Algebra.Graph.IO.Internal.Megaparsec: symbol :: Text -> Parser Text
+ Algebra.Graph.IO.Internal.Megaparsec: type ParseE = ParseErrorBundle Text Void
+ Algebra.Graph.IO.Internal.Megaparsec: type Parser = Parsec Void Text
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: AI :: DocClass
+ Algebra.Graph.IO.Datasets.LINQS.Citeseer: AI :: CiteSeerDoc
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: Agents :: DocClass
+ Algebra.Graph.IO.Datasets.LINQS.Citeseer: Agents :: CiteSeerDoc
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: DB :: DocClass
+ Algebra.Graph.IO.Datasets.LINQS.Citeseer: DB :: CiteSeerDoc
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: HCI :: DocClass
+ Algebra.Graph.IO.Datasets.LINQS.Citeseer: HCI :: CiteSeerDoc
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: IR :: DocClass
+ Algebra.Graph.IO.Datasets.LINQS.Citeseer: IR :: CiteSeerDoc
- Algebra.Graph.IO.Datasets.LINQS.Citeseer: ML :: DocClass
+ Algebra.Graph.IO.Datasets.LINQS.Citeseer: ML :: CiteSeerDoc

Files

CHANGELOG.md view
@@ -1,3 +1,7 @@+* 0.3++  Add Cora dataset+ * 0.2    Citeseer dataset : 
README.md view
@@ -15,7 +15,7 @@  The package contains some small example datasets (e.g. "lesmiserables" and "karateclub"); these are provided ready for consumption in `Algebra.Graph.IO.Datasets`. -There are also bindings to larger datasets, such as the ones provided by the LINQS group [2] (e.g. "citeseer").+There are also bindings to larger datasets, such as the ones provided by the LINQS group [2] (e.g. "citeseer" and "cora").   ## Contributing
algebraic-graphs-io.cabal view
@@ -1,5 +1,5 @@ name:                algebraic-graphs-io-version:             0.2+version:             0.3 synopsis:            I/O utilities and datasets for algebraic-graphs description:         I/O utilities and datasets for algebraic-graphs. See README for details homepage:            https://github.com/ocramz/algebraic-graphs-io@@ -30,11 +30,12 @@                        Algebra.Graph.IO.Datasets                        Algebra.Graph.IO.Datasets.LINQS                        Algebra.Graph.IO.Datasets.LINQS.Citeseer+                       Algebra.Graph.IO.Datasets.LINQS.Cora                        Algebra.Graph.IO.MatrixMarket                        Algebra.Graph.IO.SV                        Algebra.Graph.IO.Internal.Conduit-  other-modules:                               Algebra.Graph.IO.Internal.Megaparsec+  other-modules:          build-depends:       algebraic-graphs                      , attoparsec                      , base >= 4.7 && < 5
src/Algebra/Graph/IO/Datasets/LINQS.hs view
@@ -1,18 +1,40 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE OverloadedStrings #-}+{-# language DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-} -- | Link-based datasets from https://linqs.soe.ucsc.edu/data {-# options_ghc -Wno-unused-imports -Wno-unused-top-binds #-}-module Algebra.Graph.IO.Datasets.LINQS where+module Algebra.Graph.IO.Datasets.LINQS (+  restoreContent, CitesRow(..), ContentRow(..), +  -- * Internal+  stash, sourceGraphEdges, loadGraph+                                       ) where +import Control.Applicative (Alternative(..)) import Control.Monad (when) import Control.Monad.IO.Class (MonadIO(..))+import Data.Functor (($>))+import GHC.Generics (Generic(..))+import GHC.Int (Int16) +-- algebraic-graphs+import qualified Algebra.Graph as G (Graph, empty, overlay, edge)+-- bytestring import Data.ByteString (ByteString) import Data.ByteString.Char8 (unpack)+-- binary+import Data.Binary (Binary(..), encode, decode, encodeFile, decodeFileOrFail)+-- binary-conduit+import qualified Data.Conduit.Serialization.Binary as CB (conduitDecode, conduitEncode, ParseError(..)) -- conduit import Conduit (MonadUnliftIO(..), MonadResource, runResourceT) import Data.Conduit (runConduit, ConduitT, (.|), yield, await)-import qualified Data.Conduit.Combinators as C (print, sourceFile, sinkFile, map, mapM, foldM, mapWhile)+import qualified Data.Conduit.Combinators as C (print, sourceFile, sinkFile, map, mapM, foldM, mapWhile, foldMap, foldl)+-- containers+import Data.Sequence (Seq, (|>))+import qualified Data.Map as M (Map, singleton, lookup)+-- directory+import System.Directory (createDirectoryIfMissing) -- exceptions import Control.Monad.Catch (MonadThrow(..)) -- filepath@@ -21,7 +43,9 @@ import Network.HTTP.Simple (httpSource, getResponseBody, Response, Request, parseRequest, setRequestMethod) -- megaparsec import Text.Megaparsec (parse)+import Text.Megaparsec.Char (char) import Text.Megaparsec.Char.Lexer (decimal)+import Text.Megaparsec.Error (errorBundlePretty) -- parser.combinators import Control.Monad.Combinators (count) -- primitive@@ -30,8 +54,11 @@ import Data.Conduit.Tar (Header(..), untarChunks, TarChunk, withEntries, FileInfo, filePath, withFileInfo, headerFileType, FileType(..), headerFilePath) -- text import Data.Text (Text)+import qualified Data.Text as T (Text, unwords)+import qualified Data.Text.IO as T (readFile)  import Algebra.Graph.IO.Internal.Conduit (fetch, unTarGz)+import Algebra.Graph.IO.Internal.Megaparsec (Parser, ParseE, symbol, lexeme, alphaNum) import Algebra.Graph.IO.SV (parseTSV)  {-@@ -45,3 +72,161 @@ http://www.cs.umd.edu/~sen/lbc-proj/data/WebKB.tgz  -}+++-- | Download, decompress, parse, serialize and save the dataset to local storage+stash :: (Binary c) =>+         FilePath -- ^ directory where the data files will be saved+      -> String -- ^ URI of .tar.gz file+      -> Int -- ^ dictionary size+      -> Parser c -- ^ document class+      -> IO ()+stash dir uri n pc = do+  rq <- parseRequest uri+  createDirectoryIfMissing True dir+  runResourceT $ runConduit $+    fetch rq .|+    unTarGz .|+    withFileInfo ( \fi -> do+     contentToFile dir n pc fi+     citesToFile dir fi )++-- | Load the graph node data from local storage+restoreContent :: (Binary c) => FilePath -- ^ directory where the data files are saved+               -> IO (M.Map String (Seq Int16, c))+restoreContent dir = runResourceT $ runConduit $+  contentFromFile dir .|+  C.foldMap ( \(CRow k fs c) -> M.singleton k (fs, c) )+++citesFromFile :: (MonadResource m, MonadThrow m) => FilePath -> ConduitT i (CitesRow String) m ()+citesFromFile dir =+  C.sourceFile (dir </> "cites") .|+  CB.conduitDecode++-- | Reconstruct the citation graph+--+-- NB : relies on the user having `stash`ed the dataset to local disk first.+loadGraph :: (Binary c) =>+                 FilePath -- ^ directory where the data files were saved+              -> IO (G.Graph (ContentRow c))+loadGraph dir = do+  mm <- restoreContent dir+  runResourceT $ runConduit $+    citesFromFile dir .|+    C.foldl (\gr (CitesRow b a) ->+               let+                 edm = (,) <$> M.lookup a mm <*> M.lookup b mm+               in+                 case edm of+                   Nothing -> gr -- error $ show e+                   Just ((bffs, bc), (affs, ac)) ->+                     let+                       acr = CRow a affs ac+                       bcr = CRow b bffs bc+                     in+                       (acr `G.edge` bcr) `G.overlay` gr+                ) G.empty++-- | Stream out the edges of the citation graph, in which the nodes are decorated with the document metadata.+--+-- The full citation graph can be reconstructed by folding over this stream and `G.overlay`ing the graph edges as they arrive.+--+-- This way the graph can be partitioned in training , test and validation subsets at the usage site+sourceGraphEdges :: (MonadResource m, MonadThrow m) =>+                      FilePath -- ^ directory of data files+                   -> M.Map String (Seq Int16, c) -- ^ 'content' data+                   -> ConduitT i (Maybe (G.Graph (ContentRow c))) m ()+sourceGraphEdges dir mm =+    citesFromFile dir .|+    C.map (\(CitesRow b a) ->+             case (,) <$> M.lookup a mm <*> M.lookup b mm of+               Nothing -> Nothing+               Just ((bffs, bc), (affs, ac)) ->+                 let+                       acr = CRow a affs ac+                       bcr = CRow b bffs bc+                 in Just (acr `G.edge` bcr))++++++++contentToFile :: (MonadThrow m, MonadResource m, Binary c) =>+                 FilePath+              -> Int -- ^ dictionary size+              -> Parser c -- ^ document class+              -> FileInfo+              -> ConduitT ByteString o m ()+contentToFile dir n pc fi = when ((takeExtension . unpack $ filePath fi) == ".content") $ do+  parseTSV .|+    C.map T.unwords .|+    C.map ( \r -> case parse (contentRowP n pc) "" r of+              Left e -> error $ errorBundlePretty e+              Right x -> x ) .|+    CB.conduitEncode .|+    C.sinkFile (dir </> "content-z")++contentFromFile :: (MonadResource m, MonadThrow m, Binary c) => FilePath+                -> ConduitT i (ContentRow c) m ()+contentFromFile dir =+  C.sourceFile (dir </> "content-z") .|+  CB.conduitDecode+++-- | Who cites whom+data CitesRow a = CitesRow {+  cirTo :: a -- ^ cited+  , cirFrom :: a -- ^ citing+  } deriving (Eq, Show, Generic, Binary)++citesRowP :: Parser (CitesRow String)+citesRowP = CitesRow <$> lexeme alphaNum <*> lexeme alphaNum+++-- | Dataset row of the .content file+--+-- The .content file contains descriptions of the papers in the following format:+--+-- 		\<paper_id\> \<word_attributes\> \<class_label\>+--+-- The first entry in each line contains the unique string ID of the paper followed by binary values indicating whether each word in the vocabulary is present (indicated by 1) or absent (indicated by 0) in the paper. Finally, the last entry in the line contains the class label of the paper.+data ContentRow c = CRow {+  crId :: String -- ^ identifier+  , crFeatures :: Seq Int16 -- ^ features, in sparse format (without the zeros)+  , crClass :: c -- ^ document class label+                   } deriving (Eq, Ord, Show, Generic, Binary)++bit :: Parser Bool+bit = (char '0' $> False) <|> (char '1' $> True)++sparse :: Foldable t => t Bool -> Seq Int16+sparse = fst . foldl (\(acc, i) b -> if b then (acc |> i, succ i) else (acc, succ i)) (mempty, 0)++contentRowP :: Int -- ^ vocabulary size+            -> Parser c -- ^ parser for document class+            -> Parser (ContentRow c)+contentRowP n dcp = do+  i <- lexeme alphaNum+  feats <- sparse <$> count n (lexeme bit)+  c <- lexeme dcp+  pure $ CRow i feats c++++citesToFile :: (MonadThrow m, MonadIO m, MonadResource m) =>+               FilePath+            -> FileInfo+            -> ConduitT ByteString c m ()+citesToFile dir fi = do+  let fpath = unpack $ filePath fi+  when (takeExtension fpath == ".cites") $+    parseTSV .|+    C.map T.unwords .|+    C.map ( \r -> case parse citesRowP "" r of+              Left e -> error $ errorBundlePretty e+              Right x -> x ) .|+    CB.conduitEncode .|+    C.sinkFile (dir </> "cites")
src/Algebra/Graph/IO/Datasets/LINQS/Citeseer.hs view
@@ -11,9 +11,9 @@   -- * 1. Download the dataset   stash   -- * 2. Reconstruct the citation graph-  , citeseerGraph, citeseerGraphEdges, restoreContent,-    -- * Types-    ContentRow(..), DocClass(..)) where+  , sourceCiteseerGraphEdges, loadCiteseerGraph+  -- * Types+    ,CiteSeerDoc(..)) where  import Control.Applicative (Alternative(..)) import Control.Monad (when, foldM)@@ -64,38 +64,32 @@ import Algebra.Graph.IO.Internal.Conduit (fetch, unTarGz) import Algebra.Graph.IO.Internal.Megaparsec (Parser, ParseE, symbol, lexeme, alphaNum) import Algebra.Graph.IO.SV (parseTSV)-+import qualified Algebra.Graph.IO.Datasets.LINQS as DL (stash, sourceGraphEdges, loadGraph, restoreContent, CitesRow(..), ContentRow(..)) {- CiteSeer: The CiteSeer dataset consists of 3312 scientific publications classified into one of six classes. The citation network consists of 4732 links. Each publication in the dataset is described by a 0/1-valued word vector indicating the absence/presence of the corresponding word from the dictionary. The dictionary consists of 3703 unique words. The README file in the dataset provides more details.-http://www.cs.umd.edu/~sen/lbc-proj/data/citeseer.tgz -} --- | Download, parse, serialize and save the dataset to local storage stash :: FilePath -- ^ directory where the data files will be saved       -> IO ()-stash dir = do-  let path = "http://www.cs.umd.edu/~sen/lbc-proj/data/citeseer.tgz"-  rq <- parseRequest path-  createDirectoryIfMissing True dir-  runResourceT $ runConduit $-    fetch rq .|-    unTarGz .|-    withFileInfo ( \fi -> do-     contentToFile dir fi-     citesToFile dir fi )+stash fp = DL.stash fp "http://www.cs.umd.edu/~sen/lbc-proj/data/citeseer.tgz" 3703 docClassP --- | Load the graph node data from local storage-restoreContent :: FilePath -- ^ directory where the data files are saved-               -> IO (M.Map String (Seq Int16, DocClass))-restoreContent dir = runResourceT $ runConduit $-  contentFromFile dir .|-  C.foldMap ( \(CRow k fs c) -> M.singleton k (fs, c) )+-- | See `DL.sourceGraphEdges`+sourceCiteseerGraphEdges :: (MonadResource m, MonadThrow m) =>+                      FilePath -- ^ directory of data files+                   -> M.Map String (Seq Int16, CiteSeerDoc) -- ^ 'content' data+                   -> ConduitT i (Maybe (G.Graph (DL.ContentRow CiteSeerDoc))) m ()+sourceCiteseerGraphEdges = DL.sourceGraphEdges +-- | See `DL.loadGraph`+loadCiteseerGraph :: +                     FilePath -- ^ directory where the data files were saved+                  -> IO (G.Graph (DL.ContentRow CiteSeerDoc))+loadCiteseerGraph = DL.loadGraph  -- | document classes of the Citeseer dataset-data DocClass = Agents | AI | DB | IR | ML | HCI deriving (Eq, Ord, Enum, Show, Generic, Binary)+data CiteSeerDoc = Agents | AI | DB | IR | ML | HCI deriving (Eq, Ord, Enum, Show, Generic, Binary) -docClassP :: Parser DocClass+docClassP :: Parser CiteSeerDoc docClassP =   (symbol "Agents" $> Agents) <|>   (symbol "AI" $> AI) <|>@@ -104,67 +98,7 @@   (symbol "ML" $> ML) <|>   (symbol "HCI" $> HCI) -{- --}---- | only process the .content file within the archive-content :: (MonadThrow io, MonadIO io) => ConduitT TarChunk o io ()-content = withFileInfo $ \fi ->-  when ((takeExtension . unpack $ filePath fi) == ".content") $-    parseTSV .|-    C.map T.unwords .|-    C.map (parse contentRowP "") .|-    C.print--contentToFile :: (MonadThrow m, MonadResource m) =>-                 FilePath -> FileInfo -> ConduitT ByteString c m ()-contentToFile dir fi = when ((takeExtension . unpack $ filePath fi) == ".content") $ do-  parseTSV .|-    C.map T.unwords .|-    C.map ( \r -> case parse contentRowP "" r of-              Left e -> error $ errorBundlePretty e-              Right x -> x ) .|-    CB.conduitEncode .|-    C.sinkFile (dir </> "content-z")--contentFromFile :: (MonadResource m, MonadThrow m) => FilePath -> ConduitT i ContentRow m ()-contentFromFile dir =-  C.sourceFile (dir </> "content-z") .|-  CB.conduitDecode---- | Dataset row of the .content file------ The .content file contains descriptions of the papers in the following format:------ 		\<paper_id\> \<word_attributes\> \<class_label\>------ The first entry in each line contains the unique string ID of the paper followed by binary values indicating whether each word in the vocabulary is present (indicated by 1) or absent (indicated by 0) in the paper (vocabulary : 3703 unique words). Finally, the last entry in the line contains the class label of the paper.-data ContentRow = CRow {-  crId :: String -- ^ identifier-  , crFeatures :: Seq Int16 -- ^ features, in sparse format (without the zeros)-  , crClass :: DocClass -- ^ document class label-                   } deriving (Eq, Ord, Show, Generic, Binary)--bit :: Parser Bool-bit = (char '0' $> False) <|> (char '1' $> True)--sparse :: Foldable t => t Bool -> Seq Int16-sparse = fst . foldl (\(acc, i) b -> if b then (acc |> i, succ i) else (acc, succ i)) (mempty, 0)--contentRowP :: Parser ContentRow-contentRowP = do-  i <- lexeme alphaNum-  let n = 3703-  foh <- count n (lexeme bit) -- one-hot encoded features-  let feats = sparse foh-  c <- lexeme docClassP-  pure $ CRow i feats c----- {- The .cites file contains the citation graph of the corpus. Each line describes a link in the following format: @@ -174,73 +108,9 @@ -} -- | only process the .cites file within the archive -citesToFile :: (MonadThrow m, MonadIO m, MonadResource m) =>-               FilePath-            -> FileInfo-            -> ConduitT ByteString c m ()-citesToFile dir fi = do-  let fpath = unpack $ filePath fi-  when (takeExtension fpath == ".cites") $-    parseTSV .|-    C.map T.unwords .|-    C.map ( \r -> case parse citesRowP "" r of-              Left e -> error $ errorBundlePretty e-              Right x -> x ) .|-    CB.conduitEncode .|-    C.sinkFile (dir </> "cites") -citesFromFile :: (MonadResource m, MonadThrow m) => FilePath -> ConduitT i (CitesRow String) m ()-citesFromFile dir =-  C.sourceFile (dir </> "cites") .|-  CB.conduitDecode --- | Reconstruct the citation graph------ NB : relies on the user having `stash`ed the dataset to local disk first.-citeseerGraph :: FilePath -- ^ directory where the data files were saved-              -> IO (G.Graph ContentRow)-citeseerGraph dir = do-  mm <- restoreContent dir-  runResourceT $ runConduit $-    citesFromFile dir .|-    C.foldl (\gr (CitesRow b a) ->-               let-                 edm = (,) <$> M.lookup a mm <*> M.lookup b mm-               in-                 case edm of-                   Nothing -> gr -- error $ show e-                   Just ((bffs, bc), (affs, ac)) ->-                     let-                       acr = CRow a affs ac-                       bcr = CRow b bffs bc-                     in-                       (acr `G.edge` bcr) `G.overlay` gr-                ) G.empty --- | Stream out the edges of the citation graph, in which the nodes are decorated with the document metadata.------ The full citation graph can be reconstructed by folding over this stream and `G.overlay`ing the graph edges as they arrive.------ This way the graph can be partitioned in training , test and validation subsets at the usage site-citeseerGraphEdges :: (MonadResource m, MonadThrow m) =>-                      FilePath -- ^ directory of data files-                   -> M.Map String (Seq Int16, DocClass) -- ^ 'content' data-                   -> ConduitT i (Maybe (G.Graph ContentRow)) m ()-citeseerGraphEdges dir mm =-    citesFromFile dir .|-    C.map (\(CitesRow b a) ->-             case (,) <$> M.lookup a mm <*> M.lookup b mm of-               Nothing -> Nothing-               Just ((bffs, bc), (affs, ac)) ->-                 let-                       acr = CRow a affs ac-                       bcr = CRow b bffs bc-                 in Just (acr `G.edge` bcr))--data CitesRow a = CitesRow { cirTo :: a, cirFrom :: a } deriving (Eq, Show, Generic, Binary)--citesRowP :: Parser (CitesRow String)-citesRowP = CitesRow <$> lexeme alphaNum <*> lexeme alphaNum   -- test
+ src/Algebra/Graph/IO/Datasets/LINQS/Cora.hs view
@@ -0,0 +1,134 @@+{-# language DeriveAnyClass #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE OverloadedStrings #-}+{-# options_ghc -Wno-unused-imports -Wno-unused-top-binds #-}+-- | Cora document classification dataset, from :+--+-- McCallum, A. and Nigam, K., "Automating the construction of internet portals with machine learning" Information Retrieval, 2000+--+-- Qing Lu, and Lise Getoor. "Link-based classification." ICML, 2003.+--+-- https://linqs.soe.ucsc.edu/data+module Algebra.Graph.IO.Datasets.LINQS.Cora (+    -- * 1. Download the dataset+  stash+  -- * 2. Reconstruct the citation graph+  , sourceCoraGraphEdges, loadCoraGraph+  -- * Types+  , CoraDoc(..)+                                            ) where++import Control.Applicative (Alternative(..))+import Control.Monad (when, foldM)+import Control.Monad.IO.Class (MonadIO(..))+import GHC.Generics (Generic(..))+import GHC.Int (Int16)+import Data.Functor (($>))++-- algebraic-graphs+import qualified Algebra.Graph as G (Graph, empty, overlay, edge)+-- binary+import Data.Binary (Binary(..), encode, decode, encodeFile, decodeFileOrFail)+-- binary-conduit+import qualified Data.Conduit.Serialization.Binary as CB (conduitDecode, conduitEncode, ParseError(..))+-- bytestring+import Data.ByteString (ByteString)+import Data.ByteString.Char8 (unpack)+-- conduit+import Conduit (MonadUnliftIO(..), MonadResource, runResourceT)+import Data.Conduit (runConduit, ConduitT, (.|), yield, await)+import qualified Data.Conduit.Combinators as C (print, sourceFile, sinkFile, map, mapM, foldM, foldMap, foldl, foldMapM, mapWhile)+-- containers+import Data.Sequence (Seq, (|>))+import qualified Data.Map as M (Map, singleton, lookup)+-- directory+import System.Directory (createDirectoryIfMissing)+-- exceptions+import Control.Monad.Catch (MonadThrow(..))+-- filepath+import System.FilePath ((</>), takeFileName, takeExtension)+-- http-conduit+import Network.HTTP.Simple (httpSource, getResponseBody, Response, Request, parseRequest, setRequestMethod)+-- megaparsec+import Text.Megaparsec (parse, parseTest, (<?>))+import Text.Megaparsec.Char (char)+import Text.Megaparsec.Char.Lexer (decimal)+import Text.Megaparsec.Error (errorBundlePretty)+-- parser.combinators+import Control.Monad.Combinators (count)+-- primitive+import Control.Monad.Primitive (PrimMonad(..))+-- tar-conduit+import Data.Conduit.Tar (Header(..), untarChunks, TarChunk, withEntries, FileInfo, filePath, withFileInfo, headerFileType, FileType(..), headerFilePath)+-- text+import qualified Data.Text as T (Text, unwords)+import qualified Data.Text.IO as T (readFile)++import Algebra.Graph.IO.Internal.Megaparsec (Parser, ParseE, symbol, lexeme, alphaNum)++import qualified Algebra.Graph.IO.Datasets.LINQS as DL (stash, sourceGraphEdges, loadGraph, restoreContent, CitesRow(..), ContentRow(..))++{-+The Cora dataset consists of Machine Learning papers. These papers are classified into one of the following seven classes:+		Case_Based+		Genetic_Algorithms+		Neural_Networks+		Probabilistic_Methods+		Reinforcement_Learning+		Rule_Learning+		Theory+-}++-- | document classes of the Cora dataset+data CoraDoc = CB | GA | NN | PM | RL | RuL | Th deriving (Eq, Show, Ord, Enum, Generic, Binary)++docClassP :: Parser CoraDoc+docClassP =+  (symbol "Case_Based" $> CB) <|>+  (symbol "Genetic_Algorithms" $> GA) <|>+  (symbol "Neural_Networks" $> NN) <|>+  (symbol "Probabilistic_Methods" $> PM) <|>+  (symbol "Reinforcement_Learning" $> RL) <|>+  (symbol "Rule_Learning" $> RuL) <|>+  (symbol "Theory" $> Th)++{-+The papers were selected in a way such that in the final corpus every paper cites or is cited by atleast one other paper. There are 2708 papers in the whole corpus. ++After stemming and removing stopwords we were left with a vocabulary of size 1433 unique words. All words with document frequency less than 10 were removed.+-}++stash :: FilePath -> IO ()+stash fp = DL.stash fp "http://www.cs.umd.edu/~sen/lbc-proj/data/cora.tgz" 1433 docClassP++-- | See `DL.sourceGraphEdges`+sourceCoraGraphEdges :: (MonadResource m, MonadThrow m) =>+                      FilePath -- ^ directory of data files+                   -> M.Map String (Seq Int16, CoraDoc) -- ^ 'content' data+                   -> ConduitT i (Maybe (G.Graph (DL.ContentRow CoraDoc))) m ()+sourceCoraGraphEdges = DL.sourceGraphEdges++-- | See `DL.loadGraph`+loadCoraGraph :: FilePath -- ^ directory where the data files were saved+                  -> IO (G.Graph (DL.ContentRow CoraDoc))+loadCoraGraph = DL.loadGraph++++{-+++THE DIRECTORY CONTAINS TWO FILES:++The .content file contains descriptions of the papers in the following format:++		<paper_id> <word_attributes> <class_label>++The first entry in each line contains the unique string ID of the paper followed by binary values indicating whether each word in the vocabulary is present (indicated by 1) or absent (indicated by 0) in the paper. Finally, the last entry in the line contains the class label of the paper.++The .cites file contains the citation graph of the corpus. Each line describes a link in the following format:++		<ID of cited paper> <ID of citing paper>++Each line contains two paper IDs. The first entry is the ID of the paper being cited and the second ID stands for the paper which contains the citation. The direction of the link is from right to left. If a line is represented by "paper1 paper2" then the link is "paper2->paper1". +-}
src/Algebra/Graph/IO/Internal/Conduit.hs view
@@ -6,6 +6,7 @@  import Control.Monad (when) import Control.Monad.IO.Class (MonadIO(..))+import Data.Function ((&))  -- bytestring import Data.ByteString (ByteString)@@ -20,7 +21,7 @@ -- exceptions import Control.Monad.Catch (MonadThrow(..)) -- http-conduit-import Network.HTTP.Simple (httpSource, getResponseBody, Response, Request, parseRequest, setRequestMethod)+import Network.HTTP.Simple (httpSource, getResponseBody, Response, Request, parseRequest, setRequestMethod, setRequestSecure) -- primitive import Control.Monad.Primitive (PrimMonad(..)) -- tar-conduit@@ -34,7 +35,7 @@  -- | Download a file fetch :: MonadResource m => Request -> ConduitT i ByteString m ()-fetch r = httpSource r getResponseBody+fetch r = httpSource (r & setRequestSecure False) getResponseBody  -- | Download, decompress and save a .tar.gz archive fetchTarGz :: String -- ^ URL with the .tar.gz
src/Algebra/Graph/IO/Internal/Megaparsec.hs view
@@ -1,6 +1,9 @@ {-# language OverloadedStrings #-} {-# options_ghc -Wno-unused-imports #-}-module Algebra.Graph.IO.Internal.Megaparsec where+module Algebra.Graph.IO.Internal.Megaparsec (Parser, ParseE,+                                            -- * Internal+                                            lexeme, symbol, anyString, alphaNum+                                            ) where  import Control.Applicative hiding (many, some) import Data.Char (isAlpha, isSpace, isAlphaNum)@@ -20,13 +23,16 @@  type ParseE = ParseErrorBundle Text Void -lexeme :: Parser a -> Parser a+lexeme :: Parser a -- ^ disregard any whitespace around this parser+       -> Parser a lexeme = L.lexeme sc -symbol :: Text -> Parser Text+-- | Match a string+symbol :: Text+       -> Parser Text symbol = L.symbol sc --- space consumer+-- | space consumer sc :: Parser () sc = L.space      space1