Compare Mean Fluorescence Intensity of Gated Events

[1]:
import os
import numpy as np
import pandas as pd

import flowkit as fk

%matplotlib inline

Load FlowJo Workspace

[1]:
base_dir = "../../../data/8_color_data_set"

sample_path = os.path.join(base_dir, "fcs_files")
wsp_path = os.path.join(base_dir, "8_color_ICS.wsp")
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[1], line 3
      1 base_dir = "../../../data/8_color_data_set"
----> 3 sample_path = os.path.join(base_dir, "fcs_files")
      4 wsp_path = os.path.join(base_dir, "8_color_ICS.wsp")

NameError: name 'os' is not defined
[ ]:
workspace = fk.Workspace(wsp_path, fcs_samples=sample_path)
[4]:
workspace.summary()
[4]:
samples loaded_samples gates max_gate_depth
group_name
All Samples 3 3 14 6
DEN 3 3 14 6
GEN 0 0 0 0
G69 0 0 0 0
Lyo Cells 0 0 0 0
[5]:
sample_ids = workspace.get_sample_ids('DEN')
[6]:
sample_ids
[6]:
['101_DEN084Y5_15_E01_008_clean.fcs',
 '101_DEN084Y5_15_E03_009_clean.fcs',
 '101_DEN084Y5_15_E05_010_clean.fcs']
[7]:
# choose a sample ID
sample_id = '101_DEN084Y5_15_E03_009_clean.fcs'
sample = workspace.get_sample(sample_id)

Review the gate hierachy

[8]:
print(workspace.get_gate_hierarchy(sample_id, 'ascii'))
root
╰── Time
    ╰── Singlets
        ╰── aAmine-
            ╰── CD3+
                ├── CD4+
                │   ├── CD107a+
                │   ├── IFNg+
                │   ├── IL2+
                │   ╰── TNFa+
                ╰── CD8+
                    ├── CD107a+
                    ├── IFNg+
                    ├── IL2+
                    ╰── TNFa+

Process gates for the sample group

[9]:
workspace.analyze_samples(sample_id=sample_id)

Get gated events for a sample

[10]:
cd4_gate_name = 'CD4+'
cd4_events = workspace.get_gate_events(sample_id, cd4_gate_name)
[11]:
sample.event_count, cd4_events.shape[0]
[11]:
(283969, 81855)
[12]:
cd4_events.head(5)
[12]:
sample_id FSC-A FSC-H FSC-W SSC-A SSC-H SSC-W TNFa FITC FLR-A CD8 PerCP-Cy55 FLR-A IL2 BV421 FLR-A Aqua Amine FLR-A IFNg APC FLR-A CD3 APC-H7 FLR-A CD107a PE FLR-A CD4 PE-Cy7 FLR-A Time
1 101_DEN084Y5_15_E03_009_clean.fcs 0.484332 0.397831 0.304358 0.178974 0.165913 0.269681 0.242188 0.230791 0.282167 0.254901 0.244743 0.454572 0.276832 0.610560 0.029446
6 101_DEN084Y5_15_E03_009_clean.fcs 0.858325 0.713455 0.300763 0.160702 0.149963 0.267902 0.255704 0.297297 0.270034 0.259815 0.232809 0.383849 0.272156 0.476992 0.029638
7 101_DEN084Y5_15_E03_009_clean.fcs 0.499515 0.389256 0.320814 0.160819 0.148792 0.270207 0.542186 0.298411 0.430466 0.270496 0.720327 0.324062 0.353662 0.604535 0.029652
9 101_DEN084Y5_15_E03_009_clean.fcs 0.427556 0.346325 0.308638 0.181880 0.169140 0.268832 0.256053 0.305138 0.332638 0.251144 0.226288 0.607199 0.369072 0.423495 0.029680
16 101_DEN084Y5_15_E03_009_clean.fcs 0.527936 0.415756 0.317456 0.103247 0.096481 0.267532 0.241784 0.235227 0.283300 0.246906 0.251326 0.454224 0.306893 0.487684 0.029858

Calculate means for channels of interest

[13]:
fluoro_labels = []
for chan_idx in sample.fluoro_indices:
    fluoro_labels.append(sample.pnn_labels[chan_idx])
[14]:
cd4_events = cd4_events[fluoro_labels]
[15]:
cd4_mfi = cd4_events.mean()
[16]:
cd4_mfi
[16]:
TNFa FITC FLR-A         0.248338
CD8 PerCP-Cy55 FLR-A    0.274010
IL2 BV421 FLR-A         0.288348
Aqua Amine FLR-A        0.250962
IFNg APC FLR-A          0.246429
CD3 APC-H7 FLR-A        0.488177
CD107a PE FLR-A         0.279935
CD4 PE-Cy7 FLR-A        0.588879
dtype: float64

Let’s compare MFI between CD4+ & CD8+

