# Download the FCS files from FlowRepository
# experiment FR-FCM-ZZ36 alias OMIP-018
# Uncompress the ZIP file into a folder named OMIP-018_FR-FCM-ZZ36
dir(pattern = "Compensatio.+fcs", path = "OMIP-018_FR-FCM-ZZ36")
## [1] "Compensation Controls_Alexa Fluor 405 Stained Control.fcs"
## [2] "Compensation Controls_Alexa Fluor 430 Stained Control.fcs"
## [3] "Compensation Controls_Alexa Fluor 488 Stained Control.fcs"
## [4] "Compensation Controls_APC-Cy7 Stained Control.fcs"
## [5] "Compensation Controls_APC Stained Control.fcs"
## [6] "Compensation Controls_PE-Cy5-5 Stained Control.fcs"
## [7] "Compensation Controls_PE-Cy5 Stained Control.fcs"
## [8] "Compensation Controls_PE-Cy7 Stained Control.fcs"
## [9] "Compensation Controls_PE-Texas Red Stained Control.fcs"
## [10] "Compensation Controls_PE Stained Control.fcs"
## [11] "Compensation Controls_PerCP-Cy5-5 Stained Control.fcs"
## [12] "Compensation Controls_Qdot 605 Stained Control.fcs"
## [13] "Compensation Controls_Qdot 655 Stained Control.fcs"
## [14] "Compensation Controls_Qdot 800 Stained Control.fcs"
## [15] "Compensation Controls_Unstained Control.fcs"
library(flowCore)
setMethod("spillover",
signature = signature(x = "flowSet"),
definition = function(x, unstained = NULL, patt = NULL, fsc = "FSC-A",
ssc = "SSC-A", method = "median",
stain_match = c("intensity", "ordered", "regexpr"),
useNormFilt = FALSE, pregate = FALSE,
plot = FALSE, ...) {
stain_match <- match.arg(stain_match)
if (is.null(unstained)) {
stop("Sorry, we don't yet support unstained cells blended ",
"with stained cells", " please specify the name or index of unstained sample", call. = FALSE)
} else {
## We often only want spillover for a subset of the columns
allcols <- colnames(x)
cols <- if (is.null(patt)) {
allcols
} else {
grep(patt, allcols, value = TRUE)
}
## Ignore these guys if they somehow got into cols.
cols <- cols[!(cols %in% c(fsc, ssc))]
## There has got to be a better way of doing this...
if (!is.numeric(unstained)) {
unstained <- match(unstained, sampleNames(x))
if (is.na(unstained)) {
stop("Baseline(unstained sample) not found in this set.", call. = FALSE)
}
}
## Check to see if the unstained sample is in the list of
## stains. If not, we need to add it, making it the first
## row and adjust the unstained index accordingly.
## If it is there we adjust to the appropriate index.
if (useNormFilt) {
if (is.numeric(fsc)) {
fsc <- allcols[fsc]
}
if (is.numeric(ssc)) {
ssc <- allcols[ssc]
}
if (is.na(match(fsc, allcols))) {
stop("Could not find forward scatter parameter. ",
"Please set the fsc parameter", call. = FALSE)
}
if (is.na(match(ssc, allcols))) {
stop("Could not find side scatter parameter. ",
"Please set the ssc parameter", call. = FALSE)
n2f <- norm2Filter(fsc, ssc, scale.factor = 1.5)
x <- Subset(x, n2f)
}
}
# Here, we match the stain channels with the compensation controls
# if the user has specified it. Otherwise, we must "guess" below
# based on the largest statistic for the compensation control
# (i.e., the row).
# If "ordered," we assume the ordering of the channels in the
# flowSet object is the same as the ordering of the
# compensation-control samples.
# Another option is to use a regular expression to match the
# channel names with the filenames of the compensation controls.
if (stain_match == "intensity") {
channel_order <- NA
} else if (stain_match == "ordered") {
channel_order <- seq_along(sampleNames(x))[-unstained]
} else if (stain_match == "regexpr") {
channel_order <- sapply(cols, grep, x = sampleNames(x), fixed = TRUE)
if (!all(sapply(channel_order, length) == 1)) {
stop("Multiple stains match to a common compensation-control filename",
call. = FALSE)
}
}
if (pregate) {
if (any(is.na(channel_order))) {
stop("Cannot apply pregate without knowing ordering of channels. ",
"Match the channels to controls with 'ordered' or 'regexpr'.",
call. = FALSE)
}
require('flowStats')
if (plot) {
oask <- devAskNewPage(TRUE)
on.exit(devAskNewPage(oask))
}
x_gated <- lapply(sort(channel_order), function(channel_i) {
flow_frame <- x[[channel_i]]
channel_name <- cols[which(channel_order == channel_i)]
# Applies flowStats:::rangeGate to select positive population
gate_filter <- rangeGate(flow_frame, stain = channel_name,
inBetween = TRUE, borderQuant = 0,
absolute = FALSE, peakNr = 2, ...)
if (plot) {
# Plots a kernel density for the current channel
plot(density(exprs(flow_frame)[, channel_name]),
xlab = channel_name, ylab = "Density",
main = paste("Compensation Control:", sampleNames(x)[channel_i]))
# Adds a vertical line to show gate
cutpoint <- c(gate_filter@min, gate_filter@max)
cutpoint <- cutpoint[is.finite(cutpoint)]
abline(v = cutpoint, col = "black", lwd = 3, lty = 2)
}
Subset(flow_frame, gate_filter)
})
x_gated <- x_gated[channel_order]
names(x_gated) <- sampleNames(x)[channel_order]
x <- rbind2(flowSet(x_gated), x[unstained])
}
if(length(x) - 1 != length(cols))
{
stop("the number of single stained samples provided in this set doesn't match to the number of stained channels!")
}
# Compute background for each channel of each file
if (method == "mode") {
inten <- fsApply(x, function(flow_frame) {
modes <- sapply(cols, function(stain) {
density_stain <- density(exprs(flow_frame)[, stain])
with(density_stain, x[which.max(y)])
}, USE.NAMES = TRUE)
modes
})
} else {
inten <- fsApply(x, each_col, method)[, cols]
}
# background correction
inten <- pmax(sweep(inten[-unstained, ], 2, inten[unstained, ]), 0)
# normalize each row
# If the channel order was not set above, then a guess is made based on the
# largest statistic for the compensation control (i.e., the row).
# If the channels are "ordered", the diagonal must be equal to 1.
# If the channels are "regexpr" matched, the files (row of the matrix) must
# be ordered first, then the diagonal must be equal to 1.
if (stain_match == "intensity") {
# normalize by max of each row
inten <- sweep(inten, 1, apply(inten, 1, max), "/")
# find max for each file
channel_order <- apply(inten, 1, which.max)
if (anyDuplicated(channel_order) > 0) {
stop("Unable to match stains with controls based on intensity: ",
"a single stain matches to several multiple controls. ",
call. = FALSE)
}
# order the row
inten <- inten[order(channel_order),]
} else {
if (stain_match == "regexpr") # order the row
inten = inten[channel_order, ]
# normalize by the diagonal
inten <- sweep(inten, 1, diag(inten), "/")
}
# Updates row names
rownames(inten) <- colnames(inten)
inten
}
})
## [1] "spillover"
# Read files
fs = read.flowSet(pattern = "Compensatio.+fcs", path = "OMIP-018_FR-FCM-ZZ36")
pData(fs)
| name | |
|---|---|
| Compensation Controls_Alexa Fluor 405 Stained Control.fcs | Compensation Controls_Alexa Fluor 405 Stained Control.fcs |
| Compensation Controls_Alexa Fluor 430 Stained Control.fcs | Compensation Controls_Alexa Fluor 430 Stained Control.fcs |
| Compensation Controls_Alexa Fluor 488 Stained Control.fcs | Compensation Controls_Alexa Fluor 488 Stained Control.fcs |
| Compensation Controls_APC-Cy7 Stained Control.fcs | Compensation Controls_APC-Cy7 Stained Control.fcs |
| Compensation Controls_APC Stained Control.fcs | Compensation Controls_APC Stained Control.fcs |
| Compensation Controls_PE-Cy5-5 Stained Control.fcs | Compensation Controls_PE-Cy5-5 Stained Control.fcs |
| Compensation Controls_PE-Cy5 Stained Control.fcs | Compensation Controls_PE-Cy5 Stained Control.fcs |
| Compensation Controls_PE-Cy7 Stained Control.fcs | Compensation Controls_PE-Cy7 Stained Control.fcs |
| Compensation Controls_PE-Texas Red Stained Control.fcs | Compensation Controls_PE-Texas Red Stained Control.fcs |
| Compensation Controls_PE Stained Control.fcs | Compensation Controls_PE Stained Control.fcs |
| Compensation Controls_PerCP-Cy5-5 Stained Control.fcs | Compensation Controls_PerCP-Cy5-5 Stained Control.fcs |
| Compensation Controls_Qdot 605 Stained Control.fcs | Compensation Controls_Qdot 605 Stained Control.fcs |
| Compensation Controls_Qdot 655 Stained Control.fcs | Compensation Controls_Qdot 655 Stained Control.fcs |
| Compensation Controls_Qdot 800 Stained Control.fcs | Compensation Controls_Qdot 800 Stained Control.fcs |
| Compensation Controls_Unstained Control.fcs | Compensation Controls_Unstained Control.fcs |
colnames(fs)
## [1] "FSC-A" "SSC-A" "Alexa Fluor 488-A"
## [4] "PerCP-Cy5-5-A" "APC-A" "APC-Cy7-A"
## [7] "Alexa Fluor 405-A" "Alexa Fluor 430-A" "Qdot 605-A"
## [10] "Qdot 655-A" "Qdot 800-A" "PE-A"
## [13] "PE-Texas Red-A" "PE-Cy5-A" "PE-Cy5-5-A"
## [16] "PE-Cy7-A" "Time"
# Check file names vs channels
# Remove FSC, SSC, Time
cbind(pData(fs)[-length(fs),], colnames(fs)[-c(1:2, length(colnames(fs)))])
## [,1]
## [1,] "Compensation Controls_Alexa Fluor 405 Stained Control.fcs"
## [2,] "Compensation Controls_Alexa Fluor 430 Stained Control.fcs"
## [3,] "Compensation Controls_Alexa Fluor 488 Stained Control.fcs"
## [4,] "Compensation Controls_APC-Cy7 Stained Control.fcs"
## [5,] "Compensation Controls_APC Stained Control.fcs"
## [6,] "Compensation Controls_PE-Cy5-5 Stained Control.fcs"
## [7,] "Compensation Controls_PE-Cy5 Stained Control.fcs"
## [8,] "Compensation Controls_PE-Cy7 Stained Control.fcs"
## [9,] "Compensation Controls_PE-Texas Red Stained Control.fcs"
## [10,] "Compensation Controls_PE Stained Control.fcs"
## [11,] "Compensation Controls_PerCP-Cy5-5 Stained Control.fcs"
## [12,] "Compensation Controls_Qdot 605 Stained Control.fcs"
## [13,] "Compensation Controls_Qdot 655 Stained Control.fcs"
## [14,] "Compensation Controls_Qdot 800 Stained Control.fcs"
## [,2]
## [1,] "Alexa Fluor 488-A"
## [2,] "PerCP-Cy5-5-A"
## [3,] "APC-A"
## [4,] "APC-Cy7-A"
## [5,] "Alexa Fluor 405-A"
## [6,] "Alexa Fluor 430-A"
## [7,] "Qdot 605-A"
## [8,] "Qdot 655-A"
## [9,] "Qdot 800-A"
## [10,] "PE-A"
## [11,] "PE-Texas Red-A"
## [12,] "PE-Cy5-A"
## [13,] "PE-Cy5-5-A"
## [14,] "PE-Cy7-A"
# Selecting and ordering by hand, FSC & SSC being the first two channels
markers.id = c(5, 6, 1, 4, 3, 13, 12, 14, 11, 10, 2, 7, 8, 9) + 2
# Check the matching
# NB: The order of the loaded with read.flowSet despends on the locale!
cbind(gsub("Compensation Controls_", "",
gsub(" Stained Control.fcs", "",
pData(fs)[-length(fs),])),
colnames(fs)[markers.id])
## [,1] [,2]
## [1,] "Alexa Fluor 405" "Alexa Fluor 405-A"
## [2,] "Alexa Fluor 430" "Alexa Fluor 430-A"
## [3,] "Alexa Fluor 488" "Alexa Fluor 488-A"
## [4,] "APC-Cy7" "APC-Cy7-A"
## [5,] "APC" "APC-A"
## [6,] "PE-Cy5-5" "PE-Cy5-5-A"
## [7,] "PE-Cy5" "PE-Cy5-A"
## [8,] "PE-Cy7" "PE-Cy7-A"
## [9,] "PE-Texas Red" "PE-Texas Red-A"
## [10,] "PE" "PE-A"
## [11,] "PerCP-Cy5-5" "PerCP-Cy5-5-A"
## [12,] "Qdot 605" "Qdot 605-A"
## [13,] "Qdot 655" "Qdot 655-A"
## [14,] "Qdot 800" "Qdot 800-A"
# Apply selection and ordering
fs = fsApply(fs, function(ff) ff[, markers.id]) # ordered
# Verify
colnames(fs)
## [1] "Alexa Fluor 405-A" "Alexa Fluor 430-A" "Alexa Fluor 488-A"
## [4] "APC-Cy7-A" "APC-A" "PE-Cy5-5-A"
## [7] "PE-Cy5-A" "PE-Cy7-A" "PE-Texas Red-A"
## [10] "PE-A" "PerCP-Cy5-5-A" "Qdot 605-A"
## [13] "Qdot 655-A" "Qdot 800-A"
length(colnames(fs))
## [1] 14
length(fs)
## [1] 15
fs[[1]]
## flowFrame object 'Compensation Controls_Alexa Fluor 405 Stained Control.fcs'
## with 5000 cells and 14 observables:
## name desc range minRange maxRange
## $P7 Alexa Fluor 405-A <NA> 262144 -111.00 262143
## $P8 Alexa Fluor 430-A <NA> 262144 -111.00 262143
## $P3 Alexa Fluor 488-A <NA> 262144 -81.18 262143
## $P6 APC-Cy7-A <NA> 262144 -111.00 262143
## $P5 APC-A <NA> 262144 -88.16 262143
## $P15 PE-Cy5-5-A <NA> 262144 -111.00 262143
## $P14 PE-Cy5-A <NA> 262144 -55.50 262143
## $P16 PE-Cy7-A <NA> 262144 -111.00 262143
## $P13 PE-Texas Red-A <NA> 262144 -109.50 262143
## $P12 PE-A <NA> 262144 -38.25 262143
## $P4 PerCP-Cy5-5-A <NA> 262144 -100.86 262143
## $P9 Qdot 605-A <NA> 262144 -111.00 262143
## $P10 Qdot 655-A <NA> 262144 -111.00 262143
## $P11 Qdot 800-A <NA> 262144 -111.00 262143
## 227 keywords are stored in the 'description' slot
# Process
fs.spill = spillover(fs,
unstained = length(fs), # unstained is the last file
stain_match = "ordered") # manual matching
# Inspection
round(fs.spill, 3)
## Alexa Fluor 405-A Alexa Fluor 430-A Alexa Fluor 488-A
## Alexa Fluor 405-A 1.000 0.041 0.000
## Alexa Fluor 430-A 0.247 1.000 0.023
## Alexa Fluor 488-A 0.001 0.053 1.000
## APC-Cy7-A 0.002 0.001 0.000
## APC-A 0.001 0.001 0.000
## PE-Cy5-5-A 0.003 0.012 0.001
## PE-Cy5-A 0.000 0.000 0.001
## PE-Cy7-A 0.001 0.000 0.002
## PE-Texas Red-A 0.000 0.001 0.001
## PE-A 0.000 0.002 0.006
## PerCP-Cy5-5-A 0.000 0.000 0.000
## Qdot 605-A 0.000 0.000 0.000
## Qdot 655-A 0.032 0.002 0.000
## Qdot 800-A 0.000 0.000 0.000
## APC-Cy7-A APC-A PE-Cy5-5-A PE-Cy5-A PE-Cy7-A
## Alexa Fluor 405-A 0.000 0.000 0.000 0.000 0.000
## Alexa Fluor 430-A 0.000 0.000 0.000 0.000 0.000
## Alexa Fluor 488-A 0.000 0.000 0.000 0.000 0.000
## APC-Cy7-A 1.000 0.075 0.062 0.006 0.388
## APC-A 0.068 1.000 0.491 0.076 0.028
## PE-Cy5-5-A 0.003 0.006 1.000 0.017 0.056
## PE-Cy5-A 0.073 1.110 10.262 1.000 0.392
## PE-Cy7-A 0.047 0.000 0.058 0.001 1.000
## PE-Texas Red-A 0.000 0.001 0.482 0.050 0.020
## PE-A 0.000 0.000 0.425 0.048 0.014
## PerCP-Cy5-5-A 0.024 0.012 0.718 0.008 0.047
## Qdot 605-A 0.000 0.000 0.000 0.000 0.000
## Qdot 655-A 0.000 0.012 0.011 0.008 0.000
## Qdot 800-A 0.027 0.000 0.032 0.000 0.089
## PE-Texas Red-A PE-A PerCP-Cy5-5-A Qdot 605-A Qdot 655-A
## Alexa Fluor 405-A 0.000 0.000 0.000 0.011 0.004
## Alexa Fluor 430-A 0.000 0.000 0.001 0.756 0.311
## Alexa Fluor 488-A 0.000 0.000 0.018 0.029 0.011
## APC-Cy7-A 0.001 0.001 0.002 0.003 0.052
## APC-A 0.003 0.000 0.013 0.004 0.692
## PE-Cy5-5-A 0.041 0.028 0.274 0.019 0.017
## PE-Cy5-A 0.043 0.023 2.448 0.009 1.427
## PE-Cy7-A 0.019 0.011 0.022 0.005 0.003
## PE-Texas Red-A 1.000 0.118 0.125 0.210 0.113
## PE-A 1.567 1.000 0.117 0.336 0.130
## PerCP-Cy5-5-A 0.000 0.000 1.000 0.000 0.050
## Qdot 605-A 0.084 0.002 0.000 1.000 0.016
## Qdot 655-A 0.002 0.000 0.006 0.023 1.000
## Qdot 800-A 0.000 0.000 0.019 0.000 0.001
## Qdot 800-A
## Alexa Fluor 405-A 0.001
## Alexa Fluor 430-A 0.022
## Alexa Fluor 488-A 0.001
## APC-Cy7-A 1.105
## APC-A 0.060
## PE-Cy5-5-A 0.032
## PE-Cy5-A 0.181
## PE-Cy7-A 0.543
## PE-Texas Red-A 0.011
## PE-A 0.008
## PerCP-Cy5-5-A 0.548
## Qdot 605-A 0.000
## Qdot 655-A 0.001
## Qdot 800-A 1.000
cols = c("Alexa Fluor 405-A", "Alexa Fluor 430-A", "Alexa Fluor 488-A")
round(fs.spill[cols, cols], 3)
## Alexa Fluor 405-A Alexa Fluor 430-A Alexa Fluor 488-A
## Alexa Fluor 405-A 1.000 0.041 0.000
## Alexa Fluor 430-A 0.247 1.000 0.023
## Alexa Fluor 488-A 0.001 0.053 1.000
cols = c("APC-A", "APC-Cy7-A")
round(fs.spill[cols, cols], 3)
## APC-A APC-Cy7-A
## APC-A 1.000 0.068
## APC-Cy7-A 0.075 1.000
cols = c("PE-A", "PE-Cy5-5-A", "PE-Cy5-A", "PE-Cy7-A", "PE-Texas Red-A")
round(fs.spill[cols, cols], 3)
## PE-A PE-Cy5-5-A PE-Cy5-A PE-Cy7-A PE-Texas Red-A
## PE-A 1.000 0.425 0.048 0.014 1.567
## PE-Cy5-5-A 0.028 1.000 0.017 0.056 0.041
## PE-Cy5-A 0.023 10.262 1.000 0.392 0.043
## PE-Cy7-A 0.011 0.058 0.001 1.000 0.019
## PE-Texas Red-A 0.118 0.482 0.050 0.020 1.000
# Graphical overview
# Thichk line is unstained sample
matplot(asinh(t(fsApply(fs, each_col, median))/150), typ = "l", lwd = rep(c(2, 5), times = c(14,1)), xlab = "Channels", ylab = "asinh(intensity/150)")
# Read compensation files
fs = read.flowSet(pattern = "Compensatio.+fcs", path = "OMIP-018_FR-FCM-ZZ36")
pData(fs)
| name | |
|---|---|
| Compensation Controls_Alexa Fluor 405 Stained Control.fcs | Compensation Controls_Alexa Fluor 405 Stained Control.fcs |
| Compensation Controls_Alexa Fluor 430 Stained Control.fcs | Compensation Controls_Alexa Fluor 430 Stained Control.fcs |
| Compensation Controls_Alexa Fluor 488 Stained Control.fcs | Compensation Controls_Alexa Fluor 488 Stained Control.fcs |
| Compensation Controls_APC-Cy7 Stained Control.fcs | Compensation Controls_APC-Cy7 Stained Control.fcs |
| Compensation Controls_APC Stained Control.fcs | Compensation Controls_APC Stained Control.fcs |
| Compensation Controls_PE-Cy5-5 Stained Control.fcs | Compensation Controls_PE-Cy5-5 Stained Control.fcs |
| Compensation Controls_PE-Cy5 Stained Control.fcs | Compensation Controls_PE-Cy5 Stained Control.fcs |
| Compensation Controls_PE-Cy7 Stained Control.fcs | Compensation Controls_PE-Cy7 Stained Control.fcs |
| Compensation Controls_PE-Texas Red Stained Control.fcs | Compensation Controls_PE-Texas Red Stained Control.fcs |
| Compensation Controls_PE Stained Control.fcs | Compensation Controls_PE Stained Control.fcs |
| Compensation Controls_PerCP-Cy5-5 Stained Control.fcs | Compensation Controls_PerCP-Cy5-5 Stained Control.fcs |
| Compensation Controls_Qdot 605 Stained Control.fcs | Compensation Controls_Qdot 605 Stained Control.fcs |
| Compensation Controls_Qdot 655 Stained Control.fcs | Compensation Controls_Qdot 655 Stained Control.fcs |
| Compensation Controls_Qdot 800 Stained Control.fcs | Compensation Controls_Qdot 800 Stained Control.fcs |
| Compensation Controls_Unstained Control.fcs | Compensation Controls_Unstained Control.fcs |
colnames(fs)
## [1] "FSC-A" "SSC-A" "Alexa Fluor 488-A"
## [4] "PerCP-Cy5-5-A" "APC-A" "APC-Cy7-A"
## [7] "Alexa Fluor 405-A" "Alexa Fluor 430-A" "Qdot 605-A"
## [10] "Qdot 655-A" "Qdot 800-A" "PE-A"
## [13] "PE-Texas Red-A" "PE-Cy5-A" "PE-Cy5-5-A"
## [16] "PE-Cy7-A" "Time"
# Change sampleNames that are matched against colnames (aka name of markers)
# Add -A in order to match the suffix of colnames (except Time)
sampleNames(fs) = gsub("Compensation Controls_", "",
gsub(" Stained Control.fcs", "-A",
sampleNames(fs)))
# Check that there is only 1 match per compensated marker
sapply(colnames(fs), grep, x = sampleNames(fs), fixed = TRUE)
## $`FSC-A`
## integer(0)
##
## $`SSC-A`
## integer(0)
##
## $`Alexa Fluor 488-A`
## [1] 3
##
## $`PerCP-Cy5-5-A`
## [1] 11
##
## $`APC-A`
## [1] 5
##
## $`APC-Cy7-A`
## [1] 4
##
## $`Alexa Fluor 405-A`
## [1] 1
##
## $`Alexa Fluor 430-A`
## [1] 2
##
## $`Qdot 605-A`
## [1] 12
##
## $`Qdot 655-A`
## [1] 13
##
## $`Qdot 800-A`
## [1] 14
##
## $`PE-A`
## [1] 10
##
## $`PE-Texas Red-A`
## [1] 9
##
## $`PE-Cy5-A`
## [1] 7
##
## $`PE-Cy5-5-A`
## [1] 6
##
## $`PE-Cy7-A`
## [1] 8
##
## $Time
## integer(0)
# Check the pattern
grep("-A$", colnames(fs), value = TRUE)
## [1] "FSC-A" "SSC-A" "Alexa Fluor 488-A"
## [4] "PerCP-Cy5-5-A" "APC-A" "APC-Cy7-A"
## [7] "Alexa Fluor 405-A" "Alexa Fluor 430-A" "Qdot 605-A"
## [10] "Qdot 655-A" "Qdot 800-A" "PE-A"
## [13] "PE-Texas Red-A" "PE-Cy5-A" "PE-Cy5-5-A"
## [16] "PE-Cy7-A"
# Process
# fsc, ssc are declared in oreder to remove them from markers to compensate
fs.spill = spillover(fs,
unstained = length(fs), # unstained is the last file
patt = "-A$", # all parameters end with "-A"
fsc = colnames(fs)[1], # FSC name is at the 1st column
ssc = colnames(fs)[2], # SSC name is at the 2nd column
stain_match = "regexpr") # automagic matching
# Inspection
round(fs.spill, 3)
## Alexa Fluor 488-A PerCP-Cy5-5-A APC-A APC-Cy7-A
## Alexa Fluor 488-A 1.000 0.018 0.000 0.000
## PerCP-Cy5-5-A 0.000 1.000 0.012 0.024
## APC-A 0.000 0.013 1.000 0.068
## APC-Cy7-A 0.000 0.002 0.075 1.000
## Alexa Fluor 405-A 0.000 0.000 0.000 0.000
## Alexa Fluor 430-A 0.023 0.001 0.000 0.000
## Qdot 605-A 0.000 0.000 0.000 0.000
## Qdot 655-A 0.000 0.006 0.012 0.000
## Qdot 800-A 0.000 0.019 0.000 0.027
## PE-A 0.006 0.117 0.000 0.000
## PE-Texas Red-A 0.001 0.125 0.001 0.000
## PE-Cy5-A 0.001 2.448 1.110 0.073
## PE-Cy5-5-A 0.001 0.274 0.006 0.003
## PE-Cy7-A 0.002 0.022 0.000 0.047
## Alexa Fluor 405-A Alexa Fluor 430-A Qdot 605-A
## Alexa Fluor 488-A 0.001 0.053 0.029
## PerCP-Cy5-5-A 0.000 0.000 0.000
## APC-A 0.001 0.001 0.004
## APC-Cy7-A 0.002 0.001 0.003
## Alexa Fluor 405-A 1.000 0.041 0.011
## Alexa Fluor 430-A 0.247 1.000 0.756
## Qdot 605-A 0.000 0.000 1.000
## Qdot 655-A 0.032 0.002 0.023
## Qdot 800-A 0.000 0.000 0.000
## PE-A 0.000 0.002 0.336
## PE-Texas Red-A 0.000 0.001 0.210
## PE-Cy5-A 0.000 0.000 0.009
## PE-Cy5-5-A 0.003 0.012 0.019
## PE-Cy7-A 0.001 0.000 0.005
## Qdot 655-A Qdot 800-A PE-A PE-Texas Red-A PE-Cy5-A
## Alexa Fluor 488-A 0.011 0.001 0.000 0.000 0.000
## PerCP-Cy5-5-A 0.050 0.548 0.000 0.000 0.008
## APC-A 0.692 0.060 0.000 0.003 0.076
## APC-Cy7-A 0.052 1.105 0.001 0.001 0.006
## Alexa Fluor 405-A 0.004 0.001 0.000 0.000 0.000
## Alexa Fluor 430-A 0.311 0.022 0.000 0.000 0.000
## Qdot 605-A 0.016 0.000 0.002 0.084 0.000
## Qdot 655-A 1.000 0.001 0.000 0.002 0.008
## Qdot 800-A 0.001 1.000 0.000 0.000 0.000
## PE-A 0.130 0.008 1.000 1.567 0.048
## PE-Texas Red-A 0.113 0.011 0.118 1.000 0.050
## PE-Cy5-A 1.427 0.181 0.023 0.043 1.000
## PE-Cy5-5-A 0.017 0.032 0.028 0.041 0.017
## PE-Cy7-A 0.003 0.543 0.011 0.019 0.001
## PE-Cy5-5-A PE-Cy7-A
## Alexa Fluor 488-A 0.000 0.000
## PerCP-Cy5-5-A 0.718 0.047
## APC-A 0.491 0.028
## APC-Cy7-A 0.062 0.388
## Alexa Fluor 405-A 0.000 0.000
## Alexa Fluor 430-A 0.000 0.000
## Qdot 605-A 0.000 0.000
## Qdot 655-A 0.011 0.000
## Qdot 800-A 0.032 0.089
## PE-A 0.425 0.014
## PE-Texas Red-A 0.482 0.020
## PE-Cy5-A 10.262 0.392
## PE-Cy5-5-A 1.000 0.056
## PE-Cy7-A 0.058 1.000
cols = c("Alexa Fluor 405-A", "Alexa Fluor 430-A", "Alexa Fluor 488-A")
round(fs.spill[cols, cols], 3)
## Alexa Fluor 405-A Alexa Fluor 430-A Alexa Fluor 488-A
## Alexa Fluor 405-A 1.000 0.041 0.000
## Alexa Fluor 430-A 0.247 1.000 0.023
## Alexa Fluor 488-A 0.001 0.053 1.000
cols = c("APC-A", "APC-Cy7-A")
round(fs.spill[cols, cols], 3)
## APC-A APC-Cy7-A
## APC-A 1.000 0.068
## APC-Cy7-A 0.075 1.000
cols = c("PE-A", "PE-Cy5-5-A", "PE-Cy5-A", "PE-Cy7-A", "PE-Texas Red-A")
round(fs.spill[cols, cols], 3)
## PE-A PE-Cy5-5-A PE-Cy5-A PE-Cy7-A PE-Texas Red-A
## PE-A 1.000 0.425 0.048 0.014 1.567
## PE-Cy5-5-A 0.028 1.000 0.017 0.056 0.041
## PE-Cy5-A 0.023 10.262 1.000 0.392 0.043
## PE-Cy7-A 0.011 0.058 0.001 1.000 0.019
## PE-Texas Red-A 0.118 0.482 0.050 0.020 1.000
# Read compensation files
fs = read.flowSet(pattern = "Compensatio.+fcs", path = "OMIP-018_FR-FCM-ZZ36")
pData(fs)
| name | |
|---|---|
| Compensation Controls_Alexa Fluor 405 Stained Control.fcs | Compensation Controls_Alexa Fluor 405 Stained Control.fcs |
| Compensation Controls_Alexa Fluor 430 Stained Control.fcs | Compensation Controls_Alexa Fluor 430 Stained Control.fcs |
| Compensation Controls_Alexa Fluor 488 Stained Control.fcs | Compensation Controls_Alexa Fluor 488 Stained Control.fcs |
| Compensation Controls_APC-Cy7 Stained Control.fcs | Compensation Controls_APC-Cy7 Stained Control.fcs |
| Compensation Controls_APC Stained Control.fcs | Compensation Controls_APC Stained Control.fcs |
| Compensation Controls_PE-Cy5-5 Stained Control.fcs | Compensation Controls_PE-Cy5-5 Stained Control.fcs |
| Compensation Controls_PE-Cy5 Stained Control.fcs | Compensation Controls_PE-Cy5 Stained Control.fcs |
| Compensation Controls_PE-Cy7 Stained Control.fcs | Compensation Controls_PE-Cy7 Stained Control.fcs |
| Compensation Controls_PE-Texas Red Stained Control.fcs | Compensation Controls_PE-Texas Red Stained Control.fcs |
| Compensation Controls_PE Stained Control.fcs | Compensation Controls_PE Stained Control.fcs |
| Compensation Controls_PerCP-Cy5-5 Stained Control.fcs | Compensation Controls_PerCP-Cy5-5 Stained Control.fcs |
| Compensation Controls_Qdot 605 Stained Control.fcs | Compensation Controls_Qdot 605 Stained Control.fcs |
| Compensation Controls_Qdot 655 Stained Control.fcs | Compensation Controls_Qdot 655 Stained Control.fcs |
| Compensation Controls_Qdot 800 Stained Control.fcs | Compensation Controls_Qdot 800 Stained Control.fcs |
| Compensation Controls_Unstained Control.fcs | Compensation Controls_Unstained Control.fcs |
colnames(fs)
## [1] "FSC-A" "SSC-A" "Alexa Fluor 488-A"
## [4] "PerCP-Cy5-5-A" "APC-A" "APC-Cy7-A"
## [7] "Alexa Fluor 405-A" "Alexa Fluor 430-A" "Qdot 605-A"
## [10] "Qdot 655-A" "Qdot 800-A" "PE-A"
## [13] "PE-Texas Red-A" "PE-Cy5-A" "PE-Cy5-5-A"
## [16] "PE-Cy7-A" "Time"
# Change sampleNames that are matched against colnames (aka name of markers)
# Add -A in order to match the suffix of colnames (except Time)
sampleNames(fs) = gsub("Compensation Controls_", "",
gsub(" Stained Control.fcs", "-A",
sampleNames(fs)))
# Check that there is only 1 match per compensated marker
sapply(colnames(fs), grep, x = sampleNames(fs), fixed = TRUE)
## $`FSC-A`
## integer(0)
##
## $`SSC-A`
## integer(0)
##
## $`Alexa Fluor 488-A`
## [1] 3
##
## $`PerCP-Cy5-5-A`
## [1] 11
##
## $`APC-A`
## [1] 5
##
## $`APC-Cy7-A`
## [1] 4
##
## $`Alexa Fluor 405-A`
## [1] 1
##
## $`Alexa Fluor 430-A`
## [1] 2
##
## $`Qdot 605-A`
## [1] 12
##
## $`Qdot 655-A`
## [1] 13
##
## $`Qdot 800-A`
## [1] 14
##
## $`PE-A`
## [1] 10
##
## $`PE-Texas Red-A`
## [1] 9
##
## $`PE-Cy5-A`
## [1] 7
##
## $`PE-Cy5-5-A`
## [1] 6
##
## $`PE-Cy7-A`
## [1] 8
##
## $Time
## integer(0)
# Check the pattern
grep("-A$", colnames(fs), value = TRUE)
## [1] "FSC-A" "SSC-A" "Alexa Fluor 488-A"
## [4] "PerCP-Cy5-5-A" "APC-A" "APC-Cy7-A"
## [7] "Alexa Fluor 405-A" "Alexa Fluor 430-A" "Qdot 605-A"
## [10] "Qdot 655-A" "Qdot 800-A" "PE-A"
## [13] "PE-Texas Red-A" "PE-Cy5-A" "PE-Cy5-5-A"
## [16] "PE-Cy7-A"
# Process
fs.spill = spillover(fs,
unstained = length(fs), # unstained is the last file
patt = "-A$", # all parameters end with "-A"
fsc = colnames(fs)[1], # FSC name is at the 1st column
ssc = colnames(fs)[2], # SSC name is at the 2nd column
stain_match = "regexpr", # automagic matching
useNormFilt = TRUE)
# Inspection
round(fs.spill, 3)
## Alexa Fluor 488-A PerCP-Cy5-5-A APC-A APC-Cy7-A
## Alexa Fluor 488-A 1.000 0.018 0.000 0.000
## PerCP-Cy5-5-A 0.000 1.000 0.012 0.024
## APC-A 0.000 0.013 1.000 0.068
## APC-Cy7-A 0.000 0.002 0.075 1.000
## Alexa Fluor 405-A 0.000 0.000 0.000 0.000
## Alexa Fluor 430-A 0.023 0.001 0.000 0.000
## Qdot 605-A 0.000 0.000 0.000 0.000
## Qdot 655-A 0.000 0.006 0.012 0.000
## Qdot 800-A 0.000 0.019 0.000 0.027
## PE-A 0.006 0.117 0.000 0.000
## PE-Texas Red-A 0.001 0.125 0.001 0.000
## PE-Cy5-A 0.001 2.448 1.110 0.073
## PE-Cy5-5-A 0.001 0.274 0.006 0.003
## PE-Cy7-A 0.002 0.022 0.000 0.047
## Alexa Fluor 405-A Alexa Fluor 430-A Qdot 605-A
## Alexa Fluor 488-A 0.001 0.053 0.029
## PerCP-Cy5-5-A 0.000 0.000 0.000
## APC-A 0.001 0.001 0.004
## APC-Cy7-A 0.002 0.001 0.003
## Alexa Fluor 405-A 1.000 0.041 0.011
## Alexa Fluor 430-A 0.247 1.000 0.756
## Qdot 605-A 0.000 0.000 1.000
## Qdot 655-A 0.032 0.002 0.023
## Qdot 800-A 0.000 0.000 0.000
## PE-A 0.000 0.002 0.336
## PE-Texas Red-A 0.000 0.001 0.210
## PE-Cy5-A 0.000 0.000 0.009
## PE-Cy5-5-A 0.003 0.012 0.019
## PE-Cy7-A 0.001 0.000 0.005
## Qdot 655-A Qdot 800-A PE-A PE-Texas Red-A PE-Cy5-A
## Alexa Fluor 488-A 0.011 0.001 0.000 0.000 0.000
## PerCP-Cy5-5-A 0.050 0.548 0.000 0.000 0.008
## APC-A 0.692 0.060 0.000 0.003 0.076
## APC-Cy7-A 0.052 1.105 0.001 0.001 0.006
## Alexa Fluor 405-A 0.004 0.001 0.000 0.000 0.000
## Alexa Fluor 430-A 0.311 0.022 0.000 0.000 0.000
## Qdot 605-A 0.016 0.000 0.002 0.084 0.000
## Qdot 655-A 1.000 0.001 0.000 0.002 0.008
## Qdot 800-A 0.001 1.000 0.000 0.000 0.000
## PE-A 0.130 0.008 1.000 1.567 0.048
## PE-Texas Red-A 0.113 0.011 0.118 1.000 0.050
## PE-Cy5-A 1.427 0.181 0.023 0.043 1.000
## PE-Cy5-5-A 0.017 0.032 0.028 0.041 0.017
## PE-Cy7-A 0.003 0.543 0.011 0.019 0.001
## PE-Cy5-5-A PE-Cy7-A
## Alexa Fluor 488-A 0.000 0.000
## PerCP-Cy5-5-A 0.718 0.047
## APC-A 0.491 0.028
## APC-Cy7-A 0.062 0.388
## Alexa Fluor 405-A 0.000 0.000
## Alexa Fluor 430-A 0.000 0.000
## Qdot 605-A 0.000 0.000
## Qdot 655-A 0.011 0.000
## Qdot 800-A 0.032 0.089
## PE-A 0.425 0.014
## PE-Texas Red-A 0.482 0.020
## PE-Cy5-A 10.262 0.392
## PE-Cy5-5-A 1.000 0.056
## PE-Cy7-A 0.058 1.000
cols = c("Alexa Fluor 405-A", "Alexa Fluor 430-A", "Alexa Fluor 488-A")
round(fs.spill[cols, cols], 3)
## Alexa Fluor 405-A Alexa Fluor 430-A Alexa Fluor 488-A
## Alexa Fluor 405-A 1.000 0.041 0.000
## Alexa Fluor 430-A 0.247 1.000 0.023
## Alexa Fluor 488-A 0.001 0.053 1.000
cols = c("APC-A", "APC-Cy7-A")
round(fs.spill[cols, cols], 3)
## APC-A APC-Cy7-A
## APC-A 1.000 0.068
## APC-Cy7-A 0.075 1.000
cols = c("PE-A", "PE-Cy5-5-A", "PE-Cy5-A", "PE-Cy7-A", "PE-Texas Red-A")
round(fs.spill[cols, cols], 3)
## PE-A PE-Cy5-5-A PE-Cy5-A PE-Cy7-A PE-Texas Red-A
## PE-A 1.000 0.425 0.048 0.014 1.567
## PE-Cy5-5-A 0.028 1.000 0.017 0.056 0.041
## PE-Cy5-A 0.023 10.262 1.000 0.392 0.043
## PE-Cy7-A 0.011 0.058 0.001 1.000 0.019
## PE-Texas Red-A 0.118 0.482 0.050 0.020 1.000
A FlowJo workspace (.jo file) is attached with the OMIP-018 dataset. Although I don’t master FlowJo (v10.4), it’s quite easy to carry out compensation. Put the .jo file with the FCS file, open it with FowJo and click the compensation icon. The result that I get without any tweak is the following. It matches nearly exactly to the flowCore results.
FlowJo full matrix
FlowJo full matrix
Here are some closeup details.
Alexa trio, APC duo
FlowJo Alexa trio, APC duo
PE quintet
FlowJo PE quintet
I don’t recommend such an un-controlled matching.
# Read compensation files
fs = read.flowSet(path = "OMIP-018_FR-FCM-ZZ36", files = c(
dir(pattern = "Unstained.+fcs", path = "OMIP-018_FR-FCM-ZZ36"),
dir(pattern = "Compensatio.+Alexa.+fcs", path = "OMIP-018_FR-FCM-ZZ36")))
pData(fs)
| name | |
|---|---|
| Compensation Controls_Unstained Control.fcs | Compensation Controls_Unstained Control.fcs |
| Compensation Controls_Alexa Fluor 405 Stained Control.fcs | Compensation Controls_Alexa Fluor 405 Stained Control.fcs |
| Compensation Controls_Alexa Fluor 430 Stained Control.fcs | Compensation Controls_Alexa Fluor 430 Stained Control.fcs |
| Compensation Controls_Alexa Fluor 488 Stained Control.fcs | Compensation Controls_Alexa Fluor 488 Stained Control.fcs |
colnames(fs)
## [1] "FSC-A" "SSC-A" "Alexa Fluor 488-A"
## [4] "PerCP-Cy5-5-A" "APC-A" "APC-Cy7-A"
## [7] "Alexa Fluor 405-A" "Alexa Fluor 430-A" "Qdot 605-A"
## [10] "Qdot 655-A" "Qdot 800-A" "PE-A"
## [13] "PE-Texas Red-A" "PE-Cy5-A" "PE-Cy5-5-A"
## [16] "PE-Cy7-A" "Time"
# Check the pattern
grep("Alexa.+-A$", colnames(fs), value = TRUE)
## [1] "Alexa Fluor 488-A" "Alexa Fluor 405-A" "Alexa Fluor 430-A"
# Process
# default FSC, SSC channels are ignored if useNormFilt is FALSE (default)
fs.spill = spillover(fs,
unstained = 1, # unstained is the 1st file
patt = "Alexa.+-A$", # all parameters end with "-A"
stain_match = "intensity")
# Inspection
round(fs.spill, 3)
## Alexa Fluor 488-A Alexa Fluor 405-A Alexa Fluor 430-A
## Alexa Fluor 488-A 1.000 0.001 0.053
## Alexa Fluor 405-A 0.000 1.000 0.041
## Alexa Fluor 430-A 0.023 0.247 1.000
cols = c("Alexa Fluor 405-A", "Alexa Fluor 430-A", "Alexa Fluor 488-A")
round(fs.spill[cols, cols], 3)
## Alexa Fluor 405-A Alexa Fluor 430-A Alexa Fluor 488-A
## Alexa Fluor 405-A 1.000 0.041 0.000
## Alexa Fluor 430-A 0.247 1.000 0.023
## Alexa Fluor 488-A 0.001 0.053 1.000
sessionInfo()
## R version 3.3.3 (2017-03-06)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 7 x64 (build 7601) Service Pack 1
##
## locale:
## [1] LC_COLLATE=English_United Kingdom.1252
## [2] LC_CTYPE=English_United Kingdom.1252
## [3] LC_MONETARY=English_United Kingdom.1252
## [4] LC_NUMERIC=C
## [5] LC_TIME=English_United Kingdom.1252
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] flowCore_1.40.6
##
## loaded via a namespace (and not attached):
## [1] graph_1.52.0 Rcpp_0.12.13 knitr_1.17
## [4] cluster_2.0.6 magrittr_1.5 BiocGenerics_0.20.0
## [7] lattice_0.20-35 rrcov_1.4-3 pcaPP_1.9-72
## [10] highr_0.6 stringr_1.2.0 tools_3.3.3
## [13] parallel_3.3.3 grid_3.3.3 Biobase_2.34.0
## [16] corpcor_1.6.9 htmltools_0.3.6 matrixStats_0.52.2
## [19] yaml_2.1.14 rprojroot_1.2 digest_0.6.12
## [22] robustbase_0.92-7 evaluate_0.10.1 rmarkdown_1.6
## [25] stringi_1.1.5 DEoptimR_1.0-8 backports_1.1.0
## [28] stats4_3.3.3 mvtnorm_1.0-6