diff --git a/CHANGELOG.md b/CHANGELOG.md index 483ad29..4fa9831 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# task_template x.y.z +# task_spatial_trajectory_inference 1.0.0 ## BREAKING CHANGES @@ -6,28 +6,13 @@ ## NEW FUNCTIONALITY -* Added `control_methods/true_labels` component (PR #5). - -* Added `methods/logistic_regression` component (PR #5). - -* Added `metrics/accuracy` component (PR #5). ## MAJOR CHANGES -* Updated `api` files (PR #5). - -* Updated configs, components and CI to the latest Viash version (PR #8). - -* Updated to Viash 0.9.4 (PR #12). - -* Use dependencies in `openproblems-bio/openproblems` (PR #12). +* Updated `api` files and set the data processor (PR #1). ## MINOR CHANGES -* Updated `README.md` (PR #5). - -* `run_benchmark`: write the commit the workflow ran from and the launch time into - `task_info.yaml`, instead of publishing `_viash.yaml` verbatim (PR #18). ## BUGFIXES diff --git a/_viash.yaml b/_viash.yaml index c171bca..62aeb7a 100644 --- a/_viash.yaml +++ b/_viash.yaml @@ -1,34 +1,28 @@ viash_version: 0.9.4 -# Step 1: Change the name of the task. -# example: task_name_of_this_task -name: task_template +name: task_spatial_trajectory_inference organization: openproblems-bio version: dev license: MIT -# Step 2: Add keywords to describe the task. -keywords: [single-cell, openproblems, benchmark] -# Step 3: Update the `task_template` to the name of the task from step 1. +keywords: [single-cell, openproblems, benchmark, spatial, trajectory inference] links: - issue_tracker: https://github.com/openproblems-bio/task_template/issues - repository: https://github.com/openproblems-bio/task_template + issue_tracker: https://github.com/openproblems-bio/task_spatial_trajectory_inference/issues + repository: https://github.com/openproblems-bio/task_spatial_trajectory_inference docker_registry: ghcr.io -# Step 4: Update the label, summary and description. -# A unique, human-readable, short label. Used for creating summary tables and visualisations. -label: Template -summary: A one sentence summary of purpose and methodology. Used for creating an overview tables. -description: | - Provide a clear and concise description of your task, detailing the specific problem it aims - to solve. Outline the input data types, the expected output, and any assumptions or constraints. - Be sure to explain any terminology or concepts that are essential for understanding the task. +label: Spatial Trajectory Inference +summary: Predicting the spatial trajectory of cells in a tissue based on spatial transcriptomics data. +description: - Explain the motivation behind your proposed task. Describe the biological or computational - problem you aim to address and why it's important. Discuss the current state of research in - this area and any gaps or challenges that your task could help address. This section - should convince readers of the significance and relevance of your task. + Spatial transcriptomics preserves each cell's physical position alongside its expression profile, enabling pseudotime prediction using 2D spatial context. + Different methods make different assumptions about resolution, input format, and output, and are further complicated by spatial batch effects not present in dissociated single-cell data. + While dynbenchmark resolved this comparison problem for non-spatial trajectory inference methods, no equivalent standardized benchmark exists for spatial pseudotime prediction, and existing method papers are validated only against each other or qualitative marker trends rather than tissues with independently known trajectories. + + This task benchmarks pseudotime prediction methods on 2D tissue datasets with well-characterized ground-truth spatial axes. + Input data consists of AnnData objects containing 2D spatial transcriptomics data with spatial coordinates. + The output is a set of quantitative metric scores comparing each method's predicted pseudotime against ground truth, capturing both trajectory accuracy and preservation of relevant biological information. # A list of references to relevant literature. Each reference should be a DOI or a bibtex entry references: @@ -47,32 +41,51 @@ references: info: image: The name of the image file to use for the component on the website. - # Step 5: Replace the task_template to the name of the task. test_resources: - type: s3 path: s3://openproblems-data/resources_test/common/ dest: resources_test/common - type: s3 - path: s3://openproblems-data/resources_test/task_template/ - dest: resources_test/task_template + path: s3://openproblems-data/resources_test/task_spatial_trajectory_inference/ + dest: resources_test/task_spatial_trajectory_inference -# Step 6: Update the authors of the task. authors: - # Full name of the author, usually in the name of FirstName MiddleName LastName. - - name: John Doe - # Role of the author. Possible values: - # - # * `"author"`: Authors who have made substantial contributions to the component. - # * `"maintainer"`: The maintainer of the component. - # * `"contributor"`: Authors who have made smaller contributions (such as code patches etc.). + - name: Chit Tong Lio + roles: [ "author", "maintainer" ] + info: + github: yollct + orcid: 0000-0003-2297-831X + email: chit-tong.lio@tum.de + - name: Seo Hyon Kim + roles: [ "author", "maintainer" ] + info: + github: seohyonkim + orcid: 0009-0007-3062-4681 + email: seohyon.l.kim@gmail.com + - name: Wenjia Zhong + roles: [ "author", "maintainer" ] + info: + github: Wenjia218 + orcid: 0009-0009-1594-8009 + email: wenjia.zhong@tum.de + - name: Luisa Katharina Bender + roles: [ "author", "maintainer" ] + info: + github: luisabender + orcid: 0009-0001-8753-7307 + email: luisa.bender@tum.de + - name: Florian Röckl + roles: [ "author", "maintainer" ] + info: + github: floro5 + orcid: 0009-0009-6050-890X + email: florian.roeckl@tum.de + - name: Jonathan Unger roles: [ "author", "maintainer" ] - # Additional information on the author info: - github: johndoe - orcid: 0000-0000-0000-0000 - email: john@doe.me - twitter: johndoe - linkedin: johndoe + github: Jonique02 + orcid: 0009-0009-5022-2975 + email: jonathan.unger@tum.de # Step 7: Remove all of the comments of the steps you completed diff --git a/src/api/comp_control_method.yaml b/src/api/comp_control_method.yaml index f637aed..36819ed 100644 --- a/src/api/comp_control_method.yaml +++ b/src/api/comp_control_method.yaml @@ -3,7 +3,7 @@ info: type: control_method type_info: label: Control Method - summary: Quality control methods for verifying the pipeline. + summary: Quality control methods for verifying the pipeline of the spatial TI task. description: | This folder contains control components for the task. These components have the same interface as the regular methods @@ -12,12 +12,8 @@ info: the task, and also as a quality control for the metrics defined in the task. arguments: - - name: --input_train - __merge__: file_train.yaml - required: true - direction: input - - name: --input_test - __merge__: file_test.yaml + - name: --dataset + __merge__: file_dataset.yaml required: true direction: input - name: "--input_solution" @@ -33,5 +29,5 @@ test_resources: path: /common/component_tests/run_and_check_output.py - type: python_script path: /common/component_tests/check_config.py - - path: /resources_test/task_template/cxg_mouse_pancreas_atlas - dest: resources_test/task_template/cxg_mouse_pancreas_atlas \ No newline at end of file + - path: /resources_test/common/cxg_mouse_pancreas_atlas + dest: resources_test/common/cxg_mouse_pancreas_atlas \ No newline at end of file diff --git a/src/api/comp_data_processor.yaml b/src/api/comp_data_processor.yaml index 1ed53bd..3447884 100644 --- a/src/api/comp_data_processor.yaml +++ b/src/api/comp_data_processor.yaml @@ -3,20 +3,16 @@ info: type: data_processor type_info: label: Data processor - summary: A data processor. - description: | + summary: A data processor for the spatial TI task. + description: | # better description TODO A component for processing a Common Dataset into a task-specific dataset. arguments: - name: "--input" __merge__: file_common_dataset.yaml direction: input required: true - - name: "--output_train" - __merge__: file_train.yaml - direction: output - required: true - - name: "--output_test" - __merge__: file_test.yaml + - name: "--output_dataset" + __merge__: file_dataset.yaml direction: output required: true - name: "--output_solution" diff --git a/src/api/comp_method.yaml b/src/api/comp_method.yaml index 3a93846..e94c264 100644 --- a/src/api/comp_method.yaml +++ b/src/api/comp_method.yaml @@ -5,16 +5,12 @@ info: label: Method summary: A method. description: | - A method to predict the task effects. + A method for the spatial TI task. arguments: - - name: --input_train - __merge__: file_train.yaml + - name: --input + __merge__: file_dataset.yaml required: true direction: input - - name: "--input_test" - __merge__: file_test.yaml - direction: input - required: true - name: --output __merge__: file_prediction.yaml required: true @@ -24,5 +20,5 @@ test_resources: path: /common/component_tests/run_and_check_output.py - type: python_script path: /common/component_tests/check_config.py - - path: /resources_test/task_template/cxg_mouse_pancreas_atlas - dest: resources_test/task_template/cxg_mouse_pancreas_atlas \ No newline at end of file + - path: /resources_test/common/cxg_mouse_pancreas_atlas + dest: resources_test/common/cxg_mouse_pancreas_atlas \ No newline at end of file diff --git a/src/api/comp_metric.yaml b/src/api/comp_metric.yaml index 1c76a3d..a3ac1d8 100644 --- a/src/api/comp_metric.yaml +++ b/src/api/comp_metric.yaml @@ -3,9 +3,9 @@ info: type: metric type_info: label: Metric - summary: A task template metric. + summary: A metric for spatial TI task. description: | - A metric for evaluating method predictions. + A metric for evaluating spatial TI method predictions. arguments: - name: "--input_solution" __merge__: file_solution.yaml @@ -24,5 +24,5 @@ test_resources: path: /common/component_tests/run_and_check_output.py - type: python_script path: /common/component_tests/check_config.py - - path: /resources_test/task_template/cxg_mouse_pancreas_atlas - dest: resources_test/task_template/cxg_mouse_pancreas_atlas + - path: /resources_test/common/cxg_mouse_pancreas_atlas + dest: resources_test/common/cxg_mouse_pancreas_atlas diff --git a/src/api/file_train.yaml b/src/api/file_dataset.yaml similarity index 69% rename from src/api/file_train.yaml rename to src/api/file_dataset.yaml index c01eda5..7bac9af 100644 --- a/src/api/file_train.yaml +++ b/src/api/file_dataset.yaml @@ -1,24 +1,23 @@ -#TODO: Change to the required and/or optional fields of the anndata type: file -example: "resources_test/task_template/cxg_mouse_pancreas_atlas/train.h5ad" -label: "Training data" -summary: "The training data in h5ad format" +example: "resources_test/task_spatial_trajectory_inference/cxg_mouse_pancreas_atlas/dataset.h5ad" +label: "Input data" +summary: "The input data for the spatial TI methods" info: format: type: h5ad - layers: + layers: - type: integer name: counts description: Raw counts required: true - type: double name: normalized - description: Normalized counts + description: Normalized expression values required: true obs: - type: string - name: label - description: Ground truth cell type labels + name: cell_type + description: Cell type information required: true - type: string name: batch @@ -34,6 +33,10 @@ info: description: A ranking of the features by hvg. required: true obsm: + - type: double + name: X_spatial + description: Spatial coordinates of each cell/spot. + required: true - type: double name: X_pca description: The resulting PCA embedding. @@ -46,4 +49,4 @@ info: - type: string name: normalization_id description: "Which normalization was used" - required: true \ No newline at end of file + required: true diff --git a/src/api/file_solution.yaml b/src/api/file_solution.yaml index d2f6200..3a3e7d6 100644 --- a/src/api/file_solution.yaml +++ b/src/api/file_solution.yaml @@ -1,42 +1,32 @@ -#TODO: Change to the required and/or optional fields of the anndata type: file -example: "resources_test/task_template/cxg_mouse_pancreas_atlas/solution.h5ad" +example: "resources_test/task_spatial_trajectory_inference/cxg_mouse_pancreas_atlas/solution.h5ad" label: "Solution" -summary: "The solution for the test data" +summary: "The solution (ground truth) for the test data" info: format: type: h5ad - layers: + layers: - type: integer name: counts description: Raw counts required: true - - type: double - name: normalized - description: Normalized counts - required: true obs: - type: string - name: label - description: Ground truth cell type labels + name: cell_type + description: Cell type information required: true - type: string name: batch description: Batch information required: true - var: - - type: boolean - name: hvg - description: Whether or not the feature is considered to be a 'highly variable gene' - required: true - type: double - name: hvg_score - description: A ranking of the features by hvg. + name: pseudotime_true + description: Ground truth pseudotime for each cell/spot. required: true obsm: - type: double - name: X_pca - description: The resulting PCA embedding. + name: X_spatial + description: Spatial coordinates of each cell/spot. required: true uns: - type: string @@ -71,3 +61,7 @@ info: name: normalization_id description: "Which normalization was used" required: true + - type: dataframe + name: marker_genes + description: "Marker genes per biological state, with expected ordering along the trajectory (columns: gene_name, biological_state, expected_order). Used by marker-based metrics when provided." + required: false diff --git a/src/api/file_test.yaml b/src/api/file_test.yaml deleted file mode 100644 index cb9d9a6..0000000 --- a/src/api/file_test.yaml +++ /dev/null @@ -1,45 +0,0 @@ -#TODO: Change to the required and/or optional fields of the anndata -type: file -example: "resources_test/task_template/cxg_mouse_pancreas_atlas/test.h5ad" -label: "Test data" -summary: The subset of molecules used for the test dataset -info: - format: - type: h5ad - layers: - - type: integer - name: counts - description: Raw counts - required: true - - type: double - name: normalized - description: Normalized counts - required: true - obs: - - type: string - name: batch - description: Batch information - required: true - var: - - type: boolean - name: hvg - description: Whether or not the feature is considered to be a 'highly variable gene' - required: true - - type: double - name: hvg_score - description: A ranking of the features by hvg. - required: true - obsm: - - type: double - name: X_pca - description: The resulting PCA embedding. - required: true - uns: - - type: string - name: dataset_id - description: "A unique identifier for the dataset" - required: true - - type: string - name: normalization_id - description: "Which normalization was used" - required: true \ No newline at end of file diff --git a/src/data_processors/process_dataset/config.vsh.yaml b/src/data_processors/process_dataset/config.vsh.yaml index 0047ae1..23d4571 100644 --- a/src/data_processors/process_dataset/config.vsh.yaml +++ b/src/data_processors/process_dataset/config.vsh.yaml @@ -1,23 +1,43 @@ __merge__: ../../api/comp_data_processor.yaml name: process_dataset -arguments: - - name: "--method" - type: "string" - description: "The process method to assign train/test." - choices: ["batch", "random"] - default: "batch" - - name: "--obs_label" - type: "string" - description: "Which .obs slot to use as label." - default: "cell_type" - - name: "--obs_batch" - type: "string" - description: "Which .obs slot to use as batch covariate." - default: "batch" - - name: "--seed" - type: "integer" - description: "A seed for the subsampling." - example: 123 +namespace: data_processors + +info: + label: Process dataset + summary: "Filter and split a common dataset into task-specific data and solution files." + +argument_groups: + - name: Parameters + arguments: + - name: "--dataset_id" + type: "string" + description: "New dataset ID" + required: false + - name: "--obs_label" + type: "string" + description: "Which .obs slot to use as label." + default: "cell_type" + - name: "--obs_batch" + type: "string" + description: "Which .obs slot to use as batch covariate." + default: "batch" + - name: "--obs_ptime" + type: "string" + description: "Which .obs slot to use as pseudotime covariate." + default: "pseudotime_true" + - name: "--obsm_spatial" + type: "string" + description: "Which .obsm slot to use as spatial metadata." + default: "X_spatial" + - name: "--layer_counts" + type: "string" + description: "Which .layers slot to use as counts." + default: "counts" + - name: "--seed" + type: "integer" + description: "The seed for." # TODO for what..? + default: 1 + resources: - type: python_script path: script.py @@ -31,4 +51,8 @@ runners: - type: executable - type: nextflow directives: - label: [highmem, midcpu, midtime] \ No newline at end of file + label: [highmem, midcpu, midtime] + +test_resources: + - path: /resources_test/common/cxg_mouse_pancreas_atlas + dest: resources_test/common/cxg_mouse_pancreas_atlas \ No newline at end of file diff --git a/src/data_processors/process_dataset/script.py b/src/data_processors/process_dataset/script.py index 3eb56c2..fd32ad4 100644 --- a/src/data_processors/process_dataset/script.py +++ b/src/data_processors/process_dataset/script.py @@ -7,12 +7,13 @@ ## VIASH START par = { 'input': 'resources_test/common/cxg_mouse_pancreas_atlas/dataset.h5ad', - 'method': 'batch', 'seed': None, 'obs_batch': 'batch', 'obs_label': 'cell_type', - 'output_train': 'train.h5ad', - 'output_test': 'test.h5ad', + 'obs_ptime': 'pseudotime_true', + 'obsm_spatial': 'X_spatial', + 'layer_counts': 'counts', + 'output_dataset': 'dataset.h5ad', 'output_solution': 'solution.h5ad' } meta = { @@ -25,62 +26,51 @@ sys.path.append(meta['resources_dir']) from subset_h5ad_by_format import subset_h5ad_by_format +# read viash config config = op.project.read_viash_config(meta["config"]) # set seed if need be if par["seed"]: - print(f">> Setting seed to {par['seed']}") + print(f">> Setting seed to {par['seed']}", flush=True) random.seed(par["seed"]) +# read the dataset print(">> Load data", flush=True) -adata = ad.read_h5ad(par["input"]) -print("input:", adata) +input = ad.read_h5ad(par['input']) +print("input:", input, flush=True) -print(f">> Process data using {par['method']} method") -if par["method"] == "batch": - batch_info = adata.obs[par["obs_batch"]] - batch_categories = batch_info.dtype.categories - test_batches = random.sample(list(batch_categories), 1) - is_test = [ x in test_batches for x in batch_info ] -elif par["method"] == "random": - train_ix = np.random.choice(adata.n_obs, round(adata.n_obs * 0.8), replace=False) - is_test = [ not x in train_ix for x in range(0, adata.n_obs) ] - -# subset the different adatas -print(">> Figuring which data needs to be copied to which output file", flush=True) -# use par arguments to look for label and batch value in different slots +# map the source slots of the common dataset onto the dest slots expected by the +# task-specific file formats (file_dataset.yaml / file_solution.yaml) slot_mapping = { + "layers": { + "counts": par["layer_counts"], + }, "obs": { - "label": par["obs_label"], + "cell_type": par["obs_label"], "batch": par["obs_batch"], - } + "pseudotime_true": par["obs_ptime"], + }, + "obsm": { + "X_spatial": par["obsm_spatial"], + }, } -print(">> Creating train data", flush=True) -output_train = subset_h5ad_by_format( - adata[[not x for x in is_test]], - config, - "output_train", - slot_mapping -) - -print(">> Creating test data", flush=True) -output_test = subset_h5ad_by_format( - adata[is_test], +print(">> Creating input data for the methods", flush=True) +output_dataset = subset_h5ad_by_format( + input, config, - "output_test", + "output_dataset", slot_mapping ) print(">> Creating solution data", flush=True) output_solution = subset_h5ad_by_format( - adata[is_test], + input, config, "output_solution", slot_mapping ) print(">> Writing data", flush=True) -output_train.write_h5ad(par["output_train"]) -output_test.write_h5ad(par["output_test"]) +output_dataset.write_h5ad(par["output_dataset"]) output_solution.write_h5ad(par["output_solution"])