DillonHammill / CytoExploreR

Interactive Cytometry Data Analysis
61 stars 13 forks source link

Handling of transformed data with cyto_save() and cyto_load() #152

Open rwbaer opened 2 years ago

rwbaer commented 2 years ago

Describe the bug Transformation is apparently lost during save and load of gating set.

Expected behavior My expectation is that when I save a gatingSet with cyto_save() and the transform list has been specified by the 'trans = argument' that it will come back in as properly transformed data equivalent to what I created from raw .fcs files in CytoExplorer. The loaded data set should be usable in ggcyto().

Actual behavior Apparent bug. [1-works as expected] I load spill correct, and transform a gating set. I create some additional gates which I add to gating set and recompute(gsSamp), I then plot it in ggcyto() including the lines:

  ...
axis_x_inverse_trans() + 
axis_y_inverse_trans() + 
...

The plot axes are drawn, as expected, in original units.

[2- no longer works as expected] I then do a cyto_save(gsSamp, trans = TransList) followed by cyto_load(gsSamp) and plotting the very same plot no longer honors the axis_x_inverse_trans() and axis_x_inverse_trans() functions

Below are the graphs before and after the save/reload

Screenshots Expected graph (graph as drawn before cyto_save() and cyto_reload(): axisWorks

Graph after cyto_save() and cyto_load() axisDOESNOTWork

Additional context - more complete code for the repex

# ---
# cyto_save() and cyto_load  transform handling issue
# ---
eDate = "2022-05-27"
phagDur = "(16 h)"

# Create subdirectories for output in case they don't exist
dir.create(file.path(".", "Figures"), showWarnings = FALSE)
dir.create(file.path(".", "Reports"), showWarnings = FALSE)

# Load the packages we will need
library(readxl)
library(tidyverse)
library(dplyr)    # used for recode() to fix facet order
library(CytoExploreR)
library(openCyto)
library(flowWorkspace)
library(ggcyto)
library(patchwork)

# ========================================================================
#  SAMPLES-
#-------------------------------------------------------------------------
# The sample files for this example are in a subfolder named Samples
getwd()
gsSamp <- cyto_setup(path = "./Samples",
                     gatingTemplate = "Samples-Template.csv",
                     details = "Samples-Details.csv",
                     restrict = TRUE)

# Apply compensation to samples, (spill matrix from spill.r)
gsSamp <- cyto_compensate(gsSamp,
                          spillover = "Spillover-Matrix.csv")

# Translist creation
transList <- cyto_transformer_arcsinh(gsSamp)

# Apply our arcsinh transform for better viewing of our gatingSet
#   [Note: never do this line more than once; start over if in doubt.]
gsSamp = cyto_transform(gsSamp, trans = transList)

# Show us our channels and markers
cyto_channels(gsSamp)
cyto_markers(gsSamp)
cyto_fluor_channels(gsSamp)

# Gate (non-debris) Cells (Samples-Template.csv)
cyto_gate_draw(gsSamp,
               parent = "root",
               alias = "Non-Debris",
               channels = c("FSC-AREA", "SSC-AREA"))

# Gate Single Cells ((Samples-Template.csv))
cyto_gate_draw(gsSamp,
               parent = "Non-Debris",
               alias = "Single Cells",
               channels = c("FSC-AREA", "FSC-HEIGHT"))

# # Gate SytoxAAD on Unstained
# cyto_gate_draw(gsSamp,
#                parent = "Single Cells",
#                alias = c("Live", "Dead"),
#                channels = "FL4-AREA",
#                type = "interval",
#                negate = TRUE)

# This next line is necessary if you are re-using gates from before
cyto_gatingTemplate_apply(gsSamp, "Samples-Template.csv")

# --------------------- End normal gatingSet creation process ----------------------

# Create and apply some gates
# ===================================================================
# Pick out gate cut-off from pure macrophage and melanoma samples
# ===================================================================

# Samples to use as standards
cyto_details(gsSamp)  # figure out where mel and macro standards are (should be 17 & 18)
cyto_details(gsSamp)$name[17]
violetMelSamp = gsSamp[[17]] # Violet Melanoma cells
frBaseMel <- gh_pop_get_data(violetMelSamp, "Single Cells", 
                             returnType = "flowFrame")
cyto_details(gsSamp)$name[18]
orangeMacroSamp = gsSamp[[18]] # Orange macrophage cells
frBaseMacro <- gh_pop_get_data(orangeMacroSamp, "Single Cells", 
                               returnType = "flowFrame")

# quantileGate - Macrophage (more orange than 99% base melanoma)
orangeChan <- "FL3-AREA"
orangeCut <- openCyto:::.quantileGate(frBaseMel, 
                                      channels = orangeChan, 
                                      probs = .99)

# quantileGate - Melanoma (more violet than 99.5% base macrophage)
violetChan <- "FL1-AREA"
violetCut <- openCyto:::.quantileGate(frBaseMacro, 
                                      channels = violetChan, 
                                      probs = .99)

# Add a quad gate
# The quantile results (center point for simple quad gate)
orangeCut@min  # dangerous to directly poll slot
violetCut@min    # dangerous to directly poll slot

qg_man <- quadGate("FL3-AREA" = orangeCut@min, "FL1-AREA" = violetCut@min)
# Add gate defined from controls to all samples
gs_pop_add(gsSamp, qg_man, parent="Single Cells",
           names = c("Bait Cells", "Phagocytosis", "Macrophage", "Unstained"))
gs_pop_get_children(gsSamp, "Single Cells")
recompute(gsSamp, "Single Cells")

# ********** Axis inversion works ******************************
# Fix the plot order a little
ordBlocker = recode(cyto_details(gsSamp)$Blocker,
                    "Control" = "1 - Control",
                    "UCB35625" = "2 - UCB35625",
                    "Maraviroc" = "3 - Maraviroc",
                    "Both" = "4 - Both")

cyto_details(gsSamp)$ordBlocker = ordBlocker
eDate = "2022-05-27"
phagDur = "(16 h)"

# Plot some samples with inverse transform axis
# Plot this manual quad gating approach for Experimental Conditions
samples_p = ggcyto(gsSamp[1:16], aes(x = `FL3-AREA`, y = `FL1-AREA`),
                   limit = "instrument") +
  axis_x_inverse_trans() + 
  axis_y_inverse_trans() + 
  ggcyto_par_set(limits = "instrument") +
  geom_hex(bins = 64) +
  geom_gate("Bait Cells") +
  geom_stats(gate = "Bait Cells", size = 3, alpha = 0.1) +
  geom_gate("Phagocytosis") +
  geom_stats(gate = "Phagocytosis", size = 3, alpha = 0.1) +
  geom_gate("Macrophage") +
  geom_stats(gate = "Macrophage", size = 3, alpha = 0.1) +
  labs(title = paste("Effrocytosis Samples ", phagDur, eDate)) +
  facet_grid(ordBlocker ~ Adhesion + RANTES)
samples_p
ggsave(filename = "axisWorks.png", width = 7, height = 7, dpi = 300, units = 'in' )
# Save and reload breaks axis inversion
cyto_save(gsSamp, save_as = "gsSamp", trans = transList)
gsSamp = cyto_load(path = "./gsSamp")

# Redraw same graph after save and reload
# Plot this manual quad gating approach for Experimental Conditions
samples_p2 = ggcyto(gsSamp[1:16], aes(x = `FL3-AREA`, y = `FL1-AREA`),
                   limit = "instrument") +
  axis_x_inverse_trans() + 
  axis_y_inverse_trans() + 
  ggcyto_par_set(limits = "instrument") +
  geom_hex(bins = 64) +
  geom_gate("Bait Cells") +
  geom_stats(gate = "Bait Cells", size = 3, alpha = 0.1) +
  geom_gate("Phagocytosis") +
  geom_stats(gate = "Phagocytosis", size = 3, alpha = 0.1) +
  geom_gate("Macrophage") +
  geom_stats(gate = "Macrophage", size = 3, alpha = 0.1) +
  labs(title = paste("Effrocytosis Samples ", phagDur, eDate)) +
  facet_grid(ordBlocker ~ Adhesion + RANTES)
samples_p2
ggsave(filename = "axisDOESNOTWork.png", width = 7, height = 7, dpi = 300, units = 'in' )
DillonHammill commented 2 years ago

This is a known issue that I have already flagged with the cytoverse team. Hopefully support will be added for this soon.

rwbaer commented 2 years ago

Thanks @DillonHammill. I have been trying to keep my workflow gatingSet and CytoExploreR-centric, but understanding all the things you take care of for us behind the scenes (and when I might be abusing your assumptions) can get intimidating. Your feedback is very helpful.