code: def louvain_clustering(self, X=None, res=1, method='modularity'):
          
  
          if X is None:
              X = self.adata.uns['neighbors']['connectivities']
              save = True
          else:
              if not sp.isspmatrix_csr(X):
                  X = sp.csr_matrix(X)
              save = False
  
          import igraph as ig
          import louvain
  
          adjacency = sparse_knn(X.dot(X.T) / self.k, self.k).tocsr()
          sources, targets = adjacency.nonzero()
          weights = adjacency[sources, targets]
          if isinstance(weights, np.matrix):
              weights = weights.A1
          g = ig.Graph(directed=True)
          g.add_vertices(adjacency.shape[0])
          g.add_edges(list(zip(sources, targets)))
          try:
              g.es['weight'] = weights
          except BaseException:
              pass
  
          if method == 'significance':
              cl = louvain.find_partition(g, louvain.SignificanceVertexPartition)
          else:
              cl = louvain.find_partition(
                  g,
                  louvain.RBConfigurationVertexPartition,
                  resolution_parameter=res)
  
          if save:
              self.adata.obs['louvain_clusters'] = pd.Categorical(np.array(cl.membership))
          else:
              return np.array(cl.membership)
docs: Runs Louvain clustering using the vtraag implementation. Assumes
          that 'louvain' optional dependency is installed.
  
          Parameters
          ----------
          res - float, optional, default 1
              The resolution parameter which tunes the number of clusters Louvain
              finds.
  
          method - str, optional, default 'modularity'
              Can be 'modularity' or 'significance', which are two different
              optimizing funcitons in the Louvain algorithm.