Introduction
Introduction
Spectre's simple spatial analysis workflow is intended to provide a straightforward method of spatial analysis for high-dimensional imaging analysis. Critically, this workflow does not include spatially-classified cell phenotypes or tissue regions, but largely relies on the labelling of cell types by annotating clusters, and then assessing changes in these cells across images/ROIs. For more comprehensive spatial analysis approaches, including regional composition etc, see our advanced spatial analysis workflow.
Overview
This workflow is broken up into three stages, managed by three R scripts:
- Add masks
- Cellular analysis (clustering, dimensionality reduction)
- Spatial analysis
Following stage 1 (add masks), FCS files are also generated that can be used in FlowJo for analysis if desired.
Setup
For this workflow, you will need to use Spectre v0.5.0 or higher. For installation and update instructions, as well as introductory tutorials, see this page. Alternatively, you can check out our spatial analysis protocols in FlowJo.
Script 1: add masks and extract cellular data
Load packages and set directories
x
################################################################################### ### Spectre: spatial 1 - add masks and extract cellular data ###################################################################################
### Load libraries library('Spectre')
Spectre::package.check(type = 'spatial') Spectre::package.load(type = 'spatial')
### Set PrimaryDirectory dirname(rstudioapi::getActiveDocumentContext()$path) # Finds the directory where this script is located setwd(dirname(rstudioapi::getActiveDocumentContext()$path)) # Sets the working directory to where the script is located getwd() PrimaryDirectory <- getwd() PrimaryDirectory
### Set InputDirectory (ROI TIFFs) setwd(PrimaryDirectory) setwd("data/ROIs/") InputDirectory <- getwd() InputDirectory
### Set MaskDirectory (ROI mask TIFFs) setwd(PrimaryDirectory) setwd("data/masks/") MaskDirectory <- getwd() MaskDirectory
### Create output directory setwd(PrimaryDirectory) dir.create("Output 1 - add masks") setwd("Output 1 - add masks") OutputDirectory <- getwd() OutputDirectory
Check TIFFs
x
################################################################################### ### Check ROIs and TIFFs ###################################################################################
### Initialise the spatial data object with channel TIFF files setwd(InputDirectory) rois <- list.dirs(full.names = FALSE, recursive = FALSE) as.matrix(rois)
[,1] [1,] "ROI002" [2,] "ROI004" [3,] "ROI006" [4,] "ROI008" [5,] "ROI010" [6,] "ROI012"
tiff.list <- list()
### Check channel names for(i in rois){ setwd(InputDirectory) setwd(i) tiff.list[[i]] <- list.files(getwd()) }
t(as.data.frame(tiff.list))
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10] [,11] [,12] ROI002 "140Ce_Ce140.tiff" "80ArAr_ArAr80.tiff" "alphaSMA_Pr141.tiff" "CD11b_Sm149.tiff" "CD20_Dy161.tiff" "CD3_Er170.tiff" "CD4_Gd156.tiff" "CD45_Sm152.tiff" "CD68_Tb159.tiff" "CD8a_Dy162.tiff" "CollagenI_Tm169.tiff" "DNA1_Ir191.tiff" ROI004 "140Ce_Ce140.tiff" "80ArAr_ArAr80.tiff" "alphaSMA_Pr141.tiff" "CD11b_Sm149.tiff" "CD20_Dy161.tiff" "CD3_Er170.tiff" "CD4_Gd156.tiff" "CD45_Sm152.tiff" "CD68_Tb159.tiff" "CD8a_Dy162.tiff" "CollagenI_Tm169.tiff" "DNA1_Ir191.tiff" ROI006 "140Ce_Ce140.tiff" "80ArAr_ArAr80.tiff" "alphaSMA_Pr141.tiff" "CD11b_Sm149.tiff" "CD20_Dy161.tiff" "CD3_Er170.tiff" "CD4_Gd156.tiff" "CD45_Sm152.tiff" "CD68_Tb159.tiff" "CD8a_Dy162.tiff" "CollagenI_Tm169.tiff" "DNA1_Ir191.tiff" ROI008 "140Ce_Ce140.tiff" "80ArAr_ArAr80.tiff" "alphaSMA_Pr141.tiff" "CD11b_Sm149.tiff" "CD20_Dy161.tiff" "CD3_Er170.tiff" "CD4_Gd156.tiff" "CD45_Sm152.tiff" "CD68_Tb159.tiff" "CD8a_Dy162.tiff" "CollagenI_Tm169.tiff" "DNA1_Ir191.tiff" ROI010 "140Ce_Ce140.tiff" "80ArAr_ArAr80.tiff" "alphaSMA_Pr141.tiff" "CD11b_Sm149.tiff" "CD20_Dy161.tiff" "CD3_Er170.tiff" "CD4_Gd156.tiff" "CD45_Sm152.tiff" "CD68_Tb159.tiff" "CD8a_Dy162.tiff" "CollagenI_Tm169.tiff" "DNA1_Ir191.tiff" ROI012 "140Ce_Ce140.tiff" "80ArAr_ArAr80.tiff" "alphaSMA_Pr141.tiff" "CD11b_Sm149.tiff" "CD20_Dy161.tiff" "CD3_Er170.tiff" "CD4_Gd156.tiff" "CD45_Sm152.tiff" "CD68_Tb159.tiff" "CD8a_Dy162.tiff" "CollagenI_Tm169.tiff" "DNA1_Ir191.tiff" [,13] [,14] [,15] ROI002 "DNA3_Ir193.tiff" "HistoneH3_Yb176.tiff" "VIM_Nd143.tiff" ROI004 "DNA3_Ir193.tiff" "HistoneH3_Yb176.tiff" "VIM_Nd143.tiff" ROI006 "DNA3_Ir193.tiff" "HistoneH3_Yb176.tiff" "VIM_Nd143.tiff" ROI008 "DNA3_Ir193.tiff" "HistoneH3_Yb176.tiff" "VIM_Nd143.tiff" ROI010 "DNA3_Ir193.tiff" "HistoneH3_Yb176.tiff" "VIM_Nd143.tiff" ROI012 "DNA3_Ir193.tiff" "HistoneH3_Yb176.tiff" "VIM_Nd143.tiff"
Import channel TIFFs
x
################################################################################### ### Read in TIFF files and create spatial objects ###################################################################################
### Read in ROI channel TIFFs setwd(InputDirectory) spatial.dat <- read.spatial.files(rois = rois, roi.loc = getwd())
### Check results str(spatial.dat, 3)
List of 6 $ ROI002:List of 1 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots $ ROI004:List of 1 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots $ ROI006:List of 1 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots $ ROI008:List of 1 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots $ ROI010:List of 1 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots $ ROI012:List of 1 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots
spatial.dat[[1]]$RASTERS
class : RasterStack dimensions : 501, 500, 250500, 15 (nrow, ncol, ncell, nlayers) resolution : 1, 1 (x, y) extent : 0, 500, 0, 501 (xmin, xmax, ymin, ymax) crs : NA names : X140Ce_Ce140f, X80ArAr_ArAr80f, alphaSMA_Pr141f, CD11b_Sm149f, CD20_Dy161f, CD3_Er170f, CD4_Gd156f, CD45_Sm152f, CD68_Tb159f, CD8a_Dy162f, CollagenI_Tm169f, DNA1_Ir191f, DNA3_Ir193f, HistoneH3_Yb176f, VIM_Nd143f min values : 0, 3399, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 max values : 443, 5297, 1231, 33, 779, 41, 40, 734, 1223, 109, 183, 1670, 3007, 3167, 7764
Import masks
x
################################################################################### ### Read in masks files ###################################################################################
### Define cell mask extension for different mask types setwd(MaskDirectory) all.masks <- list.files(pattern = '.tif') as.matrix(all.masks)
[,1] [1,] "ROI002_Cell_mask.tiff" [2,] "ROI002_Cytoplasm_mask.tiff" [3,] "ROI002_Nuclei_mask.tiff" [4,] "ROI004_Cell_mask.tiff" [5,] "ROI004_Cytoplasm_mask.tiff" [6,] "ROI004_Nuclei_mask.tiff" [7,] "ROI006_Cell_mask.tiff" [8,] "ROI006_Cytoplasm_mask.tiff" [9,] "ROI006_Nuclei_mask.tiff" [10,] "ROI008_Cell_mask.tiff" [11,] "ROI008_Cytoplasm_mask.tiff" [12,] "ROI008_Nuclei_mask.tiff" [13,] "ROI010_Cell_mask.tiff" [14,] "ROI010_Cytoplasm_mask.tiff" [15,] "ROI010_Nuclei_mask.tiff" [16,] "ROI012_Cell_mask.tiff" [17,] "ROI012_Cytoplasm_mask.tiff" [18,] "ROI012_Nuclei_mask.tiff"
### Import CELL masks as.matrix(all.masks) cell.mask.ext <- '_Cell_mask.tiff' cell.masks <- list.files(pattern = cell.mask.ext) as.matrix(cell.masks)
[,1] [1,] "ROI002_Cell_mask.tiff" [2,] "ROI004_Cell_mask.tiff" [3,] "ROI006_Cell_mask.tiff" [4,] "ROI008_Cell_mask.tiff" [5,] "ROI010_Cell_mask.tiff" [6,] "ROI012_Cell_mask.tiff"
spatial.dat <- do.add.masks(spatial.dat = spatial.dat, mask.loc = getwd(), masks = cell.masks, mask.label = 'cell.mask', mask.ext = cell.mask.ext)
Adding masks to cell.mask -- processing ROI002 ... correcting extent ... flipping Y -- processing ROI004 ... correcting extent ... flipping Y -- processing ROI006 ... correcting extent ... flipping Y -- processing ROI008 ... correcting extent ... flipping Y -- processing ROI010 ... correcting extent ... flipping Y -- processing ROI012 ... correcting extent ... flipping Y Returning spatial data object with added masks
### Review data str(spatial.dat, 3)
List of 6 $ ROI002:List of 2 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots ..$ MASKS :List of 1 .. ..$ cell.mask:List of 1 $ ROI004:List of 2 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots ..$ MASKS :List of 1 .. ..$ cell.mask:List of 1 $ ROI006:List of 2 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots ..$ MASKS :List of 1 .. ..$ cell.mask:List of 1 $ ROI008:List of 2 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots ..$ MASKS :List of 1 .. ..$ cell.mask:List of 1 $ ROI010:List of 2 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots ..$ MASKS :List of 1 .. ..$ cell.mask:List of 1 $ ROI012:List of 2 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots ..$ MASKS :List of 1 .. ..$ cell.mask:List of 1
spatial.dat[[1]]$MASKS
$cell.mask $cell.mask$maskraster class : RasterLayer dimensions : 501, 500, 250500 (nrow, ncol, ncell) resolution : 1, 1 (x, y) extent : 0, 500, 0, 501 (xmin, xmax, ymin, ymax) crs : NA source : memory names : cell.mask values : 0, 65535 (min, max)
Create polygons for cell
x
################################################################################### ### Generate polygons and outlines ###################################################################################
### Generate polygons and outlines spatial.dat <- do.create.outlines(spatial.dat, 'cell.mask')
Creating polygons, outlines, and centroids using 'stars' method. Processing masks for ROI ROI002 although coordinates are longitude/latitude, st_union assumes that they are planar although coordinates are longitude/latitude, st_union assumes that they are planar although coordinates are longitude/latitude, st_union assumes that they are planar ... ... polygons complete Regions defined for each Polygons ... outlines complete ... centroids complete Returning spatial data
### Checks str(spatial.dat, 3)
List of 6 $ ROI002:List of 2 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots ..$ MASKS :List of 1 .. ..$ cell.mask:List of 4 $ ROI004:List of 2 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots ..$ MASKS :List of 1 .. ..$ cell.mask:List of 4 $ ROI006:List of 2 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots ..$ MASKS :List of 1 .. ..$ cell.mask:List of 4 $ ROI008:List of 2 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots ..$ MASKS :List of 1 .. ..$ cell.mask:List of 4 $ ROI010:List of 2 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots ..$ MASKS :List of 1 .. ..$ cell.mask:List of 4 $ ROI012:List of 2 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots ..$ MASKS :List of 1 .. ..$ cell.mask:List of 4
spatial.dat[[1]]$MASKS
$cell.mask $cell.mask$maskraster class : RasterLayer dimensions : 501, 500, 250500 (nrow, ncol, ncell) resolution : 1, 1 (x, y) extent : 0, 500, 0, 501 (xmin, xmax, ymin, ymax) crs : NA source : memory names : cell.mask values : 0, 65535 (min, max) $cell.mask$polygons class : SpatialPolygonsDataFrame features : 2750 extent : 0, 500, 0, 501 (xmin, xmax, ymin, ymax) crs : NA variables : 1 names : cell.mask min values : 0 max values : 65535 $cell.mask$outlines long lat order hole piece id group 1 19 103 1 FALSE 1 1 1.1 2 19 104 2 FALSE 1 1 1.1 3 20 104 3 FALSE 1 1 1.1
Mask QC plots
x
################################################################################### ### Mask QC plots ###################################################################################
### Mask plots setwd(OutputDirectory) dir.create('Plots - cell masks') setwd('Plots - cell masks')
as.matrix(names(spatial.dat[[1]]$RASTERS)) base <- 'DNA1_Ir191' mask <- 'cell.mask'
for(i in names(spatial.dat)){ make.spatial.plot(spatial.dat = spatial.dat, image.roi = i, image.channel = base, mask.outlines = 'cell.mask') }
Calculate 'per cell' data from masks
x
################################################################################### ### Extract 'per cell' data ###################################################################################
### Extract cellular data for each cell mask spatial.dat <- do.extract(spatial.dat, 'cell.mask')
Processing ROI002 ... X140Ce_Ce140f |==============================================================================================================| 100% ... X80ArAr_ArAr80f |==============================================================================================================| 100% ... alphaSMA_Pr141f |==============================================================================================================| 100% ...
str(spatial.dat, 3)
List of 6 $ ROI002:List of 3 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots ..$ MASKS :List of 1 .. ..$ cell.mask:List of 4 ..$ DATA :List of 1 .. ..$ CellData:Classes ‘data.table’ and 'data.frame': 2750 obs. of 19 variables: .. .. ..- attr(*, ".internal.selfref")=<externalptr> $ ROI004:List of 3 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots ..$ MASKS :List of 1 .. ..$ cell.mask:List of 4 ..$ DATA :List of 1 .. ..$ CellData:Classes ‘data.table’ and 'data.frame': 4205 obs. of 19 variables: .. .. ..- attr(*, ".internal.selfref")=<externalptr> $ ROI006:List of 3 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots ..$ MASKS :List of 1 .. ..$ cell.mask:List of 4 ..$ DATA :List of 1 .. ..$ CellData:Classes ‘data.table’ and 'data.frame': 4200 obs. of 19 variables: .. .. ..- attr(*, ".internal.selfref")=<externalptr> $ ROI008:List of 3 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots ..$ MASKS :List of 1 .. ..$ cell.mask:List of 4 ..$ DATA :List of 1 .. ..$ CellData:Classes ‘data.table’ and 'data.frame': 1685 obs. of 19 variables: .. .. ..- attr(*, ".internal.selfref")=<externalptr> $ ROI010:List of 3 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots ..$ MASKS :List of 1 .. ..$ cell.mask:List of 4 ..$ DATA :List of 1 .. ..$ CellData:Classes ‘data.table’ and 'data.frame': 1301 obs. of 19 variables: .. .. ..- attr(*, ".internal.selfref")=<externalptr> $ ROI012:List of 3 ..$ RASTERS:Formal class 'RasterStack' [package "raster"] with 11 slots ..$ MASKS :List of 1 .. ..$ cell.mask:List of 4 ..$ DATA :List of 1 .. ..$ CellData:Classes ‘data.table’ and 'data.frame': 1427 obs. of 19 variables: .. .. ..- attr(*, ".internal.selfref")=<externalptr>
Save spatial data object
x
################################################################################### ### Save spatial data object as qs file ###################################################################################
### Output setwd(OutputDirectory) dir.create('QS file') setwd('QS file') qsave(spatial.dat, "spatial.dat.qs")
Write FCS files
x
################################################################################### ### Write FCS files ###################################################################################
### Pull cellular data cell.dat <- do.pull.data(spatial.dat, 'CellData') cell.dat
ROI ID x y Area X140Ce_Ce140f X80ArAr_ArAr80f alphaSMA_Pr141f CD11b_Sm149f CD20_Dy161f ... 1: ROI002 1 276.24140 218.403834 100982 0.11077221 4291.782 9.292290 0.3623715 6.8142638 ... 2: ROI002 2 222.54000 3.200000 50 0.06000000 4143.240 8.840000 0.2800000 1.9000000 ... 3: ROI002 3 247.44643 3.660714 56 0.03571429 4150.036 3.392857 0.3928571 0.4464286 ... 4: ROI002 4 333.27941 4.250000 68 0.08823530 4184.838 5.720588 0.3529412 1.2205882 ... 5: ROI002 5 388.37755 4.785714 49 0.08163265 4123.775 2.102041 0.1428571 0.5714286 ... --- 15564: ROI012 1423 216.41892 494.986486 37 0.05405406 4016.324 64.027023 0.2972973 0.2972973 ... 15565: ROI012 1424 59.82099 495.919753 81 0.16049382 4092.963 5.938272 0.3333333 1.0000000 ... 15566: ROI012 1425 432.20732 495.731707 82 0.08536585 4042.378 9.524390 0.6829268 0.8780488 ... 15567: ROI012 1426 397.91463 495.548780 41 0.00000000 4051.805 8.341463 0.3170732 0.5121951 ... 15568: ROI012 1427 420.92105 496.578947 38 0.13157895 4051.763 4.315790 0.2105263 1.1578947 ...
as.matrix(names(cell.dat))
[,1] [1,] "ROI" [2,] "ID" [3,] "x" [4,] "y" [5,] "Area" [6,] "X140Ce_Ce140f" [7,] "X80ArAr_ArAr80f" [8,] "alphaSMA_Pr141f" [9,] "CD11b_Sm149f" [10,] "CD20_Dy161f" [11,] "CD3_Er170f" [12,] "CD4_Gd156f" [13,] "CD45_Sm152f" [14,] "CD68_Tb159f" [15,] "CD8a_Dy162f" [16,] "CollagenI_Tm169f" [17,] "DNA1_Ir191f" [18,] "DNA3_Ir193f" [19,] "HistoneH3_Yb176f" [20,] "VIM_Nd143f"
cellular.cols <- names(cell.dat)[c(6:20)] as.matrix(cellular.cols)
[,1] [1,] "X140Ce_Ce140f" [2,] "X80ArAr_ArAr80f" [3,] "alphaSMA_Pr141f" [4,] "CD11b_Sm149f" [5,] "CD20_Dy161f" [6,] "CD3_Er170f" [7,] "CD4_Gd156f" [8,] "CD45_Sm152f" [9,] "CD68_Tb159f" [10,] "CD8a_Dy162f" [11,] "CollagenI_Tm169f" [12,] "DNA1_Ir191f" [13,] "DNA3_Ir193f" [14,] "HistoneH3_Yb176f" [15,] "VIM_Nd143f"
### Arcsinh transformation cell.dat <- do.asinh(cell.dat, cellular.cols, cofactor = 1)
### Invert y axis all.neg <- function(test) -1*abs(test) y_invert <- cell.dat[['y']] y_invert <- all.neg(y_invert) cell.dat[['y_invert']] <- y_invert cell.dat
### Write FCS files setwd(OutputDirectory) dir.create('FCS files') setwd('FCS files') write.files(cell.dat, 'spatial_all', write.csv = FALSE, write.fcs = TRUE) write.files(cell.dat, 'spatial', divide.by = 'ROI', write.csv = FALSE, write.fcs = TRUE)
Further plots
x
################################################################################### ### Further plots ###################################################################################
### Plot settings as.matrix(names(spatial.dat)) plot.rois <- c("ROI002", "ROI004") plot.rois
[1] "ROI002" "ROI004"
as.matrix(names(spatial.dat[[1]]$RASTERS)) exp.plot <- names(spatial.dat[[1]]$RASTERS)[1:15] exp.plot
[1] "X140Ce_Ce140f" "X80ArAr_ArAr80f" "alphaSMA_Pr141f" "CD11b_Sm149f" "CD20_Dy161f" "CD3_Er170f" [7] "CD4_Gd156f" "CD45_Sm152f" "CD68_Tb159f" "CD8a_Dy162f" "CollagenI_Tm169f" "DNA1_Ir191f" [13] "DNA3_Ir193f" "HistoneH3_Yb176f" "VIM_Nd143f"
as.matrix(names(spatial.dat[[1]]$RASTERS)) factor.plot <- names(spatial.dat[[1]]$RASTERS)[0] factor.plot
character(0)
### Expression data point plots setwd(OutputDirectory) dir.create('Plots - expression') setwd('Plots - expression') for(i in plot.rois){ for(a in exp.plot){ make.spatial.plot(spatial.dat = spatial.dat, image.roi = i, image.channel = a, mask.outlines = 'cell.mask') } }
### Factor data point plots setwd(OutputDirectory) dir.create('Plots - data point expression') setwd('Plots - data point expression') for(i in plot.rois){ for(a in exp.plot){ make.spatial.plot(spatial.dat = spatial.dat, image.roi = i, image.channel = a, mask.outlines = 'cell.mask', cell.dat = 'CellData', cell.col = a) } for(a in factor.plot){ make.spatial.plot(spatial.dat = spatial.dat, image.roi = i, image.channel = a, mask.outlines = 'cell.mask', cell.dat = 'CellData', cell.col = a, cell.col.type = 'factor') } }
Script 2: Cellular analysis
x
################################################################################### ### Spectre: spatial 2 - cellular analysis ###################################################################################
### Load libraries library('Spectre') Spectre::package.check(type = 'spatial') Spectre::package.load(type = 'spatial')
### Set PrimaryDirectory dirname(rstudioapi::getActiveDocumentContext()$path) # Finds the directory where this script is located setwd(dirname(rstudioapi::getActiveDocumentContext()$path)) # Sets the working directory to where the script is located getwd() PrimaryDirectory <- getwd() PrimaryDirectory
### Set InputDirectory setwd(PrimaryDirectory) setwd("Output 1 - add masks") InputDirectory <- getwd() InputDirectory
### Set metadata directory setwd(PrimaryDirectory) setwd("metadata") MetaDirectory <- getwd() MetaDirectory
### Create output directory setwd(PrimaryDirectory) dir.create("Output 2 - cellular analysis") setwd("Output 2 - cellular analysis") OutputDirectory <- getwd() OutputDirectory
################################################################################### ### Read in spatial data object ###################################################################################
### Read in spatial data object setwd(InputDirectory) setwd("QS file/") list.files(getwd(), '.qs') spatial.dat <- qread('spatial.dat.qs') str(spatial.dat, 3) spatial.dat[[1]]$DATA$CellData
### Metadata setwd(MetaDirectory) sample.meta <- fread("ROIs and samples.csv") sample.meta
################################################################################### ### Extract cellular data from images using masks and add annotations ###################################################################################
### Pull cellular data into a separate data.table cell.dat <- do.pull.data(spatial.dat, 'CellData') cell.dat
### Add sample metadata cell.dat <- do.add.cols(cell.dat, base.col = 'ROI', add.dat = sample.meta, add.by = 'ROI') cell.dat
################################################################################### ### Filtering ###################################################################################
### Filter # # #
################################################################################### ### Setup columns ###################################################################################
### Select 'cellular' columns and perform arcsinh transformation as.matrix(names(cell.dat)) cellular.cols <- names(cell.dat)[c(6:20)] as.matrix(cellular.cols)
### Define key columns as.matrix(names(cell.dat)) roi.col <- 'ROI' sample.col <- 'Sample' group.col <- 'Group' batch.col <- 'Batch'
### Asinh transformation cell.dat <- do.asinh(cell.dat, cellular.cols, cofactor = 1) cell.dat cellular.cols <- paste0(cellular.cols, "_asinh") as.matrix(cellular.cols)
### Re-scaling cell.dat <- do.rescale(cell.dat, cellular.cols) cell.dat cellular.cols <- paste0(cellular.cols, "_rescaled") as.matrix(cellular.cols)
### Define clustering cols as.matrix(names(cell.dat)) cluster.cols <- names(cell.dat)[c(42:48)] as.matrix(cluster.cols)
################################################################################### ### Clustering and dimensionality reduction ###################################################################################
setwd(OutputDirectory)
### Run FlowSOM cell.dat <- run.flowsom(cell.dat, cluster.cols, meta.k = 15) cell.dat
### Run DR cell.dat <- run.fitsne(cell.dat, cluster.cols, perplexity = 200) cell.dat
### Individual plots make.colour.plot(cell.dat, 'FItSNE_X', 'FItSNE_Y', roi.col, 'factor') make.colour.plot(cell.dat, 'FItSNE_X', 'FItSNE_Y', sample.col, 'factor') make.colour.plot(cell.dat, 'FItSNE_X', 'FItSNE_Y', group.col, 'factor') make.colour.plot(cell.dat, 'FItSNE_X', 'FItSNE_Y', batch.col, 'factor') make.colour.plot(cell.dat, 'FItSNE_X', 'FItSNE_Y', 'FlowSOM_metacluster', 'factor', add.label = TRUE)
### Multi plots make.multi.plot(cell.dat, 'FItSNE_X', 'FItSNE_Y', plot.by = cellular.cols, figure.title = 'Cellular cols') make.multi.plot(cell.dat, 'FItSNE_X', 'FItSNE_Y', plot.by = cluster.cols, col.type = 'factor', figure.title = 'Clustering cols') make.multi.plot(cell.dat, 'FItSNE_X', 'FItSNE_Y', 'FlowSOM_metacluster', 'ROI', col.type = 'factor', figure.title = 'FlowSOM_metacluster by ROI')
### Expression heatmap exp <- do.aggregate(cell.dat, cellular.cols, 'FlowSOM_metacluster', 'mean') make.pheatmap(exp, 'FlowSOM_metacluster', cellular.cols) rm(exp)
################################################################################### ### Annotate clusters ###################################################################################
### Cluster annotations cluster.annots <- list('CD4 T cells' = c(1,5), 'CD8 T cells' = c(6,2), 'B cells' = c(7,12,8,9,13,14,15,10,11)) cluster.annots <- do.list.switch(cluster.annots) names(cluster.annots) <- c('Values', 'Annotated metacluster') cluster.annots
### Add annotations, fill in NAs cell.dat <- do.add.cols(cell.dat, 'FlowSOM_metacluster', cluster.annots, 'Values') cell.dat[['Annotated metacluster']][is.na(cell.dat[['Annotated metacluster']])] <- 'Other' cell.dat
### Extra plots make.colour.plot(cell.dat, 'FItSNE_X', 'FItSNE_Y', 'Annotated metacluster', 'factor', add.label = TRUE) make.multi.plot(cell.dat, 'FItSNE_X', 'FItSNE_Y', 'Annotated metacluster', 'ROI', col.type = 'factor', figure.title = 'Annotated metacluster by ROI')
################################################################################### ### Save data ###################################################################################
setwd(OutputDirectory)
### data.table fwrite(cell.dat, 'cell.dat.csv')
### FCS files setwd(OutputDirectory) dir.create('FCS') setwd('FCS') write.files(cell.dat, file.prefix = 'IMC_All', write.csv = FALSE, write.fcs = TRUE) write.files(cell.dat, file.prefix = 'IMC_', divide.by = sample.col, write.csv = FALSE, write.fcs = TRUE)
################################################################################### ### Spatial plotting of cluster data ###################################################################################
### Clusters setwd(OutputDirectory) dir.create('Clusters') setwd('Clusters') for(i in unique(cell.dat$ROI)){ temp <- cell.dat[cell.dat[['ROI']] == i,] make.spatial.plot(spatial.dat, i, 'DNA1_Ir191', mask.outlines = 'cell.mask', temp, cell.col = 'FlowSOM_metacluster', cell.col.type = 'factor') }
### Cell type setwd(OutputDirectory) dir.create('Annotated') setwd('Annotated') for(i in unique(cell.dat$ROI)){ temp <- cell.dat[cell.dat[['ROI']] == i,] make.spatial.plot(spatial.dat, i, 'DNA1_Ir191', mask.outlines = 'cell.mask', temp, cell.col = 'Annotated metacluster', cell.col.type = 'factor') }
Script 3: spatial analysis
x