Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f76dc7b
Modularity score functions with comments
amalia-k510 Apr 25, 2025
f092469
typo fix
amalia-k510 Apr 25, 2025
7ffa1ec
Merge branch 'scverse:main' into main
amalia-k510 Apr 25, 2025
c0d0c52
Merge branch 'scverse:main' into main
amalia-k510 May 7, 2025
68652a7
modularity code updated and 6 tests written for modularity
amalia-k510 May 7, 2025
948319a
error fixing from pipelines
amalia-k510 May 7, 2025
6a64330
ruff error fix
amalia-k510 May 7, 2025
793351f
keywords variable fix
amalia-k510 May 7, 2025
92d8e26
neighbors from a precomputed distance matrix, still need to make sure…
amalia-k510 May 7, 2025
198c4fb
revert back
amalia-k510 May 7, 2025
e7fb67a
code only for the prexisting distance matrix
amalia-k510 May 7, 2025
14cb441
initial changes for the neighborhors
amalia-k510 May 8, 2025
0ce8c15
distances name switch and sparse array allowed
amalia-k510 May 12, 2025
914b87d
input fix
amalia-k510 May 12, 2025
d285203
variable input fixes
amalia-k510 May 12, 2025
50705b3
test added
amalia-k510 May 12, 2025
4730667
numpy issue fix for one line
amalia-k510 May 12, 2025
4b9fe3e
avoid densifying sparse matrices
amalia-k510 May 12, 2025
7d754c7
switched to @needs
amalia-k510 May 12, 2025
15320af
switched to @needs
amalia-k510 May 12, 2025
623a86c
variable fix input
amalia-k510 May 12, 2025
e8c9a25
code from separate PR removed
amalia-k510 May 12, 2025
040b8b7
unify metadata assembly
flying-sheep May 16, 2025
d6a9aee
Discard changes to src/scanpy/neighbors/__init__.py
flying-sheep May 16, 2025
c03b863
comments fix and release notes
amalia-k510 May 23, 2025
473a437
comments fix typo
amalia-k510 May 23, 2025
c6e5d1f
Merge branch 'scverse:main' into main
amalia-k510 May 25, 2025
ac0a6b3
before neighbour merge
amalia-k510 May 25, 2025
1c033f0
notes
amalia-k510 May 25, 2025
662534b
Merge branch 'main' of https://github.com/amalia-k510/scanpy into main
amalia-k510 May 25, 2025
32116f0
Merge branch 'matrix_exist' into main
amalia-k510 May 25, 2025
a1b2033
merge error fix
amalia-k510 May 25, 2025
4cdc729
post merge and call form neighbor
amalia-k510 May 25, 2025
cb7aaf6
release notes fix
amalia-k510 May 26, 2025
7e34ce2
Merge branch 'main' into main
flying-sheep May 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/scanpy/metrics/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from __future__ import annotations

from ._gearys_c import gearys_c
from ._metrics import confusion_matrix
from ._metrics import confusion_matrix, modularity, modularity_adata
from ._morans_i import morans_i

__all__ = ["confusion_matrix", "gearys_c", "morans_i"]
__all__ = ["confusion_matrix", "gearys_c", "modularity", "modularity_adata", "morans_i"]
66 changes: 65 additions & 1 deletion src/scanpy/metrics/_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@

from typing import TYPE_CHECKING

import igraph as ig
import numpy as np
import pandas as pd
from anndata import AnnData
from natsort import natsorted
from pandas.api.types import CategoricalDtype

from scanpy._compat import CSRBase

if TYPE_CHECKING:
from collections.abc import Sequence

Expand Down Expand Up @@ -60,7 +64,9 @@ def confusion_matrix(
orig, new = pd.Series(orig), pd.Series(new)
assert len(orig) == len(new)

unique_labels = pd.unique(np.concatenate((orig.values, new.values)))
unique_labels = pd.unique(
np.concatenate((np.asarray(orig.values), np.asarray(new.values)))
)

# Compute
mtx = _confusion_matrix(orig, new, labels=unique_labels)
Expand Down Expand Up @@ -89,3 +95,61 @@ def confusion_matrix(
df = df.loc[np.array(orig_idx), np.array(new_idx)]

return df


def modularity(connectivities, labels, mode="UNDIRECTED") -> float:
# default mode is undirected?? can be specified as directed or undirected
"""Compute the modularity of a graph given its connectivities and labels.

Parameters
----------
connectivities: array-like or sparse matrix
Weighted adjacency matrix representing the graph. Can be a dense NumPy array or a sparse CSR matrix.
labels: array-like or pandas.Series
Cluster labels for each node in the graph.
mode: str
The mode of the graph. Can be "UNDIRECTED" or "DIRECTED". Default is "UNDIRECTED".

Returns
-------
float
The modularity of the graph based on the provided clustering.
"""
if isinstance(connectivities, CSRBase):
# Convert sparse matrix to dense format so that igraph can handle it
# Weighted_Adjacency expects with nested lists or numpy arrays and not sparse matrices
dense_connectivities = connectivities.toarray()
else:
dense_connectivities = connectivities
# creating igraph graph from the dense connectivity matrix
graph = ig.Graph.Weighted_Adjacency(dense_connectivities.tolist(), mode=mode)
if isinstance(labels, pd.Series):
labels = labels.values
# making sure labels are in the right format, i.e., a list of integers
labels = pd.Categorical(np.asarray(labels)).codes
return graph.modularity(labels)


def modularity_adata(adata: AnnData, labels="leiden", obsp="connectivities") -> float:
"""Compute modularity from an AnnData object using stored graph and clustering labels.

Parameters
----------
adata: AnnData
The AnnData object containing the data.
labels: str or array-like
The key in adata.obs that contains the cluster labels.
obsp: str
The key in adata.obsp that contains the connectivities.

Returns
-------
float
The modularity of the graph based on the provided clustering.
"""
# if user passes leiden or louvain as a string, this will get the actual labels
# from adata.obs
label_array = adata.obs[labels] if isinstance(labels, str) else labels
# extracting the connectivities from adata.obsp["connectivities"]
connectivities = adata.obsp[obsp]
return modularity(connectivities, label_array)
Loading