[17]:
cd8_gate_name = 'CD8+'
cd8_events = workspace.get_gate_events(sample_id, cd8_gate_name)

cd8_mfi = cd8_events[fluoro_labels].mean()
[18]:
mfi_comparison = pd.DataFrame([cd4_mfi, cd8_mfi], index=[cd4_gate_name, cd8_gate_name])
[19]:
mfi_comparison
[19]:
TNFa FITC FLR-A CD8 PerCP-Cy55 FLR-A IL2 BV421 FLR-A Aqua Amine FLR-A IFNg APC FLR-A CD3 APC-H7 FLR-A CD107a PE FLR-A CD4 PE-Cy7 FLR-A
CD4+ 0.248338 0.274010 0.288348 0.250962 0.246429 0.488177 0.279935 0.588879
CD8+ 0.250975 0.584248 0.283504 0.251208 0.252061 0.459689 0.292997 0.308986
[20]:
mfi_comparison = mfi_comparison.transpose()
[21]:
mfi_comparison
[21]:
CD4+ CD8+
TNFa FITC FLR-A 0.248338 0.250975
CD8 PerCP-Cy55 FLR-A 0.274010 0.584248
IL2 BV421 FLR-A 0.288348 0.283504
Aqua Amine FLR-A 0.250962 0.251208
IFNg APC FLR-A 0.246429 0.252061
CD3 APC-H7 FLR-A 0.488177 0.459689
CD107a PE FLR-A 0.279935 0.292997
CD4 PE-Cy7 FLR-A 0.588879 0.308986
[22]:
ax = mfi_comparison.plot.bar(figsize=(12, 6))
ax.set_title("Mean Fluorescence Intensity - CD4+ vs CD8+", color='black', fontsize=18)
ax.legend(bbox_to_anchor=(1.0, 1.0), fontsize=12)
ax.set_xticklabels(ax.get_xticklabels(), rotation=45)
ax.plot()
[22]:
[]
../../_images/notebooks_advanced_compare-mfi-of-gated-events_28_1.png

Matching FlowJo’s MFI

If you run the above code on your data and compare to the MFI reported in FlowJo, you may be asking: Why do my numbers not match FlowJo?

FlowJo users may have various ways they calculate statistcs. Often you’ll find they use the median for MFI, which is arguably a better statistic to use. You may also find that they calculate that median on the compensated but not transformed data. To reiterate what the FlowJo documentation says, it is always best to explicitly define what you mean by “MFI” when reporting it as a statistic.

Here we’ll replicate the above analysis, but use the median on the compensated, untransformed event data.

[23]:
# Use the 'source' kwarg to specify just the compensated events
cd4_events_comp = workspace.get_gate_events(sample_id, cd4_gate_name, source='comp')
cd8_events_comp = workspace.get_gate_events(sample_id, cd8_gate_name, source='comp')
[24]:
# Calculate the median on those extracted gated populations
cd4_fj_mfi = cd4_events_comp[fluoro_labels].median()
cd8_fj_mfi = cd8_events_comp[fluoro_labels].median()
[25]:
fj_mfi_comparison = pd.DataFrame([cd4_fj_mfi, cd8_fj_mfi], index=[cd4_gate_name, cd8_gate_name])
[26]:
fj_mfi_comparison
[26]:
TNFa FITC FLR-A CD8 PerCP-Cy55 FLR-A IL2 BV421 FLR-A Aqua Amine FLR-A IFNg APC FLR-A CD3 APC-H7 FLR-A CD107a PE FLR-A CD4 PE-Cy7 FLR-A
CD4+ 111.288873 245.009334 328.600006 127.720001 95.450704 2259.295656 258.310234 5272.984266
CD8+ 111.935477 5287.526461 300.080017 128.960007 94.533422 1799.548660 286.533448 403.920880
[27]:
fj_mfi_comparison = fj_mfi_comparison.transpose()
[28]:
fj_mfi_comparison
[28]:
CD4+ CD8+
TNFa FITC FLR-A 111.288873 111.935477
CD8 PerCP-Cy55 FLR-A 245.009334 5287.526461
IL2 BV421 FLR-A 328.600006 300.080017
Aqua Amine FLR-A 127.720001 128.960007
IFNg APC FLR-A 95.450704 94.533422
CD3 APC-H7 FLR-A 2259.295656 1799.548660
CD107a PE FLR-A 258.310234 286.533448
CD4 PE-Cy7 FLR-A 5272.984266 403.920880
[29]:
ax = fj_mfi_comparison.plot.bar(figsize=(12, 6))
ax.set_title("FlowJo MFI - CD4+ vs CD8+", color='black', fontsize=18)
ax.legend(bbox_to_anchor=(1.0, 1.0), fontsize=12)
ax.set_xticklabels(ax.get_xticklabels(), rotation=45)
ax.plot()
[29]:
[]
../../_images/notebooks_advanced_compare-mfi-of-gated-events_36_1.png
[ ]: