Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature selection by Moran'I score #110

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/usr/bin/env python

# Author_and_contribution: Qirong Mao; created script

import argparse

# TODO adjust description
parser = argparse.ArgumentParser(description="SVG selection using Squidpy (Moran's I)")

parser.add_argument(
"-c", "--coordinates", help="Path to coordinates (as tsv).", required=True
)
parser.add_argument(
"-m", "--matrix", help="Path to (transformed) counts (as mtx).", required=True
)
parser.add_argument(
"-f", "--features", help="Path to features (as tsv).", required=True
)
parser.add_argument(
"-o", "--observations", help="Path to observations (as tsv).", required=True
)
parser.add_argument(
"-n", "--n_top_genes", help="Number of genes to keep (Default:3000).", required=False, type=int
)

parser.add_argument("-d", "--out_dir", help="Output directory.", required=True)

parser.add_argument(
"--config",
help="Optional config file (json) used to pass additional parameters.",
required=False,
)

args = parser.parse_args()

from pathlib import Path
import pandas as pd

out_dir = Path(args.out_dir)

# Output files
feature_selection_file = out_dir / "features_MoranI.tsv"
# if additional output files are required write it also to out_dir

# Use these filepaths and inputs ...
coord_file = args.coordinates
matrix_file = args.matrix
feature_file = args.features
observation_file = args.observations


if args.n_top_genes is not None:
n_top_genes = int(args.n_top_genes)
Qirongmao97 marked this conversation as resolved.
Show resolved Hide resolved
else:
n_top_genes= 3000 ### Default: 3000 genes

niklasmueboe marked this conversation as resolved.
Show resolved Hide resolved

if args.config is not None:
config_file = args.config


# ... or AnnData if you want
def get_anndata(args):
# Untested template
import anndata as ad
import pandas as pd
import scipy as sp
X = sp.io.mmread(args.matrix)
if sp.sparse.issparse(X):
X = X.tocsr()
observations = pd.read_table(args.observations, index_col=0)
features = pd.read_table(args.features, index_col=0)
coordinates = (
pd.read_table(args.coordinates, index_col=0)
.loc[observations.index, :]
.to_numpy()
)

adata = ad.AnnData(
X=X, obs=observations, var=features, obsm={"spatial": coordinates}
)

return adata


adata = get_anndata(args)

## Your code goes here
import scanpy as sc
import squidpy as sq

## Warning if the input dataset has less features than the number of genes want to keep

n_input_features = len(adata.var)

if n_input_features < n_top_genes:
raise ValueError('Input data has less features than the number of genes you want to keep, please clarify the numbers of genes you need to keep (--n_top_genes)')

features_df = adata.var.copy()

## Log Normalization

sc.pp.normalize_total(adata)
sc.pp.log1p(adata)
niklasmueboe marked this conversation as resolved.
Show resolved Hide resolved

## Compute the graph based from Delaunay triangulation
sq.gr.spatial_neighbors(adata,coord_type="generic",delaunay= True)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if that should be a commandline arg rather than being calculated on the fly. Otherwise we will have situation later where the neighborhood structure used in the method might be different than the one used for feature selection which could be awkward.
@naveedishaque

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For Visium we could just use the default?

For others there are parameters like:
n_neighs ([int])
radius ([int])
delaunay ([bool])

Reference: https://squidpy.readthedocs.io/en/stable/api/squidpy.gr.spatial_neighbors.html

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if that should be a commandline arg rather than being calculated on the fly. Otherwise we will have situation later where the neighborhood structure used in the method might be different than the one used for feature selection which could be awkward.

Oh, this is interesting. We want to make sure that the transformations and neighborhood structure is controlled outside, so technically this would need to import the log1p data and import the neighborhood graph.

Comments:

  • branch name change to "preproc_MoranI_scanpy_Qirongmao"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wondering if that should be a commandline arg rather than being calculated on the fly. Otherwise we will have situation later where the neighborhood structure used in the method might be different than the one used for feature selection which could be awkward.

Oh, this is interesting. We want to make sure that the transformations and neighborhood structure is controlled outside, so technically this would need to import the log1p data and import the neighborhood graph.

Comments:

  • branch name change to "preproc_MoranI_scanpy_Qirongmao"

Then should we need to make a separate pipeline for generating the neighborhood graph?

Branch name with "Vignette 2.0" is also a good option :)

## Calculate Moran's I score for each gene
sq.gr.spatial_autocorr(adata, mode="moran", genes=adata.var_names,show_progress_bar=True)


## Selecting top spatially variable genes based on Moran's I score
SVG = (
adata.uns["moranI"]["I"].sort_values(ascending=False).head(n_top_genes).index.tolist()
)

### Create boolean indication to specify spatially variable genes
adata.var['spatially_variable'] = adata.var.index.isin(SVG)

features_df["spatially_variable"] = adata.var["spatially_variable"]


## Write output
out_dir.mkdir(parents=True, exist_ok=True)
features_df.to_csv(feature_selection_file, sep="\t", index_label="")
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
channels:
- conda-forge
dependencies:
- python=3.9.18
- scanpy=1.9.6
- pip
- pip:
- squidpy==1.3.1