Code
# If not yet done so, install required packages
install.packages("BiocManager")
::install("BiocStyle")
BiocManager::install("flowCore")
BiocManager::install("ggcyto")
BiocManager::install("FlowSOM") BiocManager
Compensation in flowCore
If you haven’t already, please go ahead and install the following packages:
# If not yet done so, install required packages
install.packages("BiocManager")
::install("BiocStyle")
BiocManager::install("flowCore")
BiocManager::install("ggcyto")
BiocManager::install("FlowSOM") BiocManager
Once installed, load the required packages into R by calling library.
library(flowCore)
library(ggcyto)
library(BiocStyle)
library(FlowSOM)
For this example, I am using an .fcs file contained within the FlowSOM package’s extdata folder. I find it using system.file, and then list.files to show the full path to it’s location. Once it’s located, I will load it into a flowframe via flowCore similar to what we can see in the Christopher Hall video.
<- system.file("extdata", package = "FlowSOM")
File_Location <- list.files(File_Location, pattern="fcs", full.names=TRUE)
FCSFile_Example <- read.FCS(FCSFile_Example, transformation = FALSE, truncate_max_range = FALSE) flowframe
If you have your own .fcs file, instead of system.file I usually will use file.path to indicate the location. For example:
<- file.path("C:", "Users", "StepUpCytometry", "Desktop", "TodaysExperiment")
Location <- list.files(Location, pattern=".fcs", full.names=TRUE)
ExperimentFCSFiles <- read.FCS(FCSFile_Example[1], transformation = FALSE, truncate_max_range = FALSE) flowframe
Once you have loaded the .fcs file into a flowframe, let’s quickly poke around to see what this kind of object looks like in R.
flowframe
flowFrame object '698c2f7d-15be-452c-b0c4-4bbb2d55db06'
with 19225 cells and 18 observables:
name desc range minRange maxRange
$P1 Time NA 262144 0 262144
$P2 FSC-A NA 262144 0 262144
$P3 FSC-H NA 262144 0 262144
$P4 FSC-W NA 262144 0 262144
$P5 SSC-A NA 262144 0 262144
... ... ... ... ... ...
$P14 APC-Cy7-A TCRb 262144 -111.0 262144
$P15 PE-A NK1/1 262144 -111.0 262144
$P16 PE-Texas Red-A CD4 262144 -111.0 262144
$P17 PE-Cy5-A CD19 262144 -111.0 262144
$P18 PE-Cy7-A CD3 262144 -93.6 262144
248 keywords are stored in the 'description' slot
Within flowCore flowframe and flowset objects, the information for an individual .fcs file is contained within exprs (data), parameters and description (keywords). For this example I will abbreviate them to the first 10 entries, if you want to see the full entries, run the code-chunks below on your own computer.
View(flowframe@description)
$`$BEGINANALYSIS`
[1] "0"
$`$BEGINDATA`
[1] "5481"
$`$BEGINSTEXT`
[1] "0"
$`$BTIM`
[1] "00:29:01"
$`$BYTEORD`
[1] "4,3,2,1"
$`$DATATYPE`
[1] "F"
$`$DATE`
[1] "02-JUL-2013"
$`$ENDANALYSIS`
[1] "0"
$`$ENDDATA`
[1] "1389680"
$`$ENDSTEXT`
[1] "0"
Description (keywords) is a named list containing everything from voltage settings, compensation matrix, metadata, software configurations, etc.
View(flowframe@parameters@data)
name desc range minRange maxRange
$P1 Time <NA> 262144 0.00 262144
$P2 FSC-A <NA> 262144 0.00 262144
$P3 FSC-H <NA> 262144 0.00 262144
$P4 FSC-W <NA> 262144 0.00 262144
$P5 SSC-A <NA> 262144 0.00 262144
$P6 SSC-H <NA> 262144 0.00 262144
$P7 SSC-W <NA> 262144 0.00 262144
$P8 FITC-A GFP 262144 -44.25 262144
$P9 Pacific Blue-A CD8 262144 -111.00 262144
$P10 AmCyan-A l/d 262144 -111.00 262144
$P11 Qdot 605-A <NA> 262144 -111.00 262144
$P12 APC-A TCRyd 262144 -111.00 262144
$P13 Alexa Fluor 700-A CD45 262144 -58.80 262144
$P14 APC-Cy7-A TCRb 262144 -111.00 262144
$P15 PE-A NK1/1 262144 -111.00 262144
$P16 PE-Texas Red-A CD4 262144 -111.00 262144
$P17 PE-Cy5-A CD19 262144 -111.00 262144
$P18 PE-Cy7-A CD3 262144 -93.60 262144
Parameters is one of the many locations containing the name of the fluorophore, and the ligand name.
View(flowframe@exprs)
Time FSC-A FSC-H FSC-W SSC-A SSC-H SSC-W FITC-A
[1,] 0.0 110519.01 69460.30 104275 42186.75 70610.42 39155 22.50
[2,] 0.2 77340.06 67542.95 75042 54567.75 80071.48 44662 54.75
[3,] 0.5 89551.35 67677.27 86718 46320.00 75690.11 40106 43.50
[4,] 0.9 78047.55 68193.54 75006 44152.50 68023.37 42538 63.75
[5,] 1.0 77033.25 67900.25 74351 33922.50 68753.52 32335 25.50
[6,] 1.3 97850.34 69162.95 92719 39126.00 68525.66 37419 46.50
[7,] 1.5 90782.37 67412.77 88255 33993.75 68022.80 32751 47.25
[8,] 1.5 56237.58 66731.59 55230 27647.25 66983.00 27050 9.75
[9,] 2.1 85523.76 68674.70 81615 36888.75 68588.56 35247 60.75
[10,] 2.2 84030.03 66443.77 82882 19114.50 68897.15 18182 51.00
Pacific Blue-A AmCyan-A Qdot 605-A APC-A Alexa Fluor 700-A
[1,] 124.62 353.40 2484.96 670.32001 6499.920
[2,] 152.52 582.18 1831.17 2626.67993 2898.000
[3,] 38.13 348.75 6149.16 8242.08008 4729.200
[4,] 46.50 526.38 679.83 225.95999 6376.440
[5,] 50.22 171.12 60.45 54.60000 4185.720
[6,] 5514.90 1886.04 342.24 811.44000 6111.000
[7,] 15879.75 4655.58 121.83 367.07999 8005.200
[8,] 93.93 331.08 166.47 97.43999 -37.800
[9,] 7871.52 2282.22 242.73 549.35999 9285.359
[10,] 19.53 -106.02 919.77 420.84000 4166.400
APC-Cy7-A PE-A PE-Texas Red-A PE-Cy5-A PE-Cy7-A
[1,] 7297.07959 6518.85 23279.10 9873.50 11799.45
[2,] 1798.43994 1076.40 484.25 1264.25 10302.50
[3,] 2652.71997 1014.00 587.60 29994.90 2083.25
[4,] 3202.07983 44454.15 12822.55 3282.50 588.90
[5,] 1896.71997 107.90 65.00 -165.75 295.10
[6,] 5848.07959 467.35 66.95 744.90 5811.65
[7,] 6662.03955 353.60 52.00 148.85 5317.00
[8,] -90.71999 -64.35 -21.45 152.10 48.75
[9,] 6241.19971 228.15 74.75 148.85 4195.75
[10,] 5498.63965 2813.20 7030.40 3245.45 11470.55
And exprs contains the actual raw data, with each row representing the measurements of an individual cell.
For the question of compensation, the compensation matrix is stored under keyword “SPILL” within the description (keyword) list and is a matrix object.
@description[["SPILL"]] flowframe
FITC-A Pacific Blue-A AmCyan-A Qdot 605-A APC-A
[1,] 1.000000e+00 0.0473734538 0.7135625558 0.053295127 0.017115568
[2,] 4.843844e-04 1.0000000000 0.2714277161 0.006727130 0.001736034
[3,] 8.109116e-04 0.0776772290 1.0000000000 0.048265462 0.001816442
[4,] 8.625151e-05 0.0002495544 0.0005347592 1.000000000 0.066816169
[5,] 4.354693e-04 0.0000000000 0.0025199164 0.230572282 1.000000000
[6,] 1.246572e-03 0.0046372490 0.0100473729 0.019708302 0.026527051
[7,] 0.000000e+00 0.0022519623 0.0028149513 0.024208587 0.085429249
[8,] 1.166385e-03 0.0017175023 0.0059660591 0.019886864 0.001551292
[9,] 5.446396e-04 0.0005402827 0.0016208480 0.099715889 0.001768989
[10,] 3.372389e-04 0.0005018117 0.0016727050 0.224393397 0.252233134
[11,] 8.522607e-04 0.0031704107 0.0068692232 0.004755614 0.003579495
Alexa Fluor 700-A APC-Cy7-A PE-A PE-Texas Red-A PE-Cy5-A
[1,] 0.0010697231 8.659662e-04 0.009933141 0.0024832865 0.004966571
[2,] 0.0001085021 0.000000e+00 0.000000000 0.0000000000 0.000000000
[3,] 0.0011352762 1.773193e-03 0.000000000 0.0001756974 0.003689648
[4,] 0.0002576045 2.606712e-05 0.000000000 0.0039119851 0.121894463
[5,] 0.1391643652 7.020152e-02 0.000000000 0.0018870336 0.268965180
[6,] 1.0000000000 4.934763e-01 0.000000000 0.0000000000 0.009723261
[7,] 0.0572918382 1.000000e+00 0.000000000 0.0000000000 0.023871598
[8,] 0.0002449408 0.000000e+00 1.000000000 0.3045236332 0.075751838
[9,] 0.0001829989 0.000000e+00 0.266786250 1.0000000000 0.426046132
[10,] 0.0405280277 2.108778e-02 0.032047816 0.0108725826 1.000000000
[11,] 0.0033408617 5.555603e-02 0.069246179 0.0221587763 0.013479923
PE-Cy7-A
[1,] 0.000000e+00
[2,] 3.875075e-05
[3,] 9.595787e-04
[4,] 3.641730e-05
[5,] 1.276409e-02
[6,] 5.765395e-02
[7,] 1.766660e-01
[8,] 3.567193e-03
[9,] 2.574693e-02
[10,] 6.295464e-02
[11,] 1.000000e+00
In the case of Christopher Hall’s video #2’s example, if we look at the initial data (from flowframe@exprs) we can see the initial values for the first ten cells
<- flowframe@exprs
Example head(Example, 10)
Time FSC-A FSC-H FSC-W SSC-A SSC-H SSC-W FITC-A
[1,] 0.0 110519.01 69460.30 104275 42186.75 70610.42 39155 22.50
[2,] 0.2 77340.06 67542.95 75042 54567.75 80071.48 44662 54.75
[3,] 0.5 89551.35 67677.27 86718 46320.00 75690.11 40106 43.50
[4,] 0.9 78047.55 68193.54 75006 44152.50 68023.37 42538 63.75
[5,] 1.0 77033.25 67900.25 74351 33922.50 68753.52 32335 25.50
[6,] 1.3 97850.34 69162.95 92719 39126.00 68525.66 37419 46.50
[7,] 1.5 90782.37 67412.77 88255 33993.75 68022.80 32751 47.25
[8,] 1.5 56237.58 66731.59 55230 27647.25 66983.00 27050 9.75
[9,] 2.1 85523.76 68674.70 81615 36888.75 68588.56 35247 60.75
[10,] 2.2 84030.03 66443.77 82882 19114.50 68897.15 18182 51.00
Pacific Blue-A AmCyan-A Qdot 605-A APC-A Alexa Fluor 700-A
[1,] 124.62 353.40 2484.96 670.32001 6499.920
[2,] 152.52 582.18 1831.17 2626.67993 2898.000
[3,] 38.13 348.75 6149.16 8242.08008 4729.200
[4,] 46.50 526.38 679.83 225.95999 6376.440
[5,] 50.22 171.12 60.45 54.60000 4185.720
[6,] 5514.90 1886.04 342.24 811.44000 6111.000
[7,] 15879.75 4655.58 121.83 367.07999 8005.200
[8,] 93.93 331.08 166.47 97.43999 -37.800
[9,] 7871.52 2282.22 242.73 549.35999 9285.359
[10,] 19.53 -106.02 919.77 420.84000 4166.400
APC-Cy7-A PE-A PE-Texas Red-A PE-Cy5-A PE-Cy7-A
[1,] 7297.07959 6518.85 23279.10 9873.50 11799.45
[2,] 1798.43994 1076.40 484.25 1264.25 10302.50
[3,] 2652.71997 1014.00 587.60 29994.90 2083.25
[4,] 3202.07983 44454.15 12822.55 3282.50 588.90
[5,] 1896.71997 107.90 65.00 -165.75 295.10
[6,] 5848.07959 467.35 66.95 744.90 5811.65
[7,] 6662.03955 353.60 52.00 148.85 5317.00
[8,] -90.71999 -64.35 -21.45 152.10 48.75
[9,] 6241.19971 228.15 74.75 148.85 4195.75
[10,] 5498.63965 2813.20 7030.40 3245.45 11470.55
And after compensation, we can compare the values to see how they change once compensation is applied:
<- spillover(flowframe)
TheComps <- compensate(flowframe, TheComps[[1]])
flowframe_comped head(flowframe_comped@exprs, 10)
Time FSC-A FSC-H FSC-W SSC-A SSC-H SSC-W FITC-A
[1,] 0.0 110519.01 69460.30 104275 42186.75 70610.42 39155 -6.313441
[2,] 0.2 77340.06 67542.95 75042 54567.75 80071.48 44662 40.889262
[3,] 0.5 89551.35 67677.27 86718 46320.00 75690.11 40106 28.703288
[4,] 0.9 78047.55 68193.54 75006 44152.50 68023.37 42538 3.952831
[5,] 1.0 77033.25 67900.25 74351 33922.50 68753.52 32335 20.015388
[6,] 1.3 97850.34 69162.95 92719 39126.00 68525.66 37419 31.672404
[7,] 1.5 90782.37 67412.77 88255 33993.75 68022.80 32751 25.865051
[8,] 1.5 56237.58 66731.59 55230 27647.25 66983.00 27050 9.484324
[9,] 2.1 85523.76 68674.70 81615 36888.75 68588.56 35247 42.592859
[10,] 2.2 84030.03 66443.77 82882 19114.50 68897.15 18182 33.278025
Pacific Blue-A AmCyan-A Qdot 605-A APC-A Alexa Fluor 700-A
[1,] 30.36604 171.18751 -63.67789 196.18180 6237.55480
[2,] 72.73710 429.49950 1085.01643 2404.24067 2529.70261
[3,] -13.50348 245.75616 -797.27920 672.76698 3412.65144
[4,] -76.47428 214.64085 -304.80468 -50.59753 6360.16110
[5,] 21.38416 108.33454 18.42346 14.75374 4202.85112
[6,] 5441.98121 283.50222 -87.46906 276.15010 5882.77782
[7,] 15804.39212 230.99478 -213.06889 -95.08870 7854.81455
[8,] 69.99279 305.62480 111.34597 64.67410 -48.09541
[9,] 7811.96835 11.47178 -72.01849 169.90971 9164.87054
[10,] -24.75892 -256.30967 34.39165 -18.69712 3958.50180
APC-Cy7-A PE-A PE-Texas Red-A PE-Cy5-A PE-Cy7-A
[1,] 3643.61990 -359.137162 23165.188223 -299.69303 10217.17443
[2,] -187.81341 326.643836 148.564628 238.72639 10138.67126
[3,] 296.54263 -7.998177 269.458448 29756.36525 -54.89582
[4,] 58.79272 44650.326470 -776.964679 217.08501 59.34040
[5,] -179.04186 99.061786 35.034765 -232.79967 97.52624
[6,] 2637.97557 129.750163 -88.694117 520.85534 4971.46997
[7,] 2546.33437 64.102696 -64.709637 25.46337 4414.51005
[8,] -77.91719 -71.686136 -2.898771 128.19014 56.42285
[9,] 1518.92617 -6.299055 1.826056 -59.83295 3400.27881
[10,] 2958.99121 289.975885 6707.161943 116.35005 10539.00981
As you might be able to tell, FSC SSC parameters remain the same, while the values for the Fluorophore columns have been adjusted. This is due to no columns being present for FSC SSC Time etc. in the Spillover matrix.
We can visualize this with the with the ggcyto package to see the effects for the before vs. after:
autoplot(flowframe, x = "PE-Cy7-A", y = "PE-Texas Red-A", bins = 270) +
scale_x_flowjo_biexp() + scale_y_flowjo_biexp() + theme_bw()
autoplot(flowframe_comped, x = "PE-Cy7-A", y = "PE-Texas Red-A", bins = 270) +
scale_x_flowjo_biexp() + scale_y_flowjo_biexp() + theme_bw()
The above works great when the acquisition software already stored the compensation matrix in the SPILL keyword. But what happens when it is not there, or we want to apply an external compensation matrix? It can be done, but there is a bit more data-tidying that needs to be done first to make the .csv output from FlowJo compatible with the matrix format flowCore stores SPILL as. To save someone who is just getting started a headache, here is a worked out example below:
If we look closer at what type of object is returned by spillover, we can see that the initial command is returning three list:
<- spillover(flowframe)
TheComps TheComps
$SPILL
FITC-A Pacific Blue-A AmCyan-A Qdot 605-A APC-A
[1,] 1.000000e+00 0.0473734538 0.7135625558 0.053295127 0.017115568
[2,] 4.843844e-04 1.0000000000 0.2714277161 0.006727130 0.001736034
[3,] 8.109116e-04 0.0776772290 1.0000000000 0.048265462 0.001816442
[4,] 8.625151e-05 0.0002495544 0.0005347592 1.000000000 0.066816169
[5,] 4.354693e-04 0.0000000000 0.0025199164 0.230572282 1.000000000
[6,] 1.246572e-03 0.0046372490 0.0100473729 0.019708302 0.026527051
[7,] 0.000000e+00 0.0022519623 0.0028149513 0.024208587 0.085429249
[8,] 1.166385e-03 0.0017175023 0.0059660591 0.019886864 0.001551292
[9,] 5.446396e-04 0.0005402827 0.0016208480 0.099715889 0.001768989
[10,] 3.372389e-04 0.0005018117 0.0016727050 0.224393397 0.252233134
[11,] 8.522607e-04 0.0031704107 0.0068692232 0.004755614 0.003579495
Alexa Fluor 700-A APC-Cy7-A PE-A PE-Texas Red-A PE-Cy5-A
[1,] 0.0010697231 8.659662e-04 0.009933141 0.0024832865 0.004966571
[2,] 0.0001085021 0.000000e+00 0.000000000 0.0000000000 0.000000000
[3,] 0.0011352762 1.773193e-03 0.000000000 0.0001756974 0.003689648
[4,] 0.0002576045 2.606712e-05 0.000000000 0.0039119851 0.121894463
[5,] 0.1391643652 7.020152e-02 0.000000000 0.0018870336 0.268965180
[6,] 1.0000000000 4.934763e-01 0.000000000 0.0000000000 0.009723261
[7,] 0.0572918382 1.000000e+00 0.000000000 0.0000000000 0.023871598
[8,] 0.0002449408 0.000000e+00 1.000000000 0.3045236332 0.075751838
[9,] 0.0001829989 0.000000e+00 0.266786250 1.0000000000 0.426046132
[10,] 0.0405280277 2.108778e-02 0.032047816 0.0108725826 1.000000000
[11,] 0.0033408617 5.555603e-02 0.069246179 0.0221587763 0.013479923
PE-Cy7-A
[1,] 0.000000e+00
[2,] 3.875075e-05
[3,] 9.595787e-04
[4,] 3.641730e-05
[5,] 1.276409e-02
[6,] 5.765395e-02
[7,] 1.766660e-01
[8,] 3.567193e-03
[9,] 2.574693e-02
[10,] 6.295464e-02
[11,] 1.000000e+00
$spillover
NULL
$`$SPILLOVER`
NULL
And that the first item that we passed to compensate is the matrix array object containing the spillover/compensation matrix. If we didn’t have it in the fcs file to begin with (either due to a difference in acquisition software, or desire to apply a separate compensation matrix you generated elsewhere), you could format it similarly and swap it in.
1]] TheComps[[
FITC-A Pacific Blue-A AmCyan-A Qdot 605-A APC-A
[1,] 1.000000e+00 0.0473734538 0.7135625558 0.053295127 0.017115568
[2,] 4.843844e-04 1.0000000000 0.2714277161 0.006727130 0.001736034
[3,] 8.109116e-04 0.0776772290 1.0000000000 0.048265462 0.001816442
[4,] 8.625151e-05 0.0002495544 0.0005347592 1.000000000 0.066816169
[5,] 4.354693e-04 0.0000000000 0.0025199164 0.230572282 1.000000000
[6,] 1.246572e-03 0.0046372490 0.0100473729 0.019708302 0.026527051
[7,] 0.000000e+00 0.0022519623 0.0028149513 0.024208587 0.085429249
[8,] 1.166385e-03 0.0017175023 0.0059660591 0.019886864 0.001551292
[9,] 5.446396e-04 0.0005402827 0.0016208480 0.099715889 0.001768989
[10,] 3.372389e-04 0.0005018117 0.0016727050 0.224393397 0.252233134
[11,] 8.522607e-04 0.0031704107 0.0068692232 0.004755614 0.003579495
Alexa Fluor 700-A APC-Cy7-A PE-A PE-Texas Red-A PE-Cy5-A
[1,] 0.0010697231 8.659662e-04 0.009933141 0.0024832865 0.004966571
[2,] 0.0001085021 0.000000e+00 0.000000000 0.0000000000 0.000000000
[3,] 0.0011352762 1.773193e-03 0.000000000 0.0001756974 0.003689648
[4,] 0.0002576045 2.606712e-05 0.000000000 0.0039119851 0.121894463
[5,] 0.1391643652 7.020152e-02 0.000000000 0.0018870336 0.268965180
[6,] 1.0000000000 4.934763e-01 0.000000000 0.0000000000 0.009723261
[7,] 0.0572918382 1.000000e+00 0.000000000 0.0000000000 0.023871598
[8,] 0.0002449408 0.000000e+00 1.000000000 0.3045236332 0.075751838
[9,] 0.0001829989 0.000000e+00 0.266786250 1.0000000000 0.426046132
[10,] 0.0405280277 2.108778e-02 0.032047816 0.0108725826 1.000000000
[11,] 0.0033408617 5.555603e-02 0.069246179 0.0221587763 0.013479923
PE-Cy7-A
[1,] 0.000000e+00
[2,] 3.875075e-05
[3,] 9.595787e-04
[4,] 3.641730e-05
[5,] 1.276409e-02
[6,] 5.765395e-02
[7,] 1.766660e-01
[8,] 3.567193e-03
[9,] 2.574693e-02
[10,] 6.295464e-02
[11,] 1.000000e+00
class(TheComps[[1]])
[1] "matrix" "array"
In the case of the FlowSOM example file, the compensation applied appears to be mostly correct. The closest example to an overcompensation was the following:
autoplot(flowframe_comped, x = "Qdot 605-A", y = "Alexa Fluor 700-A", bins = 270) +
scale_x_flowjo_biexp() + scale_y_flowjo_biexp() + theme_bw()
In FlowJo, I edited the compensation matrix and then hit Save Matrix and saved as a .csv. This can then be imported into R on an individual computer something similar to as follows:
# Set Location for your own folder
<- file.path("C:", "Users", "StepUpCytometry", "Desktop", "MyCompensations")
Location <- list.files(Location, pattern = ".csv", full.names = TRUE)
TheMatrix <- TheMatrix[4] # Ie, the 4th .csv file in the Matrix list above MyMatrixOfInterest
<- read.csv("MyComp.csv", check.names=FALSE)
MyCompensation MyCompensation
FITC-A :: GFP Pacific Blue-A :: CD8
1 FITC-A :: GFP 1.0000000000 0.0473734550
2 Pacific Blue-A :: CD8 0.0004843844 1.0000000000
3 AmCyan-A :: l_d 0.0008109116 0.0776772276
4 Qdot 605-A 0.0000862515 0.0002495544
5 APC-A :: TCRyd 0.0004354693 0.0000000000
6 Alexa Fluor 700-A :: CD45 0.0012465719 0.0046372488
7 APC-Cy7-A :: TCRb 0.0000000000 0.0022519622
8 PE-A :: NK1_1 0.0011663849 0.0017175023
9 PE-Texas Red-A :: CD4 0.0005446396 0.0005402827
10 PE-Cy5-A :: CD19 0.0003372389 0.0005018117
11 PE-Cy7-A :: CD3 0.0008522607 0.0031704106
AmCyan-A :: l_d Qdot 605-A APC-A :: TCRyd Alexa Fluor 700-A :: CD45
1 0.7135625482 0.053295128 0.017115569 0.0010697230
2 0.2714277208 0.006727130 0.001736034 0.0001085021
3 1.0000000000 0.048265461 0.001816442 0.0011352762
4 0.0005347593 1.000000000 0.066816166 0.0000000000
5 0.0025199165 0.230572283 1.000000000 0.1391643584
6 0.0100473734 -0.010000000 0.026527051 1.0000000000
7 0.0028149514 0.024208587 0.085429251 0.0572918393
8 0.0059660589 0.019886864 0.001551292 0.0002449408
9 0.0016208480 0.099715889 0.001768990 0.0001829989
10 0.0016727051 0.224393398 0.252233148 0.0405280292
11 0.0068692234 0.004755614 0.003579495 0.0033408618
APC-Cy7-A :: TCRb PE-A :: NK1_1 PE-Texas Red-A :: CD4 PE-Cy5-A :: CD19
1 0.0008659662 0.009933141 0.0024832867 0.004966570
2 0.0000000000 0.000000000 0.0000000000 0.000000000
3 0.0017731935 0.000000000 0.0001756974 0.003689648
4 0.0000260671 0.000000000 0.0039119851 0.121894464
5 0.0702015162 0.000000000 0.0018870336 0.268965185
6 0.4934762716 0.000000000 0.0000000000 0.009723261
7 1.0000000000 0.000000000 0.0000000000 0.023871597
8 0.0000000000 1.000000000 0.3045236468 0.075751841
9 0.0000000000 0.266786248 1.0000000000 0.426046133
10 0.0210877769 0.032047816 0.0108725829 1.000000000
11 0.0555560328 0.069246180 0.0221587755 0.013479923
PE-Cy7-A :: CD3
1 0.0000000000
2 0.0000387507
3 0.0009595787
4 0.0000364173
5 0.0127640879
6 0.0576539524
7 0.1766659617
8 0.0035671934
9 0.0257469285
10 0.0629546419
11 1.0000000000
If we compare the above to the matrix currently stored in spillover, we can see a couple potential issues, namely, the FlowJo exported .csv has the names of the ligands still included; and the row.names column is still present.
1] TheComps[
$SPILL
FITC-A Pacific Blue-A AmCyan-A Qdot 605-A APC-A
[1,] 1.000000e+00 0.0473734538 0.7135625558 0.053295127 0.017115568
[2,] 4.843844e-04 1.0000000000 0.2714277161 0.006727130 0.001736034
[3,] 8.109116e-04 0.0776772290 1.0000000000 0.048265462 0.001816442
[4,] 8.625151e-05 0.0002495544 0.0005347592 1.000000000 0.066816169
[5,] 4.354693e-04 0.0000000000 0.0025199164 0.230572282 1.000000000
[6,] 1.246572e-03 0.0046372490 0.0100473729 0.019708302 0.026527051
[7,] 0.000000e+00 0.0022519623 0.0028149513 0.024208587 0.085429249
[8,] 1.166385e-03 0.0017175023 0.0059660591 0.019886864 0.001551292
[9,] 5.446396e-04 0.0005402827 0.0016208480 0.099715889 0.001768989
[10,] 3.372389e-04 0.0005018117 0.0016727050 0.224393397 0.252233134
[11,] 8.522607e-04 0.0031704107 0.0068692232 0.004755614 0.003579495
Alexa Fluor 700-A APC-Cy7-A PE-A PE-Texas Red-A PE-Cy5-A
[1,] 0.0010697231 8.659662e-04 0.009933141 0.0024832865 0.004966571
[2,] 0.0001085021 0.000000e+00 0.000000000 0.0000000000 0.000000000
[3,] 0.0011352762 1.773193e-03 0.000000000 0.0001756974 0.003689648
[4,] 0.0002576045 2.606712e-05 0.000000000 0.0039119851 0.121894463
[5,] 0.1391643652 7.020152e-02 0.000000000 0.0018870336 0.268965180
[6,] 1.0000000000 4.934763e-01 0.000000000 0.0000000000 0.009723261
[7,] 0.0572918382 1.000000e+00 0.000000000 0.0000000000 0.023871598
[8,] 0.0002449408 0.000000e+00 1.000000000 0.3045236332 0.075751838
[9,] 0.0001829989 0.000000e+00 0.266786250 1.0000000000 0.426046132
[10,] 0.0405280277 2.108778e-02 0.032047816 0.0108725826 1.000000000
[11,] 0.0033408617 5.555603e-02 0.069246179 0.0221587763 0.013479923
PE-Cy7-A
[1,] 0.000000e+00
[2,] 3.875075e-05
[3,] 9.595787e-04
[4,] 3.641730e-05
[5,] 1.276409e-02
[6,] 5.765395e-02
[7,] 1.766660e-01
[8,] 3.567193e-03
[9,] 2.574693e-02
[10,] 6.295464e-02
[11,] 1.000000e+00
We could of course edit this manually by hand, or we can have R do it. First we remove the row name column:
<- MyCompensation[-1] #Removed the first rowname column
MyCompensation colnames(MyCompensation)
[1] "FITC-A :: GFP" "Pacific Blue-A :: CD8"
[3] "AmCyan-A :: l_d" "Qdot 605-A"
[5] "APC-A :: TCRyd" "Alexa Fluor 700-A :: CD45"
[7] "APC-Cy7-A :: TCRb" "PE-A :: NK1_1"
[9] "PE-Texas Red-A :: CD4" "PE-Cy5-A :: CD19"
[11] "PE-Cy7-A :: CD3"
With that done, we remove everything within the column names that is present after -A:
colnames(MyCompensation) <- sub("-A.*", "-A", colnames(MyCompensation))
colnames(MyCompensation)
[1] "FITC-A" "Pacific Blue-A" "AmCyan-A"
[4] "Qdot 605-A" "APC-A" "Alexa Fluor 700-A"
[7] "APC-Cy7-A" "PE-A" "PE-Texas Red-A"
[10] "PE-Cy5-A" "PE-Cy7-A"
And finally, we convert it from a data.frame to a matrix
<- as.matrix(MyCompensation)
MyCompensation MyCompensation
FITC-A Pacific Blue-A AmCyan-A Qdot 605-A APC-A
[1,] 1.0000000000 0.0473734550 0.7135625482 0.053295128 0.017115569
[2,] 0.0004843844 1.0000000000 0.2714277208 0.006727130 0.001736034
[3,] 0.0008109116 0.0776772276 1.0000000000 0.048265461 0.001816442
[4,] 0.0000862515 0.0002495544 0.0005347593 1.000000000 0.066816166
[5,] 0.0004354693 0.0000000000 0.0025199165 0.230572283 1.000000000
[6,] 0.0012465719 0.0046372488 0.0100473734 -0.010000000 0.026527051
[7,] 0.0000000000 0.0022519622 0.0028149514 0.024208587 0.085429251
[8,] 0.0011663849 0.0017175023 0.0059660589 0.019886864 0.001551292
[9,] 0.0005446396 0.0005402827 0.0016208480 0.099715889 0.001768990
[10,] 0.0003372389 0.0005018117 0.0016727051 0.224393398 0.252233148
[11,] 0.0008522607 0.0031704106 0.0068692234 0.004755614 0.003579495
Alexa Fluor 700-A APC-Cy7-A PE-A PE-Texas Red-A PE-Cy5-A
[1,] 0.0010697230 0.0008659662 0.009933141 0.0024832867 0.004966570
[2,] 0.0001085021 0.0000000000 0.000000000 0.0000000000 0.000000000
[3,] 0.0011352762 0.0017731935 0.000000000 0.0001756974 0.003689648
[4,] 0.0000000000 0.0000260671 0.000000000 0.0039119851 0.121894464
[5,] 0.1391643584 0.0702015162 0.000000000 0.0018870336 0.268965185
[6,] 1.0000000000 0.4934762716 0.000000000 0.0000000000 0.009723261
[7,] 0.0572918393 1.0000000000 0.000000000 0.0000000000 0.023871597
[8,] 0.0002449408 0.0000000000 1.000000000 0.3045236468 0.075751841
[9,] 0.0001829989 0.0000000000 0.266786248 1.0000000000 0.426046133
[10,] 0.0405280292 0.0210877769 0.032047816 0.0108725829 1.000000000
[11,] 0.0033408618 0.0555560328 0.069246180 0.0221587755 0.013479923
PE-Cy7-A
[1,] 0.0000000000
[2,] 0.0000387507
[3,] 0.0009595787
[4,] 0.0000364173
[5,] 0.0127640879
[6,] 0.0576539524
[7,] 0.1766659617
[8,] 0.0035671934
[9,] 0.0257469285
[10,] 0.0629546419
[11,] 1.0000000000
Now, returning to the original example of applying the spillover, we provide the external:
<- compensate(flowframe, MyCompensation) flowframe_ExternalComp
And visualize the effect before and after:
autoplot(flowframe_comped, x = "Qdot 605-A", y = "Alexa Fluor 700-A", bins = 270) +
scale_x_flowjo_biexp() + scale_y_flowjo_biexp() + theme_bw()
autoplot(flowframe_ExternalComp, x = "Qdot 605-A", y = "Alexa Fluor 700-A", bins = 270) +
scale_x_flowjo_biexp() + scale_y_flowjo_biexp() + theme_bw()
Notice it did move (still ugly-ish but I only barely adjusted the compensation matrix due to lack of time).
Hope this helps clarify a little!!! Keep it up!
sessionInfo()
R version 4.4.1 (2024-06-14 ucrt)
Platform: x86_64-w64-mingw32/x64
Running under: Windows 11 x64 (build 22631)
Matrix products: default
locale:
[1] LC_COLLATE=English_United States.utf8
[2] LC_CTYPE=English_United States.utf8
[3] LC_MONETARY=English_United States.utf8
[4] LC_NUMERIC=C
[5] LC_TIME=English_United States.utf8
time zone: America/New_York
tzcode source: internal
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] FlowSOM_2.12.0 igraph_2.1.1 BiocStyle_2.32.1
[4] ggcyto_1.32.0 flowWorkspace_4.16.0 ncdfFlow_2.50.0
[7] BH_1.84.0-0 ggplot2_3.5.1 flowCore_2.16.0
loaded via a namespace (and not attached):
[1] gtable_0.3.5 xfun_0.48
[3] htmlwidgets_1.6.4 ConsensusClusterPlus_1.68.0
[5] rstatix_0.7.2 Biobase_2.64.0
[7] lattice_0.22-6 vctrs_0.6.5
[9] tools_4.4.1 generics_0.1.3
[11] stats4_4.4.1 tibble_3.2.1
[13] fansi_1.0.6 cluster_2.1.6
[15] pkgconfig_2.0.3 data.table_1.16.2
[17] ggnewscale_0.5.0 RColorBrewer_1.1-3
[19] S4Vectors_0.42.1 graph_1.82.0
[21] lifecycle_1.0.4 compiler_4.4.1
[23] farver_2.1.2 munsell_0.5.1
[25] ggforce_0.4.2 carData_3.0-5
[27] htmltools_0.5.8.1 yaml_2.3.10
[29] Formula_1.2-5 car_3.1-3
[31] tidyr_1.3.1 hexbin_1.28.4
[33] pillar_1.9.0 ggpubr_0.6.0
[35] MASS_7.3-60.2 abind_1.4-8
[37] RProtoBufLib_2.16.0 tidyselect_1.2.1
[39] digest_0.6.37 Rtsne_0.17
[41] purrr_1.0.2 dplyr_1.1.4
[43] labeling_0.4.3 polyclip_1.10-7
[45] fastmap_1.2.0 grid_4.4.1
[47] colorspace_2.1-1 cli_3.6.3
[49] magrittr_2.0.3 XML_3.99-0.17
[51] utf8_1.2.4 broom_1.0.7
[53] withr_3.0.1 backports_1.5.0
[55] scales_1.3.0 rmarkdown_2.28
[57] matrixStats_1.4.1 gridExtra_2.3
[59] ggsignif_0.6.4 cytolib_2.16.0
[61] evaluate_1.0.1 knitr_1.48
[63] rlang_1.1.4 Rcpp_1.0.13
[65] glue_1.8.0 Rgraphviz_2.48.0
[67] tweenr_2.0.3 BiocManager_1.30.25
[69] BiocGenerics_0.50.0 rstudioapi_0.17.0
[71] jsonlite_1.8.9 R6_2.5.1
[73] plyr_1.8.9 zlibbioc_1.50.0
[75] colorRamps_2.3.4