2 Module : Gargantext.Core.Viz.Graph.Bridgeness
3 Description : Bridgeness filter
4 Copyright : (c) CNRS, 2017-Present
5 License : AGPL + CECILL v3
6 Maintainer : team@gargantext.org
7 Stability : experimental
10 Let be a graph with partitions (from Louvain algo), Bridgeness uniformly
11 filters inter-communities links.
13 TODO rewrite Bridgeness with "equivalence structurale" metrics (Confluence)
14 TODO use Map LouvainNodeId (Map LouvainNodeId)
18 module Gargantext.Core.Viz.Graph.Bridgeness -- (bridgeness)
21 import Data.List (concat, sortOn)
22 import Data.Map (Map, fromListWith, lookup, toList, mapWithKey, elems)
23 import Data.Maybe (catMaybes)
24 import Data.Ord (Down(..))
25 import Gargantext.Prelude
26 import Graph.Types (ClusterNode(..))
27 import qualified Data.Map as DM
29 ----------------------------------------------------------------------
30 type Partitions a = Map (Int, Int) Double -> IO [a]
31 ----------------------------------------------------------------------
33 nodeId2comId :: a -> (NodeId,CommunityId)
36 type CommunityId = Int
38 ----------------------------------------------------------------------
39 instance ToComId ClusterNode where
40 nodeId2comId (ClusterNode i1 i2) = (i1, i2)
42 ----------------------------------------------------------------------
43 ----------------------------------------------------------------------
44 type Bridgeness = Double
46 bridgeness :: ToComId a
49 -> Map (NodeId, NodeId) Double
50 -> Map (NodeId, NodeId) Double
51 bridgeness = bridgenessWith nodeId2comId
53 bridgenessWith :: (a -> (Int, Int))
56 -> Map (Int, Int) Double
57 -> Map (Int, Int) Double
58 bridgenessWith f b ns = DM.fromList
62 . groupEdges (DM.fromList $ map f ns)
65 groupEdges :: (Ord a, Ord b1)
68 -> Map (a, a) [((b1, b1), b2)]
69 groupEdges m = fromListWith (<>)
73 n1n2_m = (,) <$> lookup n1 m <*> lookup n2 m
74 n1n2_d = Just [((n1,n2),d)]
75 in (,) <$> n1n2_m <*> n1n2_d
79 -- | TODO : sortOn Confluence
80 filterComs :: (Ord n1, Eq n2)
82 -> Map (n2, n2) [(a3, n1)]
83 -> Map (n2, n2) [(a3, n1)]
84 filterComs _b m = DM.filter (\n -> length n > 0) $ mapWithKey filter' m
89 | otherwise = take 1 $ sortOn (Down . snd) a
92 _n = round $ 100 * a' / t
93 a'= fromIntegral $ length a
95 t = fromIntegral $ length $ concat $ elems m
97 --------------------------------------------------------------
100 Compute the median of a list
101 From: https://hackage.haskell.org/package/dsp-0.2.5.1/docs/src/Numeric.Statistics.Median.html
102 Compute the center of the list in a more lazy manner
103 and thus halves memory requirement.
105 median :: (Ord a, Fractional a) => [a] -> a
106 median [] = panic "medianFast: empty list has no median"
108 let recurse (x0:_) (_:[]) = x0
109 recurse (x0:x1:_) (_:_:[]) = (x0+x1)/2
110 recurse (_:xs) (_:_:ys) = recurse xs ys
112 panic "median: this error cannot occur in the way 'recurse' is called"