Computing Compensation from Controls
The compensation page applies the spillover matrix stored in the file. That is the normal case and it is usually right. This page is about the other case: computing the matrix yourself from single stain controls, and what you find when you compare the two.
Match the controls to the channels first
Section titled “Match the controls to the channels first”A single stain control is one tube stained with one antibody. A ten colour panel needs ten of them plus an unstained tube, and the first job is deciding which control belongs to which detector. The file names carry the stain, the panel carries the channel, and the two rarely agree on spelling.
match_table <- MatchControlsToChannels(controls, panel)head(match_table, 3)"filename","stain","channel","marker","matched_by""Single stainings_CD2 PerCP-Cy55_011.fcs","CD2 PerCP-Cy55","b-LP685 710/50-A-A","CD2 PerCPCy55","collapsed""Single stainings_CD3 PE-Cy5_004.fcs","CD3 PE-Cy5","yg-LP635 670/30-B-A","CD3 PECy5","collapsed""Single stainings_CD56 PE-Dazzle594_005.fcs","CD56 PE-Dazzle594","yg-LP600 610/20-C-A","CD56 PEDazzle594","collapsed"The matched_by column records how each match was made. collapsed means the two names
agreed only after punctuation and case were stripped, which is why PerCP-Cy55 in the file
name and PerCPCy55 in the panel end up on the same row. Recording the method matters,
because a match made by a loose rule is a match to check.
Check the controls before you trust them
Section titled “Check the controls before you trust them”A control with too few positive events cannot estimate a spillover coefficient, and it will fail quietly rather than loudly. The check is simple: what fraction of events are positive in the control’s own channel, and is that channel the brightest one in the tube?
quality <- CheckControlQuality(controls, match_table)quality[, c("stain", "positive_percent", "primary_is_brightest", "verdict")]"stain","positive_percent","primary_is_brightest","verdict""NKp30 eFluor450",3.52999579120037,TRUE,"weak""NKG2C PE",7.00934070126105,TRUE,"weak""CD57 pure+aIgM BV605",12.0607954310309,TRUE,"weak""NKG2A PE-Vio770",17.2310493389366,TRUE,"weak""Zombie auqa CD14 CD19 BV510",22.929233561213,TRUE,"ok"Four of the eleven controls on this panel are weak, the worst at 3.5 percent positive. Every one of them stains the correct channel brightest, so nothing is mislabelled. They simply do not have the events to pin down a coefficient, and a matrix computed from them carries that uncertainty into every sample it is applied to.
Compute the matrix
Section titled “Compute the matrix”With the controls matched and checked, the computation itself is the easy part.
computed <- ComputeSpilloverFromControls(controls, match_table)head(SummariseSpillover(computed, top = 5))"from","to","spill""v-LP505 525/50-E-A","v-450/50-F-A",0.961197336563175"yg-LP635 670/30-B-A","b-LP685 710/50-A-A",0.88389746991393"yg-LP600 610/20-C-A","yg-LP635 670/30-B-A",0.480200921226081"yg-582/15-D-A","yg-LP600 610/20-C-A",0.449756119746108"yg-LP600 610/20-C-A","b-LP685 710/50-A-A",0.449594703279591The largest coefficient is 0.96. A cell bright in the violet 525 detector registers almost as strongly in the violet 450 detector, which is what a pair of Brilliant Violet dyes does to each other. That is not a defect, it is the reason compensation exists.
Compare it with the stored matrix
Section titled “Compare it with the stored matrix”The interesting number is the difference between what you computed and what the instrument wrote into the file.
comparison <- CompareSpilloverMatrices(computed, stored, top = 5)comparison"from","to","computed","stored","difference""v-LP505 525/50-E-A","v-450/50-F-A",96.1197336563175,65.9999997659746,30.1197338903429"v-LP505 525/50-E-A","v-LP600 610/20-D-A",44.0502610010858,28.000000734159,16.0502602669268"yg-LP750 780/60-A-A","v-450/50-F-A",12.2551105515087,0,12.2551105515087"r-LP750 780/60-A-A","v-450/50-F-A",10.2429687067393,0,10.2429687067393"r-LP750 780/60-A-A","r-670/30-C-A",28.6520836778481,19.9999998806678,8.65208379718029The two disagree by 30 percentage points on the largest pair, 96.1 against 66.0. Two of the top five disagreements are cases where the stored matrix holds exactly zero and the controls say 12.3 and 10.2 percent, which is a spillover the instrument was never told about.
Notice the round numbers in the stored column: 66.0, 28.0, 20.0, and exact zeros. A matrix of values like that was typed by a person, not fitted from controls. That is the finding worth taking away from this page. The matrix in the file is a claim, and the controls sitting beside it in the same deposit are the way to check it.
When to do this
Section titled “When to do this”Recompute when the controls are in the deposit and the stored matrix looks hand entered, or when a population sits in a place the biology does not allow. Do not recompute from controls that fail the quality check, because a matrix fitted to 3.5 percent positive events is worse than a rounded one.
The compensation and panel design page covers the other half of this, which is choosing a panel where the largest coefficient is small enough that none of it matters much.