From 80ea606b40ff93ea1df10de670494ee4fe52b590 Mon Sep 17 00:00:00 2001 From: luisabender Date: Wed, 12 Aug 2026 13:58:44 +0200 Subject: [PATCH 1/3] added spearman correlation metric --- src/metrics/spearman_corr/config.vsh.yaml | 104 ++++++++++++++++++++++ src/metrics/spearman_corr/script.py | 74 +++++++++++++++ 2 files changed, 178 insertions(+) create mode 100644 src/metrics/spearman_corr/config.vsh.yaml create mode 100644 src/metrics/spearman_corr/script.py diff --git a/src/metrics/spearman_corr/config.vsh.yaml b/src/metrics/spearman_corr/config.vsh.yaml new file mode 100644 index 0000000..2d1b311 --- /dev/null +++ b/src/metrics/spearman_corr/config.vsh.yaml @@ -0,0 +1,104 @@ +# The API specifies which type of component this is. +# It contains specifications for: +# - The input/output files +# - Common parameters +# - A unit test +__merge__: ../../api/comp_metric.yaml + +# A unique identifier for your component (required). +# Can contain only lowercase letters or underscores. +name: spearman_corr + + + +# Metadata for your component +info: + metrics: + # A unique identifier for your metric (required). + # Can contain only lowercase letters or underscores. + - name: spearman_corr + # A relatively short label, used when rendering visualisarions (required) + label: Spearman's Corr + # A one sentence summary of how this metric works (required). Used when + # rendering summary tables. + summary: "Computes the Spearman rank correlation coefficient between inferred and ground-truth pseudotime." + # A multi-line description of how this component works (required). Used + # when rendering reference documentation. + description: | + Calculates the Spearman rank correlation coefficient (rho) between predicted cell pseudotime + values and the true ground-truth pseudotime. Measures monotonic relationships regardless of linearity. + + references: + doi: + - 10.1038/s41592-020-0772-5 + bibtex: | + @article{Virtanen_2020, + author = {Virtanen, Pauli and Gommers, Ralf and Oliphant, Travis E. and Haberland, Matt and Reddy, Tyler and Cournapeau, David and Burovski, Evgeni and Peterson, Pearu and Weckesser, Warren and Bright, Jonathan and {van der Walt}, St{\'e}fan J. and Brett, Matthew and Wilson, Joshua and Jarrod Millman, K. and Mayorov, Nikolay and Nelson, Andrew R. J. and Jones, Eric and Kern, Robert and Larson, Eric and Carey, C. J. and Polat, {\dot{I}}lhan and Feng, Yu and Moore, Eric W. and VanderPlas, Jake and Laxalde, Denis and Perktold, Josef and Cimrman, Robert and Henriksen, Ian and Quintero, E. A. and Harris, Charles R. and Archibald, Anne M. and Ribeiro, Ant{\^o}nio H. and Pedregosa, Fabian and {van Mulbregt}, Paul and {SciPy 1.0 Contributors}}, + title = {Author Correction: SciPy 1.0: fundamental algorithms for scientific computing in Python}, + journal = {Nature Methods}, + volume = {17}, + number = {3}, + pages = {352}, + year = {2020}, + doi = {10.1038/s41592-020-0772-5} + } + + links: + # URL to the documentation for this metric (required). + documentation: https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.spearmanr.html + # URL to the code repository for this metric (required). + repository: https://github.com/scipy/scipy + # The minimum possible value for this metric (required) + min: -1 + # The maximum possible value for this metric (required) + max: 1 + # Whether a higher value represents a 'better' solution (required) + maximize: true + +# Component-specific parameters (optional) +# arguments: + #- name: "--true_col" + # type: string + #default: "pseudotime_true" + #description: "obs column name for ground-truth pseudotime" +# - name: "--inferred_col" + # type: string + # default: "pseudotime" + # description: "obs column name for inferred pseudotime" + +# Resources required to run the component +resources: + # The script of your component (required) + - type: python_script + path: script.py + # Additional resources your script needs (optional) + # - type: file + # path: weights.pt + +engines: + - type: docker + image: python:3.11-slim + setup: + - type: apt + packages: + - procps + - git + - type: python + packages: + - anndata~=0.10.0 + - scanpy~=1.10.0 + - scipy + - pandas + - pyyaml + - requests + - jsonschema + github: + - "openproblems-bio/core#subdirectory=packages/python/openproblems" + +runners: + # This platform allows running the component natively + - type: executable + # Allows turning the component into a Nextflow module / pipeline. + - type: nextflow + directives: + label: [midtime,midmem,midcpu] diff --git a/src/metrics/spearman_corr/script.py b/src/metrics/spearman_corr/script.py new file mode 100644 index 0000000..cdb3ac1 --- /dev/null +++ b/src/metrics/spearman_corr/script.py @@ -0,0 +1,74 @@ +import anndata as ad +import numpy as np +import pandas as pd +from scipy import stats + +## VIASH START +# Note: this section is auto-generated by viash at runtime. To edit it, make changes +# in config.vsh.yaml and then run `viash config inject config.vsh.yaml`. +par = { + 'input_solution': 'resources_test/task_spatial_trajectory_inference/cxg_mouse_pancreas_atlas/solution.h5ad', + 'input_prediction': 'resources_test/task_spatial_trajectory_inference/cxg_mouse_pancreas_atlas/prediction.h5ad', + 'output': 'output.h5ad', +} +meta = { + 'name': 'spearman_corr' +} +## VIASH END + + +def compute_spearman(true_values, inferred_values): + """Calculates Spearman rank correlation on finite overlapping values.""" + mask = np.isfinite(true_values) & np.isfinite(inferred_values) + + if mask.sum() < 2: + return 0.0 + + rho, _ = stats.spearmanr(true_values[mask], inferred_values[mask]) + + if np.isnan(rho): + return 0.0 + + return float(rho) + +# read input data +print('Reading input files', flush=True) +input_solution = ad.read_h5ad(par['input_solution']) +input_prediction = ad.read_h5ad(par['input_prediction']) + +assert (input_prediction.obs_names == input_solution.obs_names).all(), "obs_names not the same in prediction and solution inputs" + + +# ground truth and predicted pseudotime +TRUE_COL = "pseudotime_true" +INFERRED_COL = "pseudotime_inferred" + +true_vals = pd.to_numeric(input_solution.obs[TRUE_COL], errors='coerce').values +inferred_vals = pd.to_numeric(input_prediction.obs[INFERRED_COL], errors='coerce').values + +# generate results +print('Compute metrics', flush=True) +# metric_ids and metric_values can have length > 1 +# but should be of equal length + +score = compute_spearman(true_vals, inferred_vals) + +uns_metric_ids = [ 'spearman_corr' ] +uns_metric_values = [ score ] + +# Write output data to file +print("Write output AnnData to file...", flush=True) + +output = ad.AnnData( + obs=pd.DataFrame(index=pd.Index(np.array([], dtype=str))), + var=pd.DataFrame(index=pd.Index(np.array([], dtype=str))), + uns={ + 'dataset_id': input_solution.uns.get('dataset_id', 'unknown'), + 'normalization_id': input_solution.uns.get('normalization_id', 'unknown'), + 'method_id': input_prediction.uns.get('method_id', 'unknown'), + 'metric_ids': uns_metric_ids, + 'metric_values': uns_metric_values, + } +) + +output.write_h5ad(par['output'], compression='gzip') From e1b90c5f206d9dbc7d7b8ef00e6f62dd91733db1 Mon Sep 17 00:00:00 2001 From: luisabender Date: Thu, 13 Aug 2026 09:57:42 +0200 Subject: [PATCH 2/3] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43e97b2..fb3d361 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ## NEW FUNCTIONALITY * Added `spaTrack` method (PR #4). +* Added `Spearman's correlation` metric (PR #5). ## MAJOR CHANGES From d6a4e650fc6f153f57d61f5adda424ca9780dd96 Mon Sep 17 00:00:00 2001 From: luisabender Date: Thu, 13 Aug 2026 12:46:13 +0200 Subject: [PATCH 3/3] Remove template boilerplate comments from spearman_corr config Co-Authored-By: Claude Opus 5 (1M context) --- src/metrics/spearman_corr/config.vsh.yaml | 68 +++++------------------ 1 file changed, 13 insertions(+), 55 deletions(-) diff --git a/src/metrics/spearman_corr/config.vsh.yaml b/src/metrics/spearman_corr/config.vsh.yaml index 2d1b311..ad51136 100644 --- a/src/metrics/spearman_corr/config.vsh.yaml +++ b/src/metrics/spearman_corr/config.vsh.yaml @@ -1,79 +1,39 @@ -# The API specifies which type of component this is. -# It contains specifications for: -# - The input/output files -# - Common parameters -# - A unit test __merge__: ../../api/comp_metric.yaml -# A unique identifier for your component (required). -# Can contain only lowercase letters or underscores. name: spearman_corr - - -# Metadata for your component info: metrics: - # A unique identifier for your metric (required). - # Can contain only lowercase letters or underscores. - name: spearman_corr - # A relatively short label, used when rendering visualisarions (required) label: Spearman's Corr - # A one sentence summary of how this metric works (required). Used when - # rendering summary tables. summary: "Computes the Spearman rank correlation coefficient between inferred and ground-truth pseudotime." - # A multi-line description of how this component works (required). Used - # when rendering reference documentation. description: | Calculates the Spearman rank correlation coefficient (rho) between predicted cell pseudotime values and the true ground-truth pseudotime. Measures monotonic relationships regardless of linearity. - references: - doi: + doi: - 10.1038/s41592-020-0772-5 bibtex: | - @article{Virtanen_2020, - author = {Virtanen, Pauli and Gommers, Ralf and Oliphant, Travis E. and Haberland, Matt and Reddy, Tyler and Cournapeau, David and Burovski, Evgeni and Peterson, Pearu and Weckesser, Warren and Bright, Jonathan and {van der Walt}, St{\'e}fan J. and Brett, Matthew and Wilson, Joshua and Jarrod Millman, K. and Mayorov, Nikolay and Nelson, Andrew R. J. and Jones, Eric and Kern, Robert and Larson, Eric and Carey, C. J. and Polat, {\dot{I}}lhan and Feng, Yu and Moore, Eric W. and VanderPlas, Jake and Laxalde, Denis and Perktold, Josef and Cimrman, Robert and Henriksen, Ian and Quintero, E. A. and Harris, Charles R. and Archibald, Anne M. and Ribeiro, Ant{\^o}nio H. and Pedregosa, Fabian and {van Mulbregt}, Paul and {SciPy 1.0 Contributors}}, - title = {Author Correction: SciPy 1.0: fundamental algorithms for scientific computing in Python}, - journal = {Nature Methods}, - volume = {17}, - number = {3}, - pages = {352}, - year = {2020}, - doi = {10.1038/s41592-020-0772-5} - } - + @article{Virtanen_2020, + author = {Virtanen, Pauli and Gommers, Ralf and Oliphant, Travis E. and Haberland, Matt and Reddy, Tyler and Cournapeau, David and Burovski, Evgeni and Peterson, Pearu and Weckesser, Warren and Bright, Jonathan and {van der Walt}, St{\'e}fan J. and Brett, Matthew and Wilson, Joshua and Jarrod Millman, K. and Mayorov, Nikolay and Nelson, Andrew R. J. and Jones, Eric and Kern, Robert and Larson, Eric and Carey, C. J. and Polat, {\dot{I}}lhan and Feng, Yu and Moore, Eric W. and VanderPlas, Jake and Laxalde, Denis and Perktold, Josef and Cimrman, Robert and Henriksen, Ian and Quintero, E. A. and Harris, Charles R. and Archibald, Anne M. and Ribeiro, Ant{\^o}nio H. and Pedregosa, Fabian and {van Mulbregt}, Paul and {SciPy 1.0 Contributors}}, + title = {Author Correction: SciPy 1.0: fundamental algorithms for scientific computing in Python}, + journal = {Nature Methods}, + volume = {17}, + number = {3}, + pages = {352}, + year = {2020}, + doi = {10.1038/s41592-020-0772-5} + } links: - # URL to the documentation for this metric (required). documentation: https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.spearmanr.html - # URL to the code repository for this metric (required). repository: https://github.com/scipy/scipy - # The minimum possible value for this metric (required) min: -1 - # The maximum possible value for this metric (required) max: 1 - # Whether a higher value represents a 'better' solution (required) maximize: true -# Component-specific parameters (optional) -# arguments: - #- name: "--true_col" - # type: string - #default: "pseudotime_true" - #description: "obs column name for ground-truth pseudotime" -# - name: "--inferred_col" - # type: string - # default: "pseudotime" - # description: "obs column name for inferred pseudotime" - -# Resources required to run the component resources: - # The script of your component (required) - type: python_script path: script.py - # Additional resources your script needs (optional) - # - type: file - # path: weights.pt engines: - type: docker @@ -81,8 +41,8 @@ engines: setup: - type: apt packages: - - procps - - git + - procps # required by Nextflow + - git # pip needs it to install openproblems core from git+https - type: python packages: - anndata~=0.10.0 @@ -96,9 +56,7 @@ engines: - "openproblems-bio/core#subdirectory=packages/python/openproblems" runners: - # This platform allows running the component natively - type: executable - # Allows turning the component into a Nextflow module / pipeline. - type: nextflow directives: label: [midtime,midmem,midcpu]