Skip to content

Commit

Permalink
Merge pull request #402 from lenatr99/morani_scorer
Browse files Browse the repository at this point in the history
Spatial Autocorrelation Scorer Implementation
  • Loading branch information
lenatr99 authored May 29, 2024
2 parents 9ea1e47 + dd245da commit ca64d5d
Show file tree
Hide file tree
Showing 8 changed files with 393 additions and 25 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions doc/widgets/spatial_autocorrelation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Spatial Autocorrelation Scorer
==============================

Calculate Moran's I or Geary's C spatial autocorrelation score based on input gene expression data.

**Inputs**

- Data: input dataset with spatial coordinates and gene expression values

**Outputs**

- Scorer: an instance of SpatialScorer containing the adjacency matrix and selected method for scoring

**Spatial Autocorrelation Scorer** calculates the spatial autocorrelation score (Moran's I or Geary's C) for each gene in the dataset. The input dataset must include gene expression values in columns and spatial coordinates (x and y) in selected columns. The adjacency matrix is computed using k-nearest neighbors based on the spatial coordinates. The scores are computed independently for each gene.

![](images/SpatialAutocorrelation-stamped.png)


1. Information about the input data.
2. Select X and Y columns that provide spatial coordinates for each cell.
3. Select the method for calculating spatial autocorrelation (Moran I or Geary C) and specify the number of nearest neighbors (k) for constructing the adjacency matrix.
4. Tick to automatically process input data and send the result to the output. If left unchecked, processing must be triggered manually.

Example
-------

We will use [Single Cell Datasets](single_cell_datasets.md) widget to load *Bone marrow mononuclear cells with AML (sample)* data. Then we will pass it through **t-SNE** to get the spatial coordinates of the cells. We will pass the data to the **Spatial Autocorrelation Scorer** widget, select *t-SNE-x* and *t-SNE-y* as spatial coordinates, and calculate Moran's I score with 30 nearest neighbors.

We pass the scorer and the data to **Rank** widget to get the top 10 genes with the highest Moran's I score and visualize the results in **Scatter Plot**.

![](images/SpatialAutocorrelation-Example.png)
50 changes: 50 additions & 0 deletions orangecontrib/single_cell/tests/test_owspatialautocorrelation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import unittest
from Orange.data import Table
from Orange.widgets.tests.base import WidgetTest
from orangecontrib.single_cell.widgets.owspatialautocorrelation import OWSpatialAutocorrelation
from Orange.widgets.utils.concurrent import TaskState


class TestOWSpatialAutocorrelation(WidgetTest):
def setUp(self):
self.widget = self.create_widget(OWSpatialAutocorrelation)

def test_input_data(self):
data = Table("iris")
self.widget.set_data(data)
self.assertEqual(self.widget.data, data)

def test_feature_selection(self):
data = Table("iris")
self.widget.set_data(data)
self.widget.feature_x_combo.setCurrentIndex(1)
self.widget.feature_y_combo.setCurrentIndex(2)
self.assertEqual(self.widget.feature_x, "sepal width")
self.assertEqual(self.widget.feature_y, "petal length")

def test_method_selection(self):
self.widget.method = "Geary C"
self.assertEqual(self.widget.method, "Geary C")

def test_k_neighbors_input(self):
self.widget.k_input.setText("10")
self.widget._on_k_changed()
self.assertEqual(self.widget.k_neighbors, 10)

def test_auto_commit(self):
self.widget.auto_commit = False
self.assertFalse(self.widget.auto_commit)

def test_calculate(self):
data = Table("iris")
self.widget.set_data(data)
self.widget.feature_x_combo.setCurrentIndex(0)
self.widget.feature_y_combo.setCurrentIndex(1)
self.widget.k_input.setText("5")
self.widget._on_k_changed()
self.widget.calculate(TaskState())
self.assertIsNotNone(self.widget.adjacency_matrix)


if __name__ == "__main__":
unittest.main()
38 changes: 38 additions & 0 deletions orangecontrib/single_cell/widgets/icons/SpatialAutocorrelation.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 7 additions & 23 deletions orangecontrib/single_cell/widgets/load_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -488,18 +488,10 @@ def leading_cols(self, value):

def _set_annotation_files(self):
dir_name, _ = os.path.split(self._file_name)
genes_paths = ['genes.tsv', 'features.tsv', 'genes.tsv.gz', 'features.tsv.gz']
for genes_path in genes_paths:
genes_path = os.path.join(dir_name, genes_path)
if os.path.isfile(genes_path):
self.col_annotation_file = RecentPath.create(genes_path, [])
break
barcodes_paths = ['barcodes.tsv', 'barcodes.tsv.gz']
for barcodes_path in barcodes_paths:
barcodes_path = os.path.join(dir_name, barcodes_path)
if os.path.isfile(barcodes_path):
self.row_annotation_file = RecentPath.create(barcodes_path, [])
break
genes_path = os.path.join(dir_name, "genes.tsv")
if os.path.isfile(genes_path):
self.col_annotation_file = RecentPath.create(genes_path, [])
barcodes_path = os.path.join(dir_name, "barcodes.tsv")
if os.path.isfile(barcodes_path):
self.row_annotation_file = RecentPath.create(barcodes_path, [])

Expand Down Expand Up @@ -764,21 +756,13 @@ def key(var):
metas=sorted(metas, key=key))
concat_data_t = concat_data.transform(domain)
data_t = data.transform(domain)

new_values = source_var.values + (source_name,)
new_source_var = DiscreteVariable(source_var.name, values=new_values)
new_metas = tuple(var if var.name != source_var.name else new_source_var for var in domain.metas)
new_domain = Domain(domain.attributes, metas=new_metas)
concat_data_t = concat_data_t.transform(new_domain)
data_t = data_t.transform(new_domain)
source_var_index = new_source_var.values.index(source_name)
source_var.values + (source_name, )
# metas can be unlocked, source_var added to metas by append_source_name
with data_t.unlocked(data_t.metas):
data_t[:, new_source_var] = np.full(
(len(data_t), 1), source_var_index, dtype=object
data_t[:, source_var] = np.full(
(len(data), 1), len(source_var.values) - 1, dtype=object
)
concat_data = Table.concatenate((concat_data_t, data_t), axis=0)
source_var = new_source_var # Update source_var for the next iteration
return concat_data

@staticmethod
Expand Down
2 changes: 0 additions & 2 deletions orangecontrib/single_cell/widgets/owloaddata.py
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,6 @@ def browse_row_annotations(self):
pathitem = RecentPath.create(filename, [])
index = insert_recent_path(m, pathitem)
self.row_annotations_combo.setCurrentIndex(index)
self._row_annotations_combo_changed()
self._invalidate()

@Slot()
Expand All @@ -633,7 +632,6 @@ def browse_col_annotations(self):
pathitem = RecentPath.create(filename, [])
index = insert_recent_path(m, pathitem)
self.col_annotations_combo.setCurrentIndex(index)
self._col_annotations_combo_changed()
self._invalidate()

def _invalidate(self):
Expand Down
Loading

0 comments on commit ca64d5d

Please sign in to comment.