# Details for cluster\_louvain local moving heuristic (for r users)

**URL:** <https://igraph.discourse.group/t/details-for-cluster-louvain-local-moving-heuristic-for-r-users/380>\
**Category:** Usage\
**Tags:** R\
**Created:** [27 July 2020 08:20 UTC](https://igraph.discourse.group/t/details-for-cluster-louvain-local-moving-heuristic-for-r-users/380 "2020-07-27T08:20:59Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![EttoreS](https://avatars.discourse-cdn.com/v4/letter/e/a8b319/32.png) [@EttoreS](https://igraph.discourse.group/u/EttoreS)\
**Post date:** [27 July 2020 08:21 UTC](https://igraph.discourse.group/t/details-for-cluster-louvain-local-moving-heuristic-for-r-users/380/1 "2020-07-27T08:21:00Z")

</div>

Hi

I’d be interested in gaining a better understanding of how cluster\_louvain specifically deals with the local moving heuristics i.e. the first stage of the standard two-step procedure as per Blondel et al. J. Stat. Mech. Theor. Exp. (2008).

Conscious of the following:

1. A detailed description of cluster\_louvain for R users is unavailable, as it relies on functions developed in a C-layer
2. Similar questions have been previously raised in the mailing list and on this forum, namely:  
2.1) This post ([https://lists.nongnu.org/archive/html/igraph-help/2012-09/msg00079.html](https://lists.nongnu.org/archive/html/igraph-help/2012-09/msg00079.html)) raised some concerns about different results obtained by cluster\_louvain and one matlab implementation seemingly ‘endorsed’ by the louvain developers.  
2.2) This post ([Modularity (Q) based on the Louvain split, unexpected values](https://igraph.discourse.group/t/modularity-q-based-on-the-louvain-split-unexpected-values/159/4)) mentions another benchmark for cluster\_louvain, the Brain Connectivity Toolbox, also in matlab - a pretty good match, it seems
3. There are some language-agnostic, useful pseudocodes out there: although with the aim of describing louvain as a benchmark for improved algorithms see e.g.:  
3.1) Traag et al Sci Rep 9, 5233 (2019) - esp. supplementary materials;  
3.2) Waltman, L., van Eck, N.J. Eur. Phys. J. B 86, 471 (2013).

Long story short, none of the above helps me unpick the cluster\_louvain function specifically; nor is it meant chiefly as a resource for R users.  
Obviously, I’ve attempted my own implementation in R form scratch, relying solely on the ‘basics’ as I understand them. It goes without saying I’m getting mixed results: good matching for small toy examples (i.e. yields the same n. of communities and modularity score across partitions), but then my implementation strands from cluster\_louvain with slightly larger examples (not even 200 arcs…).

Grateful if anyone could point me in the right direction re what’s underneath the local moving heuristics in the cluster\_louvain function, and whether that might be made digestible to a C illiterate like myself.

Many thanks,  
Ettore

---

<div class="post-metadata">

**Author:** ![iosonofabio](https://yyz2.discourse-cdn.com/free1/user_avatar/igraph.discourse.group/iosonofabio/32/16_2.png) [@iosonofabio](https://igraph.discourse.group/u/iosonofabio)\
**Post date:** [29 July 2020 06:52 UTC](https://igraph.discourse.group/t/details-for-cluster-louvain-local-moving-heuristic-for-r-users/380/2 "2020-07-29T06:52:50Z")

</div>

Hi Ettore (or ciao),

Welcome to the forum!

Vincent is the first author of Leiden, so he knows the inner workings of Louvain, smart local move, faster versions thereof, and eventually Leiden. He’s on holiday by will be back in a few days.

Since I have a little knowledge too, I’ll attempt to answer you in the meanwhile. I’m not an R user myself, using Python instead, but am happy to help if I can.

First, why are you looking at Louvain in the first place? It’s an old, slow, and flawed algorithm, which is the whole point of the later improved algos. Do you have an actual graph you are trying to cluster and have observed different results with R-igraph and another library?

Second, the original paper and the Leiden paper more recently, but also the SLM paper from 2013 are pretty good at describing how the clustering happens including the first stage. I myself started there and then went down into the Leiden code without too much trouble. I’m not sure if you need an R-specific explanation of the algorithm, R developers should read those papers just fine.

Third, if you have an actual graph you are working on it would be simpler if you could just share it with us, and also the expected result for the clustering and related modularity. That way we could see if it’s an inconsistency in the implementation.

Cheers,  
Fabio

---

<div class="post-metadata">

**Author:** ![EttoreS](https://avatars.discourse-cdn.com/v4/letter/e/a8b319/32.png) [@EttoreS](https://igraph.discourse.group/u/EttoreS)\
**Post date:** [1 August 2020 14:30 UTC](https://igraph.discourse.group/t/details-for-cluster-louvain-local-moving-heuristic-for-r-users/380/3 "2020-08-01T14:30:05Z")

</div>

Ciao Fabio (well spotted…)

Thank you for the heads up - I really appreciate. Below, I try to address the questions you raised in response to mine:

1 - I am only interested in ‘Louvain’ for the sake self-education. I chiefly deploy it to cluster text documents and, not being a developer, I could have happily lived trusting whatever happens to be built into applications such as Gephi, VOS viewer, and obviously igraph. Lately, I’ve been having some issues interpreting apparently small differences between clusters generated by igraph and Gephi (both using Louvain), prompting me to try and replicate what’s going on in there from scratch.

2 - Before I share a fully-fledged graph, it’s enough to consider a very simple example like the one in this post ([https://medium.com/walmartlabs/demystifying-louvains-algorithm-and-its-implementation-in-gpu-9a07cdd3b010](https://medium.com/walmartlabs/demystifying-louvains-algorithm-and-its-implementation-in-gpu-9a07cdd3b010)). I illustrate a ‘dry run’ leading to different results:  
a - blogger answer: A = {1, 2, 3}; B = {4}; C ={5,6,7}; D = {8, 9}  
b - igraph cluster\_louvain answer (modularity: 0.44): A = {1, 2, 3}; B = {4, 5, 6}; C = {7, 8, 9};  
c - my ‘from scratch’ answer (modularity: 0.401):  
---- c.1) first pass: A = {1,4}; B = {2,3}; C = {5,6}; D= {7}; E = {8,9}  
---- c.2) second pass: A = {1,2,3,4}; B = {5,6}; C = {7,8,9}

The core of my query is therefore, how wrong am I in arriving at answer (“c”)? So far, I reasoned as follows re the ‘first pass’:

for each node i  
–set ‘best modularity gain’ to zero  
–for each neighbouring node j’s community c\_j  
------remove node i from the community it is currently assigned to  
------compute the changein modularity due to moving i into c\_j  
------if change in modularity \> best modularity gain  
----------call it c\_j\*  
----------re-assign node i to c\_j\* (until next round)  
----------best modularity gain \<- modularity gain at this iteration  
------else  
----------restore previously assigned community  
–end  
end

I produced a tableau (that cannot be attached - but see picture below) to illustrate how the above pseudo-code runs in my ‘from scratch’ replication, generating answer “c”. One might notice the presence of ‘ties’ on modularity gains in several occasions.

![image](https://global.discourse-cdn.com/free1/uploads/igraph/original/1X/49d977f18feedc61da099cc6377c70ea05b162bc.png)

Since R doens’t seem very popular on here, I spare you the actual R script but obviouly I’m happy to share if sensible.

Hopefully the exapmle above makes enough sense despite the apparent triviality of the issue?

Thanks,  
Ettore

---

<div class="post-metadata">

**Author:** ![EttoreS](https://avatars.discourse-cdn.com/v4/letter/e/a8b319/32.png) [@EttoreS](https://igraph.discourse.group/u/EttoreS)\
**Post date:** [6 August 2020 10:36 UTC](https://igraph.discourse.group/t/details-for-cluster-louvain-local-moving-heuristic-for-r-users/380/5 "2020-08-06T10:36:21Z")

</div>

Apologies for replying to myself - it’s kind of lame, I know.

Eventually, I managed to match the cluster\_louvain results for the small network in the example mentioned in the previous post, by _randomly breaking the ties_.

Sadly, it’s still not quite the fix I was after, since my ‘from scratch’ R code doesn’t quite match yet the output of the cluster\_louvain function when applied to other datasets (being a new user of this forum, I am not allowed to upload).

See picture below for a couple of experiments in which I shuffle the nodes visiting order for 100 and 200 times. In the second experiment I get very close to cluster\_louvain guessing both the number of communities (7) and approaching the modularity metric (0.39 - ish) althought not matching the allocation (see off-diagonal elements in the lhs chart)

 ![image](https://global.discourse-cdn.com/free1/uploads/igraph/original/1X/c08de99c6ed7221931816e4d3305b8f72a1d721a.png)

Based on the above I suspect that, just like the ‘random’ breaking of ties, the order in which the nodes are visited may play a role when it comes to replicating the output of cluster\_louvain.

Perhaps someone in here could advise re whether cluster\_louvain has a preferred/recommended approach to randomise the nodes visited? For the uninitiated it’s hard to appreciate this aspect (just as well as the random breaking of ties) straight from the C-layer coding of the function.

Many thanks,  
Ettore

---

<div class="post-metadata">

**Author:** ![EttoreS](https://avatars.discourse-cdn.com/v4/letter/e/a8b319/32.png) [@EttoreS](https://igraph.discourse.group/u/EttoreS)\
**Post date:** [26 August 2020 00:21 UTC](https://igraph.discourse.group/t/details-for-cluster-louvain-local-moving-heuristic-for-r-users/380/6 "2020-08-26T00:21:13Z")

</div>

I thought I’d close off this self-talk by pointing out to a possible solution - in case it benfits other “dummies” like myself.

- There is indeed an R version of the BCT’s implementaton of Louvain ([https://rdrr.io/github/AlexChristensen/NetworkToolbox/src/R/louvain.R](https://rdrr.io/github/AlexChristensen/NetworkToolbox/src/R/louvain.R)). Unlike cluster\_louvain, this one is not developed in a C-layer. However, a quirk of this implementation is its way of pre-calculating bits and bolbs of the modularity equation - hence, at first, it might not lend itself to an easy interpretation.

- The code above helped me realise the point I was missing i.e., the first pass _must be ‘wrapped’ in a loop and iterated a number of times before ‘compacting’ the network_. Unaware of this, I was erroneously passing the output of the first pass on to the second too early, then subconsciously attempting to make up for this by perturbing the whole thing a few hundred times…

At this point it is worth underlying that missing the loop mentioned at point 2 above might sound unbelievably silly to those in the know; yet, it does not easily come across in top-level descriptions of the algorithm - see e.g., how the first pass is worded in [https://www.r-bloggers.com/community-detection-with-louvain-and-infomap/](https://www.r-bloggers.com/community-detection-with-louvain-and-infomap/)

For the records, I include a picture of how my ‘from scratch’ R implementation (which now ‘wraps’ the first pass in a loop…) performs on the same dataset mentioned in my previous post compared to cluster\_louvain and the BCT-inspired ‘louvain’ function in R’s package Network Toolbox: it can’t do as good as cluster\_louvain, but at least it is now in the right ballpark…

 ![Comparison Louvain tools - 25 august 2020](https://global.discourse-cdn.com/free1/uploads/igraph/original/1X/a7073d9f6881c260acb5c4c14334a0d801d036d3.png)

Thanks for bearing with me,  
E.

---

<div class="post-metadata">

**Author:** ![vtraag](https://yyz2.discourse-cdn.com/free1/user_avatar/igraph.discourse.group/vtraag/32/38_2.png) [@vtraag](https://igraph.discourse.group/u/vtraag)\
**Post date:** [1 September 2020 20:51 UTC](https://igraph.discourse.group/t/details-for-cluster-louvain-local-moving-heuristic-for-r-users/380/7 "2020-09-01T20:51:12Z")

</div>

Great to see you figured everything out!

I just wanted to point out that a relatively high-level replication of the Louvain algorithm can be implemented relatively easily using my `leidenalg` package. Unfortunately it is only available in Python, but it might be instructive to explore it. See the [documentation](https://leidenalg.readthedocs.io/en/stable/advanced.html#optimiser) for the details. For example, the Louvain algorithm is essentially this (where `la` refers to the `leidenalg` package):

```auto
>>> partition = la.ModularityVertexPartition(G)
>>> while optimiser.move_nodes(partition) > 0:
... partition = partition.aggregate_partition()

```

Essentially, the `move_nodes` function (which you also explored in some detail) can be replicated by doing something like

```auto
>>> for v in G.vs:
... best_comm = max(range(len(partition)),
... key=lambda c: partition.diff_move(v.index, c))
... partition.move_node(v.index, best_comm)

```

---

<div class="post-metadata">

**Author:** ![EttoreS](https://avatars.discourse-cdn.com/v4/letter/e/a8b319/32.png) [@EttoreS](https://igraph.discourse.group/u/EttoreS)\
**Post date:** [1 September 2020 21:27 UTC](https://igraph.discourse.group/t/details-for-cluster-louvain-local-moving-heuristic-for-r-users/380/8 "2020-09-01T21:27:57Z")

</div>

Thanks Vincent for the heads up - I see how those excerpts can be a useful complement to the pseudocode available in your Sci. Rep. paper. Would be good to take a closer look at it if I manage ot pick up Python at some stage…

Speaking of your Leiden algorithm - whilst I am no devleoper, it was nevertheless instructive to stumble on your critique of Louvain. If anything, it’s good for the ‘average’ user to be aware of its limitations.
