├── CliquePercolationMethod.py ├── LICENSE ├── README.md └── karate.GraphML /CliquePercolationMethod.py: -------------------------------------------------------------------------------- 1 | from igraph import Graph, summary, plot 2 | from functools import partial 3 | from multiprocessing.pool import Pool 4 | import time 5 | 6 | 7 | def clique_percolation_method(graph, k = 3, workers = 1, attribute = None, verbose = False): 8 | """ 9 | Function that implements the Clique Percolation Method (CPM) algorithm for 10 | finding overlapping communities within networks, introduced by Palla et al. (2005). 11 | 12 | Parameters 13 | ---------- 14 | graph : igraph object 15 | The igraph object containing the graph. 16 | k : int, optional 17 | Size of the clique. The default is 3. 18 | workers : int, optional 19 | Number of threads to allocate for running this algorithm. The default is 1. 20 | attribute : str, optional 21 | The attribute of the vertices to use for displaying nodes within the communities. 22 | For displaying purposes, if vertices have names, the latter can be quite handy to figure out which node belongs to a certain community. 23 | If no attribute is given, the algorithm will display their id. The default is None. 24 | verbose : bool, optional 25 | If set to True it shows status updates. The default is False. 26 | 27 | Raises 28 | ------ 29 | TypeError 30 | If the types of the variables passed are incorrect. 31 | ValueError 32 | If the values of the variables passed are incorrect. 33 | 34 | Returns 35 | ------- 36 | list 37 | Contains lists of communities (lists). 38 | 39 | """ 40 | if not isinstance(graph,Graph): 41 | raise TypeError("The object graph must be an instance of the igraph class") 42 | 43 | if not isinstance(k,int): 44 | raise TypeError("Expecting size of cliques (k) to be an integer") 45 | 46 | if not isinstance(workers,int): 47 | raise TypeError("Number of workers must be integer") 48 | 49 | if workers < 1: 50 | raise ValueError("Expecting number of workers greater than or equal to 1") 51 | 52 | if attribute is not None: 53 | if not isinstance(attribute,str): 54 | raise TypeError("Expecting attribute to be a string") 55 | 56 | if attribute not in graph.vs.attributes(): 57 | raise ValueError("Attribute {} in vertices does not exist".format(attribute)) 58 | 59 | if not isinstance(verbose,bool): 60 | raise TypeError("Field verbose must be set to either True or False") 61 | 62 | communities = list() 63 | 64 | if verbose: 65 | start_time = time.time() 66 | 67 | # FINDING CLIQUES 68 | cliques = graph.cliques(min=k, max=k) 69 | num_cliques = len(cliques) 70 | 71 | if verbose: 72 | print("Finished cliques --- %s seconds ---" % (time.time() - start_time)) 73 | print("Cliques found %s" % (num_cliques)) 74 | 75 | set_cliques = [set(i) for i in cliques] 76 | 77 | # FINDING CLIQUE GRAPH 78 | indices = list(range(num_cliques)) 79 | 80 | edge_list = list() 81 | minimum = k-1 82 | annotate = partial(partial_clique_graph, set_cliques=set_cliques, minimum=minimum, num_cliques=num_cliques) 83 | 84 | pool = Pool(workers) 85 | edges = pool.map(annotate, indices) 86 | edge_list = [j for i in edges for j in i] 87 | 88 | if verbose: 89 | print("Finished comparison cliques --- %s seconds ---" % (time.time() - start_time)) 90 | 91 | clique_graph = Graph(edge_list) 92 | clique_graph.vs["name"] = [i for i in range(0,num_cliques)] 93 | 94 | # FINDING CONNECTED COMPONENTS IN THE GRAPH 95 | components = clique_graph.decompose() 96 | 97 | # CREATING COMMUNITIES 98 | for component in components: 99 | members_list = [list(cliques[i["name"]]) for i in component.vs] 100 | this_community = [item for sublist in members_list for item in sublist] 101 | communities.append(list(set(this_community))) 102 | 103 | if attribute is not None: 104 | communities_with_names = list() 105 | for community in communities: 106 | communities_with_names.append([graph.vs[element][attribute] for element in community]) 107 | communities = communities_with_names 108 | 109 | if verbose: 110 | print("Finished all --- %s seconds ---" % (time.time() - start_time)) 111 | 112 | for comm in communities: 113 | print(len(comm)) 114 | 115 | return communities 116 | 117 | 118 | def partial_clique_graph(i, set_cliques, minimum, num_cliques): 119 | """ 120 | Function that supports the creation of the clique graph, the second stage of CPM. 121 | This function is detached from the main function since it is parallelised 122 | (based on the amout of workers). 123 | 124 | Parameters 125 | ---------- 126 | i : integer 127 | The iterator for parallelisation. 128 | set_cliques : list(set) 129 | List containing all found cliques. Each clique is a set so it becomes easier to compare 130 | minimum : int 131 | Minimum overlapping between two cliques (size_of_cliques-1). 132 | num_cliques : int 133 | Number of cliques found in the graph. 134 | 135 | Returns 136 | ------- 137 | edge_list : list 138 | List of edges belonging to the iterated node. 139 | 140 | """ 141 | edge_list = list() 142 | this_set = set_cliques[i] 143 | for j in range(i+1, num_cliques): 144 | if len(this_set.intersection(set_cliques[j])) == minimum: 145 | edge_list.append((i,j)) 146 | return edge_list 147 | 148 | 149 | def test(): 150 | g = Graph() 151 | g.add_vertices(["1","2","3","4","5","6","7","8","9"]) 152 | g.add_edges([("1", "2"), ("1", "3"), ("1", "4"), ("2", "3"), ("3", "4"), ("4", "5"), ("4", "6"), ("5", "6"), ("5", "8"), ("5", "7"), ("6", "8"), ("6", "7"), ("7", "8"), ("7", "9")]) 153 | summary(g) 154 | plot(g) 155 | communities = clique_percolation_method(g,3) 156 | print("Cliques:") 157 | for count, comm in enumerate(communities): 158 | print("{}: {}".format(count,[g.vs[i]["name"] for i in comm])) 159 | 160 | 161 | def test_karate(): 162 | karate = Graph.Read_GraphML("karate.GraphML") 163 | summary(karate) 164 | communities = clique_percolation_method(karate,3,attribute="name") 165 | print("Cliques:") 166 | for count, comm in enumerate(communities): 167 | print("{}: {}".format(count,comm)) 168 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Angelo Antonio Salatino 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CliquePercolationMethod-Python 2 | Clique Percolation Method (CPM) is an algorithm for finding overlapping communities within networks, introduced by Palla et al. (2005, see references). This implementation in Python, firstly detects communities of size k, then creates a clique graph. Each community will be represented by each connected component in the clique graph. 3 | 4 | # Algorithm 5 | The algorithm performs the following steps: 6 | 7 | 1- first find all cliques of size k in the graph
8 | 2- then create graph where nodes are cliques of size k
9 | 3- add edges if two nodes (cliques) share k-1 common nodes
10 | 4- each connected component is a community
11 | 12 | # Main Implementations 13 | * clique_percolation_method(graph, k = 3): Implementation of the Clique Percolation Method 14 | 15 | It requires igraph library: 16 | ``` 17 | pip install python-igraph 18 | ``` 19 | 20 | # Run 21 | In this version, the main script contains some test functionalities that help on how to get going with this algorithm. 22 | ``` 23 | import CliquePercolationMethod as cpm 24 | 25 | cpm.text() 26 | # or 27 | cpm.test_karate() 28 | ``` 29 | 30 | # Parameters 31 | * **graph** : igraph object 32 | The _igraph object_ containing the graph. 33 | * **k** : int, optional 34 | Size of the clique. The default is _3_. 35 | * **workers** : int, optional 36 | Number of threads to allocate for running this algorithm. The default is _1_. 37 | * **attribute** : str, optional 38 | The attribute of the vertices to use for displaying nodes within the communities. 39 | For displaying purposes, if vertices have names, the latter can be quite handy to figure out which node belongs to a certain community. 40 | If no attribute is given, the algorithm will display their id. The default is _None_. 41 | * **verbose** : bool, optional 42 | If set to _True_ it shows status updates. The default is _False_. 43 | 44 | # Reference 45 | Palla, Gergely, Imre Derényi, Illés Farkas, and Tamás Vicsek. "Uncovering the overlapping community structure of complex networks in nature and society." Nature 435, no. 7043 (2005): 814-818. 46 | -------------------------------------------------------------------------------- /karate.GraphML: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Zachary's karate club network 15 | Wayne W. Zachary. An Information Flow Model for Conflict and Fission in Small Groups. Journal of Anthropological Research Vol. 33, No. 4 452-473 16 | Wayne W. Zachary 17 | 18 | 1 19 | Mr Hi 20 | 21 | 22 | 1 23 | Actor 2 24 | 25 | 26 | 1 27 | Actor 3 28 | 29 | 30 | 1 31 | Actor 4 32 | 33 | 34 | 1 35 | Actor 5 36 | 37 | 38 | 1 39 | Actor 6 40 | 41 | 42 | 1 43 | Actor 7 44 | 45 | 46 | 1 47 | Actor 8 48 | 49 | 50 | 2 51 | Actor 9 52 | 53 | 54 | 2 55 | Actor 10 56 | 57 | 58 | 1 59 | Actor 11 60 | 61 | 62 | 1 63 | Actor 12 64 | 65 | 66 | 1 67 | Actor 13 68 | 69 | 70 | 1 71 | Actor 14 72 | 73 | 74 | 2 75 | Actor 15 76 | 77 | 78 | 2 79 | Actor 16 80 | 81 | 82 | 1 83 | Actor 17 84 | 85 | 86 | 1 87 | Actor 18 88 | 89 | 90 | 2 91 | Actor 19 92 | 93 | 94 | 1 95 | Actor 20 96 | 97 | 98 | 2 99 | Actor 21 100 | 101 | 102 | 1 103 | Actor 22 104 | 105 | 106 | 2 107 | Actor 23 108 | 109 | 110 | 2 111 | Actor 24 112 | 113 | 114 | 2 115 | Actor 25 116 | 117 | 118 | 2 119 | Actor 26 120 | 121 | 122 | 2 123 | Actor 27 124 | 125 | 126 | 2 127 | Actor 28 128 | 129 | 130 | 2 131 | Actor 29 132 | 133 | 134 | 2 135 | Actor 30 136 | 137 | 138 | 2 139 | Actor 31 140 | 141 | 142 | 2 143 | Actor 32 144 | 145 | 146 | 2 147 | Actor 33 148 | 149 | 150 | 2 151 | John A 152 | 153 | 154 | 4 155 | 156 | 157 | 5 158 | 159 | 160 | 3 161 | 162 | 163 | 3 164 | 165 | 166 | 3 167 | 168 | 169 | 3 170 | 171 | 172 | 2 173 | 174 | 175 | 2 176 | 177 | 178 | 2 179 | 180 | 181 | 3 182 | 183 | 184 | 1 185 | 186 | 187 | 3 188 | 189 | 190 | 2 191 | 192 | 193 | 2 194 | 195 | 196 | 2 197 | 198 | 199 | 2 200 | 201 | 202 | 6 203 | 204 | 205 | 3 206 | 207 | 208 | 4 209 | 210 | 211 | 5 212 | 213 | 214 | 1 215 | 216 | 217 | 2 218 | 219 | 220 | 2 221 | 222 | 223 | 2 224 | 225 | 226 | 3 227 | 228 | 229 | 4 230 | 231 | 232 | 5 233 | 234 | 235 | 1 236 | 237 | 238 | 3 239 | 240 | 241 | 2 242 | 243 | 244 | 2 245 | 246 | 247 | 2 248 | 249 | 250 | 3 251 | 252 | 253 | 3 254 | 255 | 256 | 3 257 | 258 | 259 | 2 260 | 261 | 262 | 3 263 | 264 | 265 | 5 266 | 267 | 268 | 3 269 | 270 | 271 | 3 272 | 273 | 274 | 3 275 | 276 | 277 | 3 278 | 279 | 280 | 3 281 | 282 | 283 | 4 284 | 285 | 286 | 2 287 | 288 | 289 | 3 290 | 291 | 292 | 3 293 | 294 | 295 | 2 296 | 297 | 298 | 3 299 | 300 | 301 | 4 302 | 303 | 304 | 1 305 | 306 | 307 | 2 308 | 309 | 310 | 1 311 | 312 | 313 | 3 314 | 315 | 316 | 1 317 | 318 | 319 | 2 320 | 321 | 322 | 3 323 | 324 | 325 | 5 326 | 327 | 328 | 4 329 | 330 | 331 | 3 332 | 333 | 334 | 5 335 | 336 | 337 | 4 338 | 339 | 340 | 2 341 | 342 | 343 | 3 344 | 345 | 346 | 2 347 | 348 | 349 | 7 350 | 351 | 352 | 4 353 | 354 | 355 | 2 356 | 357 | 358 | 4 359 | 360 | 361 | 2 362 | 363 | 364 | 2 365 | 366 | 367 | 4 368 | 369 | 370 | 2 371 | 372 | 373 | 3 374 | 375 | 376 | 3 377 | 378 | 379 | 4 380 | 381 | 382 | 4 383 | 384 | 385 | 5 386 | 387 | 388 | --------------------------------------------------------------------------------