2 Module : Gargantext.Core.Text.Ngrams
3 Description : Ngrams definition and tools
4 Copyright : (c) CNRS, 2017 - present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
10 An @n-gram@ is a contiguous sequence of n items from a given sample of
11 text. In Gargantext application the items are words, n is a non negative
14 Using Latin numerical prefixes, an n-gram of size 1 is referred to as a
15 "unigram"; size 2 is a "bigram" (or, less commonly, a "digram"); size
16 3 is a "trigram". English cardinal numbers are sometimes used, e.g.,
17 "four-gram", "five-gram", and so on.
19 Source: https://en.wikipedia.org/wiki/Ngrams
23 compute occ by node of Tree
24 group occs according groups
31 {-# LANGUAGE TemplateHaskell #-}
32 {-# LANGUAGE ConstrainedClassMethods #-}
34 module Gargantext.Core.Text.Terms
38 import Data.HashMap.Strict (HashMap)
39 import Data.Hashable (Hashable)
41 import Data.Text (Text)
42 import Data.Traversable
43 import GHC.Base (String)
44 import GHC.Generics (Generic)
45 import qualified Data.List as List
46 import qualified Data.Set as Set
47 import qualified Data.Text as Text
48 import qualified Data.HashMap.Strict as HashMap
49 import Gargantext.Core
50 import Gargantext.Core.Text (sentences, HasText(..))
51 import Gargantext.Core.Text.Terms.Eleve (mainEleveWith, Tries, Token, buildTries, toToken)
52 import Gargantext.Core.Text.Terms.Mono (monoTerms)
53 import Gargantext.Core.Text.Terms.Mono.Stem (stem)
54 import Gargantext.Core.Text.Terms.Mono.Token.En (tokenize)
55 import Gargantext.Core.Text.Terms.Multi (multiterms)
56 import Gargantext.Core.Types
57 import Gargantext.Database.Prelude (Cmd)
58 import Gargantext.Database.Query.Table.Ngrams (insertNgrams)
59 import Gargantext.Database.Query.Table.NgramsPostag (NgramsPostag(..), insertNgramsPostag, np_form, np_lem)
60 import Gargantext.Database.Schema.Ngrams (Ngrams(..), NgramsType(..), ngramsTerms, text2ngrams, NgramsId)
61 import Gargantext.Prelude
64 = Mono { _tt_lang :: !lang }
65 | Multi { _tt_lang :: !lang }
66 | MonoMulti { _tt_lang :: !lang }
67 | Unsupervised { _tt_lang :: !lang
68 , _tt_windowSize :: !Int
69 , _tt_ngramsSize :: !Int
70 , _tt_model :: !(Maybe (Tries Token ()))
75 --group :: [Text] -> [Text]
79 -- map (filter (\t -> not . elem t)) $
80 ------------------------------------------------------------------------
81 -- | Sugar to extract terms from text (hiddeng mapM from end user).
82 --extractTerms :: Traversable t => TermType Lang -> t Text -> IO (t [Terms])
83 extractTerms :: TermType Lang -> [Text] -> IO [[Terms]]
85 extractTerms (Unsupervised l n s m) xs = mapM (terms (Unsupervised l n s (Just m'))) xs
89 Nothing -> newTries n (Text.intercalate " " xs)
91 extractTerms termTypeLang xs = mapM (terms termTypeLang) xs
94 ------------------------------------------------------------------------
95 withLang :: (Foldable t, Functor t, HasText h)
99 withLang (Unsupervised l n s m) ns = Unsupervised l n s m'
102 Nothing -> -- trace ("buildTries here" :: String)
103 Just $ buildTries n ( fmap toToken
105 $ Text.intercalate " . "
112 ------------------------------------------------------------------------
113 data ExtractedNgrams = SimpleNgrams { unSimpleNgrams :: Ngrams }
114 | EnrichedNgrams { unEnrichedNgrams :: NgramsPostag }
115 deriving (Eq, Ord, Generic, Show)
117 instance Hashable ExtractedNgrams
119 class ExtractNgramsT h
121 extractNgramsT :: HasText h
124 -> Cmd err (HashMap ExtractedNgrams (Map NgramsType Int))
125 ------------------------------------------------------------------------
126 enrichedTerms :: Lang -> PosTagAlgo -> POS -> Terms -> NgramsPostag
127 enrichedTerms l pa po (Terms ng1 ng2) =
128 NgramsPostag l pa po form lem
130 form = text2ngrams $ Text.intercalate " " ng1
131 lem = text2ngrams $ Text.intercalate " " $ Set.toList ng2
133 ------------------------------------------------------------------------
134 cleanNgrams :: Int -> Ngrams -> Ngrams
136 | Text.length (ng ^. ngramsTerms) < s = ng
137 | otherwise = text2ngrams (Text.take s (ng ^. ngramsTerms))
139 cleanExtractedNgrams :: Int -> ExtractedNgrams -> ExtractedNgrams
140 cleanExtractedNgrams s (SimpleNgrams ng) = SimpleNgrams $ (cleanNgrams s) ng
141 cleanExtractedNgrams s (EnrichedNgrams ng) = EnrichedNgrams $ over np_form (cleanNgrams s)
142 $ over np_lem (cleanNgrams s) ng
144 extracted2ngrams :: ExtractedNgrams -> Ngrams
145 extracted2ngrams (SimpleNgrams ng) = ng
146 extracted2ngrams (EnrichedNgrams ng) = view np_form ng
149 ---------------------------
150 insertExtractedNgrams :: [ ExtractedNgrams ] -> Cmd err (HashMap Text NgramsId)
151 insertExtractedNgrams ngs = do
152 let (s, e) = List.partition isSimpleNgrams ngs
153 m1 <- insertNgrams (map unSimpleNgrams s)
154 --printDebug "others" m1
156 m2 <- insertNgramsPostag (map unEnrichedNgrams e)
157 --printDebug "terms" m2
159 let result = HashMap.union m1 m2
162 isSimpleNgrams :: ExtractedNgrams -> Bool
163 isSimpleNgrams (SimpleNgrams _) = True
164 isSimpleNgrams _ = False
166 ------------------------------------------------------------------------
169 -- Multi : multi terms
170 -- MonoMulti : mono and multi
171 -- TODO : multi terms should exclude mono (intersection is not empty yet)
172 terms :: TermType Lang -> Text -> IO [Terms]
173 terms (Mono lang) txt = pure $ monoTerms lang txt
174 terms (Multi lang) txt = multiterms lang txt
175 terms (MonoMulti lang) txt = terms (Multi lang) txt
176 terms (Unsupervised lang n s m) txt = termsUnsupervised (Unsupervised lang n s (Just m')) txt
178 m' = maybe (newTries n txt) identity m
179 -- terms (WithList list) txt = pure . concat $ extractTermsWithList list txt
182 ------------------------------------------------------------------------
183 -- | Unsupervised ngrams extraction
184 -- language agnostic extraction
186 -- TODO: newtype BlockText
188 type WindowSize = Int
189 type MinNgramSize = Int
191 termsUnsupervised :: TermType Lang -> Text -> IO [Terms]
192 termsUnsupervised (Unsupervised l n s m) =
196 . (List.filter (\l' -> List.length l' >= s))
198 . mainEleveWith (maybe (panic "no model") identity m) n
200 termsUnsupervised _ = undefined
204 newTries :: Int -> Text -> Tries Token ()
205 newTries n t = buildTries n (fmap toToken $ uniText t)
207 -- | TODO removing long terms > 24
208 uniText :: Text -> [[Text]]
209 uniText = map (List.filter (not . isPunctuation))
211 . sentences -- TODO get sentences according to lang
214 text2term :: Lang -> [Text] -> Terms
215 text2term _ [] = Terms [] Set.empty
216 text2term lang txt = Terms txt (Set.fromList $ map (stem lang) txt)
218 isPunctuation :: Text -> Bool
219 isPunctuation x = List.elem x $ (Text.pack . pure)
220 <$> ("!?(),;." :: String)