diff --git a/docker/spatial_py/build.env b/docker/spatial_py/build.env index 8c9703d..13ec4df 100644 --- a/docker/spatial_py/build.env +++ b/docker/spatial_py/build.env @@ -1,5 +1,5 @@ # Tool versions -SPATIAL_PY_VERSION=1.0.0 +SPATIAL_PY_VERSION=1.0.1 GCLOUD_CLI_VERSION=524.0.0-slim PYTHON3_VERSION=3.12.5 diff --git a/docker/spatial_py/scripts/cluster b/docker/spatial_py/scripts/cluster index 996adbb..d29f7b8 100755 --- a/docker/spatial_py/scripts/cluster +++ b/docker/spatial_py/scripts/cluster @@ -14,9 +14,16 @@ def main(args): ########################## adata = sc.read_h5ad(args.adata_input) + n_comps = args.n_comps + if adata.n_obs < n_comps or adata.n_vars < n_comps: + n_comps = min(n_comps, adata.n_obs - 1, adata.n_vars - 1) + print(f"[INFO] Detected small dimensions (n_obs={adata.n_obs}, n_vars={adata.n_vars}). Setting n_comps={n_comps}.") + else: + print(f"[INFO] Computing the nearest neighbors distance matrix with default settings (n={n_comps}).") + sc.pp.neighbors( adata, - n_pcs=args.n_comps, + n_pcs=n_comps, ) sc.tl.umap(adata) diff --git a/docker/spatial_py/scripts/visium_plot_spatial b/docker/spatial_py/scripts/visium_plot_spatial index 1211aeb..231d276 100755 --- a/docker/spatial_py/scripts/visium_plot_spatial +++ b/docker/spatial_py/scripts/visium_plot_spatial @@ -15,6 +15,13 @@ def main(args): colors = ["total_counts", "n_genes_by_counts", "batch", "leiden"] titles = [f"{slide} - {color}" for slide in slides for color in colors] + # Clean up + present_slides = adata.obs["visium_slide_ref"].unique() + all_slides = list(adata.uns["spatial"].keys()) + for slide in all_slides: + if slide not in present_slides: + del adata.uns["spatial"][slide] + sq.pl.spatial_scatter( adata, library_key="visium_slide_ref", diff --git a/docker/spatial_py/scripts/visium_process b/docker/spatial_py/scripts/visium_process index a9d8246..8f02854 100755 --- a/docker/spatial_py/scripts/visium_process +++ b/docker/spatial_py/scripts/visium_process @@ -72,9 +72,17 @@ def main(args): ############################## ## DIMENSIONALITY REDUCTION ## ############################## + n_comps = args.n_comps + if hvg_adata.n_obs < n_comps or hvg_adata.n_vars < n_comps: + n_comps = min(n_comps, hvg_adata.n_obs - 1, hvg_adata.n_vars - 1) + print(f"[INFO] Detected small dimensions (n_obs={hvg_adata.n_obs}, n_vars={hvg_adata.n_vars}). Setting n_comps={n_comps}.") + print(f"[WARNING] PCA might not be meaningful since dataset is small (e.g., <30 cells) or check filters.") + else: + print(f"[INFO] Running PCA with default settings (n={n_comps}).") + sc.pp.pca( hvg_adata, - n_comps=args.n_comps, + n_comps=n_comps, svd_solver="arpack", ) diff --git a/docker/spatial_py/scripts/visium_spatially_variable_genes b/docker/spatial_py/scripts/visium_spatially_variable_genes index 98dafc0..4ec1e38 100755 --- a/docker/spatial_py/scripts/visium_spatially_variable_genes +++ b/docker/spatial_py/scripts/visium_spatially_variable_genes @@ -15,26 +15,42 @@ def main(args): adata = sc.read_h5ad(args.adata_input) genes = adata.var_names[adata.var["highly_variable"]].tolist() + + # Check spots per slide + spots_per_slide = adata.obs["visium_slide_ref"].value_counts() + print("Spots per slide:") + print(spots_per_slide) + slides_to_keep = spots_per_slide[spots_per_slide >= 4].index.tolist() + adata_filtered = adata[adata.obs["visium_slide_ref"].isin(slides_to_keep)].copy() + sq.gr.spatial_neighbors( - adata, + adata_filtered, library_key="visium_slide_ref", coord_type="generic", delaunay=True, ) sq.gr.spatial_autocorr( - adata, + adata_filtered, mode="moran", genes=genes, n_perms=100, n_jobs=1, ) - top_10_variable_genes = adata.uns["moranI"].head(10) + top_10_variable_genes = adata_filtered.uns["moranI"].head(10) - top_4_variable_gene_list = adata.uns["moranI"].head(4).index.tolist() - slides = adata.obs["visium_slide_ref"].unique().tolist() + top_4_variable_gene_list = adata_filtered.uns["moranI"].head(4).index.tolist() + slides = adata_filtered.obs["visium_slide_ref"].unique().tolist() titles = [f"{slide} - {color}" for slide in slides for color in top_4_variable_gene_list] + + # Clean up + present_slides = adata_filtered.obs["visium_slide_ref"].unique() + all_slides = list(adata_filtered.uns["spatial"].keys()) + for slide in all_slides: + if slide not in present_slides: + del adata_filtered.uns["spatial"][slide] + sq.pl.spatial_scatter( - adata, + adata_filtered, library_key="visium_slide_ref", color=top_4_variable_gene_list, title=titles, @@ -43,9 +59,9 @@ def main(args): # Save table and adata object top_10_variable_genes.to_csv(f"{args.cohort_id}.moran_top_10_variable_genes.csv") - metadata = adata.obs + metadata = adata_filtered.obs metadata.to_csv(f"{args.cohort_id}.final_metadata.csv") - adata.write_h5ad(filename=args.adata_output, compression="gzip") + adata_filtered.write_h5ad(filename=args.adata_output, compression="gzip") if __name__ == "__main__": diff --git a/wdl-ci.config.json b/wdl-ci.config.json index 704c465..d7b0827 100644 --- a/wdl-ci.config.json +++ b/wdl-ci.config.json @@ -262,7 +262,7 @@ "tasks": { "integrate_sample_data": { "key": "integrate_sample_data", - "digest": "qjngtpgf2tzriftjt4hfwgo2i2z2uucg", + "digest": "hcdomqajbz6ntwc6xeqrsb65q4end4hn", "tests": [ { "inputs": { @@ -276,8 +276,7 @@ "integrated_adata_object": { "value": "${geomx_input_file_path}/${cohort_analysis_workflow_name}/${geomx_team_id}.harmony_integrated.h5ad", "test_tasks": [ - "compare_file_basename", - "check_hdf5" + "compare_file_basename" ] } } @@ -286,7 +285,7 @@ }, "cluster": { "key": "cluster", - "digest": "ibhtiot7sakpfyfuuvpxnqiqmsgzh4hj", + "digest": "frghkwauyalgin75nkrjs466t4vep5uk", "tests": [ { "inputs": { @@ -304,8 +303,7 @@ "clustered_adata_object": { "value": "${geomx_input_file_path}/${cohort_analysis_workflow_name}/${geomx_team_id}.clustered.h5ad", "test_tasks": [ - "compare_file_basename", - "check_hdf5" + "compare_file_basename" ] }, "umap_cluster_plots_png": { @@ -329,7 +327,7 @@ "tasks": { "merge_and_plot_qc_metrics": { "key": "merge_and_plot_qc_metrics", - "digest": "tfku5xa5owqltkkjxr2ibq36iw5fh6hx", + "digest": "6e55pxc6kpkxuyqrthug5cy4xgpirqta", "tests": [ { "inputs": { @@ -369,7 +367,7 @@ }, "filter_and_normalize": { "key": "filter_and_normalize", - "digest": "526uk3dkuil6gmxfyxiltlyvegxbxpki", + "digest": "sfml3i63wsmbm4jtgvfjn32lrchcesaw", "tests": [ { "inputs": { @@ -429,7 +427,7 @@ }, "plot_spatial": { "key": "plot_spatial", - "digest": "ufcwh7kuftyt6nu74cyoawgjvro7lsfv", + "digest": "3pfo5jdbiyz42mvxy6geg4gd7ednbjy3", "tests": [ { "inputs": { @@ -463,7 +461,7 @@ "tasks": { "spatially_variable_gene_analysis": { "key": "spatially_variable_gene_analysis", - "digest": "in5bndajrnrb4kupxqv7xydhlqpnmghy", + "digest": "ysgd246bcuttozfizskpva2frc2tnml2", "tests": [ { "inputs": { @@ -627,7 +625,7 @@ }, "counts_to_adata": { "key": "counts_to_adata", - "digest": "zh45hobarbnoa2z2b6d3i66fdyneqj3w", + "digest": "srukdp6rz3im6dzlcn365i36tv3422yp", "tests": [ { "inputs": { @@ -658,7 +656,7 @@ }, "qc": { "key": "qc", - "digest": "2jfnfowy32wp57i7ctsx7ni5pzzeadbi", + "digest": "s3lt4rtck6w3p7rc6dlav2wda4oz7f7l", "tests": [ { "inputs": { diff --git a/wf-common b/wf-common index d29c543..d96f404 160000 --- a/wf-common +++ b/wf-common @@ -1 +1 @@ -Subproject commit d29c543283f7dbed87e5344412880aa26f421e4e +Subproject commit d96f40474d5880ccb08a37e8372e848b8d5bae6d diff --git a/workflows/integrate_data/integrate_data.wdl b/workflows/integrate_data/integrate_data.wdl index 19235d9..89b3fe6 100644 --- a/workflows/integrate_data/integrate_data.wdl +++ b/workflows/integrate_data/integrate_data.wdl @@ -92,7 +92,7 @@ task integrate_sample_data { } runtime { - docker: "~{container_registry}/spatial_py:1.0.0" + docker: "~{container_registry}/spatial_py:1.0.1" cpu: 4 memory: "~{mem_gb} GB" disks: "local-disk ~{disk_size} HDD" @@ -155,7 +155,7 @@ task cluster { } runtime { - docker: "~{container_registry}/spatial_py:1.0.0" + docker: "~{container_registry}/spatial_py:1.0.1" cpu: 4 memory: "~{mem_gb} GB" disks: "local-disk ~{disk_size} HDD" diff --git a/workflows/spatial_visium/cohort_analysis/cohort_analysis.wdl b/workflows/spatial_visium/cohort_analysis/cohort_analysis.wdl index c7f902c..6a3e69c 100644 --- a/workflows/spatial_visium/cohort_analysis/cohort_analysis.wdl +++ b/workflows/spatial_visium/cohort_analysis/cohort_analysis.wdl @@ -39,7 +39,7 @@ workflow cohort_analysis { } String sub_workflow_name = "cohort_analysis" - String sub_workflow_version = "1.0.0" + String sub_workflow_version = "1.0.1" Array[Array[String]] workflow_info = [[run_timestamp, workflow_name, workflow_version, workflow_release]] @@ -273,7 +273,7 @@ task merge_and_plot_qc_metrics { } runtime { - docker: "~{container_registry}/spatial_py:1.0.0" + docker: "~{container_registry}/spatial_py:1.0.1" cpu: 2 memory: "~{mem_gb} GB" disks: "local-disk ~{disk_size} HDD" @@ -354,7 +354,7 @@ task filter_and_normalize { } runtime { - docker: "~{container_registry}/spatial_py:1.0.0" + docker: "~{container_registry}/spatial_py:1.0.1" cpu: 4 memory: "~{mem_gb} GB" disks: "local-disk ~{disk_size} HDD" @@ -420,7 +420,7 @@ task plot_spatial { } runtime { - docker: "~{container_registry}/spatial_py:1.0.0" + docker: "~{container_registry}/spatial_py:1.0.1" cpu: 2 memory: "~{mem_gb} GB" disks: "local-disk ~{disk_size} HDD" diff --git a/workflows/spatial_visium/cohort_analysis/spatial_statistics/spatial_statistics.wdl b/workflows/spatial_visium/cohort_analysis/spatial_statistics/spatial_statistics.wdl index 9bd2403..47c4b83 100644 --- a/workflows/spatial_visium/cohort_analysis/spatial_statistics/spatial_statistics.wdl +++ b/workflows/spatial_visium/cohort_analysis/spatial_statistics/spatial_statistics.wdl @@ -89,7 +89,7 @@ task spatially_variable_gene_analysis { } runtime { - docker: "~{container_registry}/spatial_py:1.0.0" + docker: "~{container_registry}/spatial_py:1.0.1" cpu: 2 memory: "~{mem_gb} GB" disks: "local-disk ~{disk_size} HDD" diff --git a/workflows/spatial_visium/main.wdl b/workflows/spatial_visium/main.wdl index 190a37e..92cad4e 100644 --- a/workflows/spatial_visium/main.wdl +++ b/workflows/spatial_visium/main.wdl @@ -31,7 +31,7 @@ workflow spatial_visium_analysis { String workflow_execution_path = "workflow_execution" String workflow_name = "spatial_visium" - String workflow_version = "v1.0.0" + String workflow_version = "v1.0.1" String workflow_release = "https://github.com/ASAP-CRN/spatial-transcriptomics-wf/releases/tag/spatial_visium_analysis-~{workflow_version}" call GetWorkflowMetadata.get_workflow_metadata { diff --git a/workflows/spatial_visium/preprocess/preprocess.wdl b/workflows/spatial_visium/preprocess/preprocess.wdl index 8550099..3dbb3f2 100644 --- a/workflows/spatial_visium/preprocess/preprocess.wdl +++ b/workflows/spatial_visium/preprocess/preprocess.wdl @@ -461,7 +461,7 @@ task counts_to_adata { } runtime { - docker: "~{container_registry}/spatial_py:1.0.0" + docker: "~{container_registry}/spatial_py:1.0.1" cpu: 2 memory: "4 GB" disks: "local-disk ~{disk_size} HDD" @@ -526,7 +526,7 @@ task qc { } runtime { - docker: "~{container_registry}/spatial_py:1.0.0" + docker: "~{container_registry}/spatial_py:1.0.1" cpu: threads memory: "~{mem_gb} GB" disks: "local-disk ~{disk_size} HDD"