|
4 | 4 | "cell_type": "markdown", |
5 | 5 | "metadata": {}, |
6 | 6 | "source": [ |
7 | | - "# basic CytoVI workflow" |
| 7 | + "# Basic notebook to train a CytoVI model for the integration of flow cytometry data" |
| 8 | + ] |
| 9 | + }, |
| 10 | + { |
| 11 | + "cell_type": "markdown", |
| 12 | + "metadata": {}, |
| 13 | + "source": [ |
| 14 | + "In the following notebook we demonstrate the basic use of CytoVI and how a CytoVI model can be trained to integrate full spectrum cytometry data from two distinct batches. We first import fcs files and store them in the commonly used anndata format for single cell analyses, preprocess the data and do QC checks, train a CytoVI model and inspect the batch corrected latent space of CytoVI. Consecutively, we cluster the CytoVI latent space, anotate the clusters and compute batch-corrected expression estimates. Subsequently, common downstream analysis tasks can be performed such as quantification the relative abundance of cell populations between groups of samples, differential expression analysis or trajectory inference. For more advanced applications of CytoVI we refer to the reproducibility repository and the documentation. Advanced tutorials will be published with the official release of CytoVI into scvi-tools." |
8 | 15 | ] |
9 | 16 | }, |
10 | 17 | { |
|
25 | 32 | "metadata": {}, |
26 | 33 | "outputs": [], |
27 | 34 | "source": [ |
28 | | - "# read data\n", |
| 35 | + "# read the flow cytometry data of the two batches and store the raw expression values in adata.layers\n", |
29 | 36 | "adata_batch1 = readfcs.read('../data/raw/Spectral flow/Nunez/For Chiquito/Raw_100000/batch1')\n", |
30 | 37 | "adata_batch1.layers['raw'] = adata_batch1.X.copy()\n", |
31 | 38 | "\n", |
|
39 | 46 | "metadata": {}, |
40 | 47 | "outputs": [], |
41 | 48 | "source": [ |
42 | | - "# preprocess\n", |
| 49 | + "# preprocess the raw flow cytometry data; we first perform a hyperbolic arcsin transformation, followed by feature-wise min-max scaling\n", |
| 50 | + "# as a starting point we recommend a global scaling factor of 2000 for full spectrum cytometry data, 100 for conventional flow cytometry data and\n", |
| 51 | + "# 5 for mass cytometry data; the resulting distribution of scaled expresssion can be visualized in scatter plots and histograms (see below)\n", |
43 | 52 | "cytovi.pp.arcsinh(adata_batch1, global_scaling_factor=2000)\n", |
44 | 53 | "cytovi.pp.scale(adata_batch1)\n", |
45 | 54 | "\n", |
|
63 | 72 | "metadata": {}, |
64 | 73 | "outputs": [], |
65 | 74 | "source": [ |
66 | | - "# do some plotting\n", |
67 | | - "cytovi.pl.histogram(adata, marker = 'all', groupby='batch', layer_key='transformed')\n", |
68 | | - "cytovi.pl.biaxial(adata, marker_x = 'CD3', marker_y = 'CD4', groupby='batch', layer_key='transformed')" |
| 75 | + "# inspect the expression values in the scaled layer across the different batches\n", |
| 76 | + "cytovi.pl.histogram(adata, marker = 'all', groupby='batch', layer_key='scaled')\n", |
| 77 | + "cytovi.pl.biaxial(adata, marker_x = 'CD3', marker_y = 'CD4', color='batch', layer_key='scaled')" |
69 | 78 | ] |
70 | 79 | }, |
71 | 80 | { |
|
74 | 83 | "metadata": {}, |
75 | 84 | "outputs": [], |
76 | 85 | "source": [ |
77 | | - "# train model\n", |
| 86 | + "# optional: subsample the anndata in equal proportions for each batch to speed up training for the purpose of this tutorial\n", |
| 87 | + "adata = cytovi.pp.subsample(adata, groupby='batch', n_obs=10000)" |
| 88 | + ] |
| 89 | + }, |
| 90 | + { |
| 91 | + "cell_type": "code", |
| 92 | + "execution_count": null, |
| 93 | + "metadata": {}, |
| 94 | + "outputs": [], |
| 95 | + "source": [ |
| 96 | + "# register the anndata and train the CytoVI model controlling for the 'batch' covariate\n", |
78 | 97 | "cytovi.CytoVI.setup_anndata(adata, layer='scaled', batch_key='batch')\n", |
79 | 98 | "model = cytovi.CytoVI(adata)\n", |
80 | 99 | "model.train()" |
|
86 | 105 | "metadata": {}, |
87 | 106 | "outputs": [], |
88 | 107 | "source": [ |
89 | | - "# save model on disk for later use\n", |
| 108 | + "# optional: save model on disk for later use\n", |
90 | 109 | "model_path = '../saved_models/'\n", |
91 | 110 | "model.save(f'{model_path}my_cytovi_model', overwrite=True)" |
92 | 111 | ] |
|
97 | 116 | "metadata": {}, |
98 | 117 | "outputs": [], |
99 | 118 | "source": [ |
100 | | - "# plot training dynamics\n", |
| 119 | + "# optional: load model from disk\n", |
| 120 | + "model = cytovi.CytoVI.load(f'{model_path}my_cytovi_model', adata=adata)" |
| 121 | + ] |
| 122 | + }, |
| 123 | + { |
| 124 | + "cell_type": "code", |
| 125 | + "execution_count": null, |
| 126 | + "metadata": {}, |
| 127 | + "outputs": [], |
| 128 | + "source": [ |
| 129 | + "# plot training dynamics, note: here we want to observe a plateau of the ELBO\n", |
101 | 130 | "plt.subplot(1, 2, 1)\n", |
102 | 131 | "plt.plot(model.history['elbo_train'])\n", |
103 | 132 | "plt.xlabel('epochs')\n", |
|
106 | 135 | "plt.subplot(1, 2, 2)\n", |
107 | 136 | "plt.plot(model.history['elbo_validation'])\n", |
108 | 137 | "plt.xlabel('epochs')\n", |
109 | | - "plt.ylabel('elbo_validation')" |
| 138 | + "plt.ylabel('elbo_validation')\n", |
| 139 | + "plt.show()" |
110 | 140 | ] |
111 | 141 | }, |
112 | 142 | { |
|
115 | 145 | "metadata": {}, |
116 | 146 | "outputs": [], |
117 | 147 | "source": [ |
118 | | - "# compute umap and cluster CytoVI latent space\n", |
| 148 | + "# compute umap of the CytoVI latent space and cluster the latent space using leiden\n", |
119 | 149 | "adata.obsm[\"X_CytoVI\"] = model.get_latent_representation()\n", |
120 | 150 | "sc.pp.neighbors(adata, use_rep=\"X_CytoVI\")\n", |
121 | 151 | "sc.tl.umap(adata)\n", |
|
128 | 158 | "metadata": {}, |
129 | 159 | "outputs": [], |
130 | 160 | "source": [ |
131 | | - "# plot data\n", |
| 161 | + "# plot the the integrated CytoVI latent space and the corresponding clusters including their expression profile\n", |
132 | 162 | "sc.pl.umap(adata, color=[\"leiden_CytoVI\", \"batch\"])\n", |
133 | | - "sc.pl.matrixplot(adata, var_names=adata.var_names, groupby=\"leiden_CytoVI\")" |
| 163 | + "sc.pl.matrixplot(adata, var_names=adata.var_names, groupby=\"leiden_CytoVI\", layer='scaled', dendrogram=True, standard_scale='var')" |
134 | 164 | ] |
135 | 165 | }, |
136 | 166 | { |
|
139 | 169 | "metadata": {}, |
140 | 170 | "outputs": [], |
141 | 171 | "source": [ |
142 | | - "# annotate cells\n", |
| 172 | + "# annotate the leiden clusters into cell types and visualize on the CytoVI latent space\n", |
143 | 173 | "annot_dict = {\n", |
144 | 174 | " '0': 'Granulocytes',\n", |
145 | 175 | " '1': 'Monocytes',\n", |
|
158 | 188 | "metadata": {}, |
159 | 189 | "outputs": [], |
160 | 190 | "source": [ |
161 | | - "# get imputed expression, note: by default we return the mean across all batches, you can change this by setting the transform_batch argument\n", |
| 191 | + "# get imputed batch-corrected expression; note: by default we return the mean across all batches, you can change this by setting the transform_batch argument\n", |
162 | 192 | "adata.layers['imputed'] = model.get_normalized_expression(adata, n_samples = 10)\n", |
163 | 193 | "\n", |
164 | 194 | "# visualize imputed expression\n", |
165 | 195 | "sc.pl.umap(adata, color = ['CD3', 'CD4', 'CD8', 'CD19', 'CD56'], layer='imputed')" |
166 | 196 | ] |
| 197 | + }, |
| 198 | + { |
| 199 | + "cell_type": "code", |
| 200 | + "execution_count": null, |
| 201 | + "metadata": {}, |
| 202 | + "outputs": [], |
| 203 | + "source": [ |
| 204 | + "# optional: save the anndata\n", |
| 205 | + "adata.write('../data/processed/Nunez_100k_annotated.h5ad')" |
| 206 | + ] |
167 | 207 | } |
168 | 208 | ], |
169 | 209 | "metadata": { |
|
0 commit comments