Mercurial > repos > goeckslab > vitessce_spatial
comparison vitessce_spatial.py @ 4:068da7f7cd83 draft
planemo upload for repository https://github.com/goeckslab/tools-mti/tree/main/tools/vitessce commit bc4c0bb6784a55399241f99a29b176541a164a18
| author | goeckslab |
|---|---|
| date | Thu, 20 Feb 2025 19:47:16 +0000 |
| parents | 9f60ef2d586e |
| children | 97be83fc3677 |
comparison
equal
deleted
inserted
replaced
| 3:7cc457aa78b1 | 4:068da7f7cd83 |
|---|---|
| 1 import argparse | 1 import argparse |
| 2 import json | 2 import json |
| 3 import warnings | 3 import warnings |
| 4 from os.path import isdir, join | |
| 4 from pathlib import Path | 5 from pathlib import Path |
| 5 | 6 |
| 6 import scanpy as sc | 7 import scanpy as sc |
| 7 from anndata import read_h5ad | 8 from anndata import read_h5ad |
| 8 from vitessce import ( | 9 from vitessce import ( |
| 10 Component as cm, | 11 Component as cm, |
| 11 MultiImageWrapper, | 12 MultiImageWrapper, |
| 12 OmeTiffWrapper, | 13 OmeTiffWrapper, |
| 13 VitessceConfig, | 14 VitessceConfig, |
| 14 ) | 15 ) |
| 15 | 16 from vitessce.data_utils import ( |
| 16 | 17 optimize_adata, |
| 17 def main(inputs, output, image, anndata=None, masks=None): | 18 VAR_CHUNK_SIZE, |
| 19 ) | |
| 20 | |
| 21 | |
| 22 def main(inputs, output, image, offsets=None, anndata=None, masks=None): | |
| 18 """ | 23 """ |
| 19 Parameter | 24 Parameter |
| 20 --------- | 25 --------- |
| 21 inputs : str | 26 inputs : str |
| 22 File path to galaxy tool parameter. | 27 File path to galaxy inputs config file. |
| 23 output : str | 28 output : str |
| 24 Output folder for saving web content. | 29 Output folder for saving web content. |
| 25 image : str | 30 image : str |
| 26 File path to the OME Tiff image. | 31 File path to the OME Tiff image. |
| 27 anndata : str | 32 anndata : str |
| 32 warnings.simplefilter('ignore') | 37 warnings.simplefilter('ignore') |
| 33 | 38 |
| 34 with open(inputs, 'r') as param_handler: | 39 with open(inputs, 'r') as param_handler: |
| 35 params = json.load(param_handler) | 40 params = json.load(param_handler) |
| 36 | 41 |
| 37 vc = VitessceConfig(name=None, description=None) | 42 # initialize vitessce config and add OME-TIFF image, and masks if specified |
| 43 vc = VitessceConfig(schema_version="1.0.17", name=None, description=None) | |
| 38 dataset = vc.add_dataset() | 44 dataset = vc.add_dataset() |
| 39 image_wrappers = [OmeTiffWrapper(img_path=image, name='OMETIFF')] | 45 |
| 46 # FIXME: grab offsets file for faster display. NEED TO TEST | |
| 47 image_wrappers = [OmeTiffWrapper(img_path=image, offsets_path=offsets, name='OMETIFF')] | |
| 40 if masks: | 48 if masks: |
| 41 image_wrappers.append( | 49 image_wrappers.append( |
| 42 OmeTiffWrapper(img_path=masks, name='MASKS', is_bitmask=True) | 50 OmeTiffWrapper(img_path=masks, name='MASKS', is_bitmask=True) |
| 43 ) | 51 ) |
| 44 dataset.add_object(MultiImageWrapper(image_wrappers)) | 52 dataset.add_object(MultiImageWrapper(image_wrappers)) |
| 45 | 53 |
| 46 status = vc.add_view(dataset, cm.STATUS) | 54 # set relative view sizes (w,h), full window dims are 12x12 |
| 47 spatial = vc.add_view(dataset, cm.SPATIAL) | 55 # if no anndata file, image and layer view can take up whole window |
| 48 lc = vc.add_view(dataset, cm.LAYER_CONTROLLER) | |
| 49 | |
| 50 if not anndata: | 56 if not anndata: |
| 51 vc.layout(status / lc | spatial) | 57 spatial_dims = (9, 12) |
| 52 config_dict = vc.export(to='files', base_url='http://localhost', out_dir=output) | 58 lc_dims = (3, 12) |
| 59 else: | |
| 60 spatial_dims = (6, 6) | |
| 61 lc_dims = (3, 6) | |
| 62 | |
| 63 # add views for the images, and the layer/channels selector | |
| 64 spatial = vc.add_view( | |
| 65 view_type=cm.SPATIAL, | |
| 66 dataset=dataset, | |
| 67 w=spatial_dims[0], | |
| 68 h=spatial_dims[1]) | |
| 69 | |
| 70 lc = vc.add_view( | |
| 71 view_type=cm.LAYER_CONTROLLER, | |
| 72 dataset=dataset, | |
| 73 w=lc_dims[0], | |
| 74 h=lc_dims[1]) | |
| 75 | |
| 76 # if no anndata file, export the config with these minimal components | |
| 77 if not anndata: | |
| 78 vc.layout(lc | spatial) | |
| 79 config_dict = vc.export( | |
| 80 to='files', | |
| 81 base_url='http://localhost', | |
| 82 out_dir=output) | |
| 53 with open(Path(output).joinpath('config.json'), 'w') as f: | 83 with open(Path(output).joinpath('config.json'), 'w') as f: |
| 54 json.dump(config_dict, f, indent=4) | 84 json.dump(config_dict, f, indent=4) |
| 55 return | 85 return |
| 56 | 86 |
| 87 # read anndata file, compute embeddings | |
| 57 adata = read_h5ad(anndata) | 88 adata = read_h5ad(anndata) |
| 58 | 89 |
| 59 params = params['do_phenotyping'] | 90 params = params['do_phenotyping'] |
| 60 embedding = params['scatterplot_embeddings']['embedding'] | 91 embedding = params['scatterplot_embeddings']['embedding'] |
| 61 embedding_options = params['scatterplot_embeddings']['options'] | 92 embedding_options = params['scatterplot_embeddings']['options'] |
| 66 mappings_obsm_name = "UMAP" | 97 mappings_obsm_name = "UMAP" |
| 67 elif embedding == 'tsne': | 98 elif embedding == 'tsne': |
| 68 sc.tl.tsne(adata, **embedding_options) | 99 sc.tl.tsne(adata, **embedding_options) |
| 69 mappings_obsm = 'X_tsne' | 100 mappings_obsm = 'X_tsne' |
| 70 mappings_obsm_name = "tSNE" | 101 mappings_obsm_name = "tSNE" |
| 71 else: # pca | 102 else: |
| 72 sc.tl.pca(adata, **embedding_options) | 103 sc.tl.pca(adata, **embedding_options) |
| 73 mappings_obsm = 'X_pca' | 104 mappings_obsm = 'X_pca' |
| 74 mappings_obsm_name = "PCA" | 105 mappings_obsm_name = "PCA" |
| 75 | 106 |
| 76 adata.obsm['XY_centroid'] = adata.obs[['X_centroid', 'Y_centroid']].values | 107 # Add spatial coords to obsm, although uncertain if this is needed |
| 77 | 108 # FIXME: provide options for alternative coordinate colnames |
| 109 adata.obsm['spatial'] = adata.obs[['X_centroid', 'Y_centroid']].values | |
| 110 | |
| 111 # parse list of obs columns to use as cell type labels | |
| 78 cell_set_obs = params['phenotype_factory']['phenotypes'] | 112 cell_set_obs = params['phenotype_factory']['phenotypes'] |
| 79 if not isinstance(cell_set_obs, list): | 113 if not isinstance(cell_set_obs, list): |
| 80 cell_set_obs = [x.strip() for x in cell_set_obs.split(',')] | 114 cell_set_obs = [x.strip() for x in cell_set_obs.split(',')] |
| 115 | |
| 116 # write anndata out as zarr hierarchy | |
| 117 zarr_filepath = join("data", "adata.zarr") | |
| 118 if not isdir(zarr_filepath): | |
| 119 adata = optimize_adata( | |
| 120 adata, | |
| 121 obs_cols=cell_set_obs, | |
| 122 obsm_keys=[mappings_obsm, 'spatial'], | |
| 123 optimize_X=True | |
| 124 ) | |
| 125 adata.write_zarr( | |
| 126 zarr_filepath, | |
| 127 chunks=[adata.shape[0], VAR_CHUNK_SIZE] | |
| 128 ) | |
| 129 | |
| 130 # create a nicer label for the cell types to be displayed on the dashboard | |
| 81 cell_set_obs_names = [obj[0].upper() + obj[1:] for obj in cell_set_obs] | 131 cell_set_obs_names = [obj[0].upper() + obj[1:] for obj in cell_set_obs] |
| 132 | |
| 133 # add anndata zarr to vitessce config | |
| 82 dataset.add_object( | 134 dataset.add_object( |
| 83 AnnDataWrapper( | 135 AnnDataWrapper( |
| 84 adata, | 136 adata_path=zarr_filepath, |
| 85 mappings_obsm=[mappings_obsm], | 137 obs_feature_matrix_path="X", # FIXME: provide rep options |
| 86 mappings_obsm_names=[mappings_obsm_name], | 138 obs_set_paths=['obs/' + x for x in cell_set_obs], |
| 87 spatial_centroid_obsm='XY_centroid', | 139 obs_set_names=cell_set_obs_names, |
| 88 cell_set_obs=cell_set_obs, | 140 obs_locations_path='spatial', |
| 89 cell_set_obs_names=cell_set_obs_names, | 141 obs_embedding_paths=['obsm/' + mappings_obsm], |
| 90 expression_matrix="X" | 142 obs_embedding_names=[mappings_obsm_name] |
| 91 ) | 143 ) |
| 92 ) | 144 ) |
| 93 | 145 |
| 94 cellsets = vc.add_view(dataset, cm.CELL_SETS) | 146 # add views |
| 95 scattorplot = vc.add_view(dataset, cm.SCATTERPLOT, mapping=mappings_obsm_name) | 147 cellsets = vc.add_view( |
| 96 heatmap = vc.add_view(dataset, cm.HEATMAP) | 148 view_type=cm.OBS_SETS, |
| 97 genes = vc.add_view(dataset, cm.GENES) | 149 dataset=dataset, |
| 98 cell_set_sizes = vc.add_view(dataset, cm.CELL_SET_SIZES) | 150 w=3, |
| 99 cell_set_expression = vc.add_view(dataset, cm.CELL_SET_EXPRESSION) | 151 h=3) |
| 152 | |
| 153 scatterplot = vc.add_view( | |
| 154 view_type=cm.SCATTERPLOT, | |
| 155 dataset=dataset, | |
| 156 mapping=mappings_obsm_name, | |
| 157 w=3, | |
| 158 h=6) | |
| 159 | |
| 160 heatmap = vc.add_view( | |
| 161 view_type=cm.HEATMAP, | |
| 162 dataset=dataset, | |
| 163 w=3, | |
| 164 h=3) | |
| 165 | |
| 166 genes = vc.add_view( | |
| 167 view_type=cm.FEATURE_LIST, | |
| 168 dataset=dataset, | |
| 169 w=3, | |
| 170 h=3) | |
| 171 | |
| 172 cell_set_sizes = vc.add_view( | |
| 173 view_type=cm.OBS_SET_SIZES, | |
| 174 dataset=dataset, | |
| 175 w=3, | |
| 176 h=3) | |
| 177 | |
| 178 cell_set_expression = vc.add_view( | |
| 179 view_type=cm.OBS_SET_FEATURE_VALUE_DISTRIBUTION, | |
| 180 dataset=dataset, | |
| 181 w=3, | |
| 182 h=6) | |
| 183 | |
| 184 # define the dashboard layout | |
| 100 vc.layout( | 185 vc.layout( |
| 101 (status / genes / cell_set_expression) | 186 (cellsets / genes / cell_set_expression) |
| 102 | (cellsets / lc / scattorplot) | 187 | (lc / scatterplot) |
| 103 | (cell_set_sizes / heatmap / spatial) | 188 | (cell_set_sizes / heatmap / spatial) |
| 104 ) | 189 ) |
| 105 config_dict = vc.export(to='files', base_url='http://localhost', out_dir=output) | 190 |
| 191 # export the config file | |
| 192 config_dict = vc.export( | |
| 193 to='files', | |
| 194 base_url='http://localhost', | |
| 195 out_dir=output) | |
| 106 | 196 |
| 107 with open(Path(output).joinpath('config.json'), 'w') as f: | 197 with open(Path(output).joinpath('config.json'), 'w') as f: |
| 108 json.dump(config_dict, f, indent=4) | 198 json.dump(config_dict, f, indent=4) |
| 109 | 199 |
| 110 | 200 |
| 111 if __name__ == '__main__': | 201 if __name__ == '__main__': |
| 112 aparser = argparse.ArgumentParser() | 202 aparser = argparse.ArgumentParser() |
| 113 aparser.add_argument("-i", "--inputs", dest="inputs", required=True) | 203 aparser.add_argument("-i", "--inputs", dest="inputs", required=True) |
| 114 aparser.add_argument("-e", "--output", dest="output", required=True) | 204 aparser.add_argument("-e", "--output", dest="output", required=True) |
| 115 aparser.add_argument("-g", "--image", dest="image", required=True) | 205 aparser.add_argument("-g", "--image", dest="image", required=True) |
| 206 aparser.add_argument("-f", "--offsets", dest="offsets", required=False) | |
| 116 aparser.add_argument("-a", "--anndata", dest="anndata", required=False) | 207 aparser.add_argument("-a", "--anndata", dest="anndata", required=False) |
| 117 aparser.add_argument("-m", "--masks", dest="masks", required=False) | 208 aparser.add_argument("-m", "--masks", dest="masks", required=False) |
| 118 | 209 |
| 119 args = aparser.parse_args() | 210 args = aparser.parse_args() |
| 120 | 211 |
| 121 main(args.inputs, args.output, args.image, args.anndata, args.masks) | 212 main(args.inputs, args.output, args.image, args.offsets, args.anndata, args.masks) |
