2 Module : Gargantext.Text.Ngrams.Lists
3 Description : Tools to build lists
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
12 {-# LANGUAGE NoImplicitPrelude #-}
13 {-# LANGUAGE OverloadedStrings #-}
14 {-# LANGUAGE RankNTypes #-}
16 module Gargantext.Text.List
19 import Data.Either (partitionEithers, Either(..))
20 import Debug.Trace (trace)
23 import Data.Text (Text)
24 import Gargantext.API.Ngrams (NgramsElement, mkNgramsElement, RootParent(..), mSetFromList)
25 import Gargantext.API.Ngrams.Tools (getCoocByNgrams', Diagonal(..))
26 import Gargantext.Core (Lang(..))
27 import Gargantext.Core.Types (ListType(..), MasterCorpusId, UserCorpusId, NodeId)
28 import Gargantext.Database.Metrics.NgramsByNode (getTficf', sortTficf, ngramsGroup, getNodesByNgramsUser, groupNodesByNgramsWith)
29 import Gargantext.Database.Schema.Ngrams (NgramsType(..))
30 import Gargantext.Database.Utils (Cmd)
31 import Gargantext.Text.List.Learn (Model(..))
32 import Gargantext.Text.Metrics (takeScored)
33 import Gargantext.Prelude
34 --import Gargantext.Text.Terms (TermType(..))
35 import qualified Data.Char as Char
36 import qualified Data.List as List
37 import qualified Data.Map as Map
38 import qualified Data.Set as Set
39 import qualified Data.Text as Text
42 data NgramsListBuilder = BuilderStepO { stemSize :: Int
46 | BuilderStep1 { withModel :: Model }
47 | BuilderStepN { withModel :: Model }
48 | Tficf { nlb_lang :: Lang
51 , nlb_stopSize :: StopSize
52 , nlb_userCorpusId :: UserCorpusId
53 , nlb_masterCorpusId :: MasterCorpusId
57 data StopSize = StopSize {unStopSize :: Int}
59 -- | TODO improve grouping functions of Authors, Sources, Institutes..
60 buildNgramsLists :: Lang -> Int -> Int -> StopSize -> UserCorpusId -> MasterCorpusId
61 -> Cmd err (Map NgramsType [NgramsElement])
62 buildNgramsLists l n m s uCid mCid = do
63 ngTerms <- buildNgramsTermsList l n m s uCid mCid
64 othersTerms <- mapM (buildNgramsOthersList uCid identity) [Authors, Sources, Institutes]
65 pure $ Map.unions $ othersTerms <> [ngTerms]
68 buildNgramsOthersList :: UserCorpusId -> (Text -> Text) -> NgramsType
69 -> Cmd err (Map NgramsType [NgramsElement])
70 buildNgramsOthersList uCid groupIt nt = do
71 ngs <- groupNodesByNgramsWith groupIt <$> getNodesByNgramsUser uCid nt
75 all' = List.reverse $ List.sortOn (Set.size . snd . snd) $ Map.toList ngs
76 graphTerms = List.take listSize all'
77 candiTerms = List.drop listSize all'
78 pure $ Map.unionsWith (<>) [ toElements GraphTerm graphTerms
79 , toElements CandidateTerm candiTerms]
81 toElements nType x = Map.fromList [(nt, [ mkNgramsElement t nType Nothing (mSetFromList [])
88 buildNgramsTermsList' :: UserCorpusId
90 -> ((Text, (Set Text, Set NodeId)) -> Bool) -> Int -> Int
91 -> Cmd err (Map NgramsType [NgramsElement])
93 buildNgramsTermsList' uCid groupIt stop gls is = do
94 ngs <- groupNodesByNgramsWith groupIt <$> getNodesByNgramsUser uCid NgramsTerms
97 (stops, candidates) = partitionEithers
98 $ map (\t -> if stop t then Left t else Right t)
100 $ Map.filter ((\s' -> Set.size s' > 1) . snd) ngs
102 (maps, candidates') = takeScored gls is
103 $ getCoocByNgrams' snd (Diagonal True)
104 $ Map.fromList candidates
107 toList' t = (fst t, (fromIntegral $ Set.size $ snd $ snd t, fst $ snd t))
110 , List.filter (\(k,_) -> List.elem k candidates') candidates
111 , List.filter (\(k,_) -> List.elem k maps) candidates
114 let ngs' = List.concat
115 $ map toNgramsElement
116 $ map (\t -> (StopTerm, toList' t)) s
117 <> map (\t -> (CandidateTerm, toList' t)) c
118 <> map (\t -> (GraphTerm, toList' t)) m
120 pure $ Map.fromList [(NgramsTerms, ngs')]
123 buildNgramsTermsList :: Lang -> Int -> Int -> StopSize -> UserCorpusId -> MasterCorpusId
124 -> Cmd err (Map NgramsType [NgramsElement])
125 buildNgramsTermsList l n m s uCid mCid = do
126 candidates <- sortTficf <$> getTficf' uCid mCid NgramsTerms (ngramsGroup l n m)
128 candidatesSize = 2000
131 candidatesHead = List.take candidatesSize candidates
132 candidatesTail = List.drop candidatesSize candidates
133 termList = (toTermList a b ((isStopTerm s) . fst) candidatesHead)
134 <> (map (toList ((isStopTerm s) .fst) CandidateTerm) candidatesTail)
135 let ngs = List.concat $ map toNgramsElement termList
137 pure $ Map.fromList [(NgramsTerms, ngs)]
140 toNgramsElement :: (ListType, (Text, (Double, Set Text))) -> [NgramsElement]
141 toNgramsElement (listType, (_stem, (_score, setNgrams))) =
142 case Set.toList setNgrams of
144 (parent:children) -> [parentElem] <> childrenElems
146 parentElem = mkNgramsElement parent
149 (mSetFromList children)
150 childrenElems = map (\t -> mkNgramsElement t listType
151 (Just $ RootParent parent parent)
156 toList :: (b -> Bool) -> ListType -> b -> (ListType, b)
157 toList stop l n = case stop n of
158 True -> (StopTerm, n)
162 toTermList :: Int -> Int -> (a -> Bool) -> [a] -> [(ListType, a)]
163 toTermList _ _ _ [] = []
164 toTermList a b stop ns = trace ("computing toTermList") $
165 map (toList stop CandidateTerm) xs
166 <> map (toList stop GraphTerm) ys
167 <> toTermList a b stop zs
176 isStopTerm :: StopSize -> Text -> Bool
177 isStopTerm (StopSize n) x = Text.length x < n || any isStopChar (Text.unpack x)
179 isStopChar c = not (c `elem` ("- /()%" :: [Char]) || Char.isAlpha c)