Last updated: 2025-03-24

Checks: 6 1

Knit directory: McConville_Lab_Microbiome_PMC255A/

This reproducible R Markdown analysis was created with workflowr (version 1.7.1). The Checks tab describes the reproducibility checks that were applied when the results were created. The Past versions tab lists the development history.


The R Markdown file has unstaged changes. To know which version of the R Markdown file created these results, you’ll want to first commit it to the Git repo. If you’re still working on the analysis, you can ignore this warning. When you’re finished, you can run wflow_publish to commit the R Markdown file and build the HTML.

Great job! The global environment was empty. Objects defined in the global environment can affect the analysis in your R Markdown file in unknown ways. For reproduciblity it’s best to always run the code in an empty environment.

The command set.seed(20250117) was run prior to running the code in the R Markdown file. Setting a seed ensures that any results that rely on randomness, e.g. subsampling or permutations, are reproducible.

Great job! Recording the operating system, R version, and package versions is critical for reproducibility.

Nice! There were no cached chunks for this analysis, so you can be confident that you successfully produced the results during this run.

Great job! Using relative paths to the files within your workflowr project makes it easier to run your code on other machines.

Great! You are using Git for version control. Tracking code development and connecting the code version to the results is critical for reproducibility.

The results in this page were generated with repository version 3a65c0b. See the Past versions tab to see a history of the changes made to the R Markdown and HTML files.

Note that you need to be careful to ensure that all relevant files for the analysis have been committed to Git prior to generating the results (you can use wflow_publish or wflow_git_commit). workflowr only checks the R Markdown file, but you know if there are other scripts or data files that it depends on. Below is the status of the Git repository when the results were generated:


Ignored files:
    Ignored:    .Rhistory
    Ignored:    .Rproj.user/
    Ignored:    data/raw_data/

Untracked files:
    Untracked:  analysis/PMC255A_ScreenQC_v4.Rmd
    Untracked:  analysis/prelim/

Unstaged changes:
    Modified:   analysis/PMC255A_ScreenComparison.Rmd
    Deleted:    analysis/PMC255A_ScreenQC_v3.Rmd
    Modified:   analysis/_site.yml

Note that any generated files, e.g. HTML, png, CSS, etc., are not included in this status report because it is ok for generated content to have uncommitted changes.


These are the previous versions of the repository in which changes were made to the R Markdown (analysis/PMC255A_ScreenComparison.Rmd) and HTML (docs/PMC255A_ScreenComparison.html) files. If you’ve configured a remote Git repository (see ?wflow_git_remote), click on the hyperlinks in the table below to view the files as they were in that past version.

File Version Author Date Message
Rmd 3a65c0b annrann.wong 2025-03-14 Update Integration Page
html 3a65c0b annrann.wong 2025-03-14 Update Integration Page
Rmd 4e5953a annrann.wong 2025-03-14 Update Integration Page
html 4e5953a annrann.wong 2025-03-14 Update Integration Page
Rmd afd7a2a annrann.wong 2025-03-14 Update integration data
html afd7a2a annrann.wong 2025-03-14 Update integration data
Rmd 584065d annrann.wong 2025-03-12 Update integration data
html 584065d annrann.wong 2025-03-12 Update integration data
Rmd 8603594 annrann.wong 2025-03-12 Update integration data
html 8603594 annrann.wong 2025-03-12 Update integration data
Rmd 5a8e2f9 annrann.wong 2025-03-11 Added integration
html 5a8e2f9 annrann.wong 2025-03-11 Added integration

Aim

To examine the polarisation potential of BDBM cells from M0 to M1 or M2 in the presence of compounds and different concentrations of polarisation activator.

Researcher

Kayla Wilson, Ada Koo, McConville Lab, Bedoui Lab

Readout

Image analysis of BMDM cells stained with DAPI, ARG, INOS, and BODIPY (CX7 Pro LZR, 9 fields @ 20x) and image analysis of OT-I cells expressing GFP and OT-II cells expressing tdTomato (Cy5 and Cy10, 1 field @ 4x).

Image analysis software

CellProfiler 4.2.8

Required R packages

data.table, DT, reshape2, tidyverse, viridis, patchwork

# load the required packages
library(data.table)
library(readxl)
library(DT)
library(reshape2)
library(viridis)
library(tidyverse)
library(flexdashboard)
library(plotly)
library(crosstalk)
library(htmltools)
library(summarywidget)


# fix ggplotly axis labels
layout_ggplotly <- function(gg, x = -0.02, y = -0.08){
  # The 1 and 2 goes into the list that contains the options for the x and y axis labels respectively
  gg[['x']][['layout']][['annotations']][[1]][['y']] <- x
  gg[['x']][['layout']][['annotations']][[2]][['x']] <- y
  gg
}

# set the file prefix
prefix <- "KW_PMC255A_PMC255B"

# turn off scientific notation
options(scipen=999)

Screen details

Screen dates (yyyy-mm-dd)

2024-12-04

Cell lines

BMDM M0
DC/T OT1
DC/T OT2

Screen outline

Library: Custom MCE library
Library plates: 4 (only plates 3 and 4 used in this analysis)
Cell plates per library plate: 24

Controls

Per well data is normalised to Stimulated DMSO on a per plate basis.



Import data

The raw and normalised (fold change to DMSO) data for all wells across screen runs are read into a dataframe.

# read in data from three comparisons
norm_255a <- read_csv("output/KW_PMC255A_data_norm_melt.csv", show_col_types = FALSE) %>% 
  filter(grepl("Unstim_M0", Barcode) & grepl("3|4", VCFG_Plate_ID))
norm_255b_ot1 <- read_csv("output/KW_PMC255B_data_norm_melt_OT1.csv", show_col_types = FALSE) %>% 
  filter(grepl("3|4", VCFG_Plate_ID))
norm_255b_ot2 <- read_csv("output/KW_PMC255B_data_norm_melt_OT2.csv", show_col_types = FALSE) %>% 
  filter(grepl("3|4", VCFG_Plate_ID))

Data processing

Data filtering

Non-samples are filtered out, leaving compound only wells and related data.

# filter out compounds and data
norm_255b_ot1_filt <- norm_255b_ot1 %>% 
  filter(Description == "Sample" & grepl("A", Plate)) %>%  #OT1 plateA (low OVA conc)
  filter(grepl("MeanArea", Variable) & Hours == 72) %>% 
  select("VCFG_Plate_ID", "Barcode", "QCL_Sample_Number", "WellID", "Compound_Name", "Concentration", "Units", Raw, Norm = "NormToT0_NormToDMSO+OVA") %>% 
  mutate(Feature = "MeanAreaOT1")

norm_255b_ot2_filt <- norm_255b_ot2 %>% 
  filter(Description == "Sample" & grepl("A", Plate)) %>%  #OT2 plateA (low OVA conc)
  filter(grepl("MeanArea", Variable) & Hours == 96) %>% 
  select("VCFG_Plate_ID", "Barcode", "WellID", "Compound_Name", "Concentration", "Units", Raw, Norm = "NormToT0_NormToDMSO+OVA") %>% 
  mutate(Feature = "MeanAreaOT2")

# pivot data into wide form
norm_255a_filt <- norm_255a %>% 
  filter(Description == "Sample") %>% 
  select("Feature",  "VCFG_Plate_ID", "Barcode", "QCL_Sample_Number", "WellID", "Compound_Name", "Concentration", "Units", "Raw", Norm = "NormToStimDMSO") #%>% 
  # pivot_wider(names_from = Feature, values_from = c(Raw, Norm), names_glue = "{.value}_{Feature}")

Viability binning

Compounds were sorted into the following viability bins based on the normalised Total DAPI counts:

  • High viability (> 1.15)
  • Normal viability (0.8 <= 1.15)
  • Moderate viability (0.5 <= 0.8)
  • Low viability (0.3 <= 0.5)
  • Very Low viability (<= 0.3)
# calculate viability bins
viability_bin <- norm_255a %>% 
  filter(Description == "Sample") %>% 
  group_by(VCFG_Plate_ID, WellID) %>% 
  reframe(Viability_Bin = 
         case_when(grepl("DAPI", Feature) & NormToStimDMSO > 1.15 ~ "High",
                   grepl("DAPI", Feature) & NormToStimDMSO > 0.8 & NormToStimDMSO <= 1.15 ~ "Normal",
                   grepl("DAPI", Feature) & NormToStimDMSO > 0.5 & NormToStimDMSO <= 0.8 ~ "Moderate",
                   grepl("DAPI", Feature) & NormToStimDMSO > 0.3 & NormToStimDMSO <= 0.5 ~ "Low",
                   grepl("DAPI", Feature) & NormToStimDMSO <= 0.3 ~ "VeryLow")) %>% 
  filter(!is.na(Viability_Bin))

Aggregation

Data from three readouts (Mean Area OT1, OT2, Total DAPI count) are aggregated.

# combine datasets from all data norm of PMC255 
norm_comb <- bind_rows(norm_255b_ot1_filt, norm_255b_ot2_filt, norm_255a_filt) %>% 
  select(-QCL_Sample_Number) %>% 
  # fix QCL sample number
  left_join(., norm_255a_filt %>% 
  select(WellID, VCFG_Plate_ID, QCL_Sample_Number) %>% distinct(), by = c("WellID", "VCFG_Plate_ID"))

Robust Z-Scoring

The normalised values have been Robust Z-Scored to the median of all of the test compounds in order to assess the relative strength of each compound. This method can be used to select the strongest and most robust hits from a large dataset. Z-Scores of <= -2 or >= 2 are considered to be significant, but this value must be viewed in combination with the fold change. See the Robust Z-Scoring section of the Methods page for a more information.


Formula:
Robust Z-Score = (sample value - median of all sample values) / median absolute deviation of all sample values

zscored_comb <- norm_comb %>% 
  left_join(., viability_bin, by = c("VCFG_Plate_ID", "WellID")) %>% 
  group_by(Feature) %>% 
  mutate(Robust_ZScore = (Norm - median(Norm, na.rm = TRUE))/mad(Norm, na.rm = TRUE)) %>%
  mutate_if(is.numeric, round, 2)

Waterfall plot

Robust Z-Scores for all of the test compounds are displayed below. The plots is interactive. Hover over the points to view the compound name and other information. The number of hits (unique compound IDs) and corresponding fold change to DMSO (FC) at Z-Score cut-offs of 2, 3 and 4 are printed below the plots.

# prepare the data
feat_list <- unique(zscored_comb$Feature)[1:2] 

data_zscored_plot <- zscored_comb %>%
  filter(Feature == feat_list) %>% 
  unite(Conc, c(Concentration, Units), sep = "") %>%
  unite(Treatment, c(Compound_Name, Conc), remove = FALSE) %>%
  mutate(Status = ifelse(Robust_ZScore >= 2, "Significant Increase",
                  ifelse(Robust_ZScore <= -2, "Significant Decrease", "No Change")))

# set order of status for plotting
stat_order <- c("Significant Increase", "Significant Decrease", "No Change")
data_zscored_plot$Status <- factor(data_zscored_plot$Status, levels = stat_order)

# reorder
data_zscored_plot_filt <- data_zscored_plot %>%
  group_by(Feature) %>% 
  arrange(Robust_ZScore) %>%
  mutate(Index = 1:n())

# print a plot and dataframe for each feature
  for (feature in feat_list) {
  
  # create the feature heading
  cat("\n")
  cat("\n## ", feature, "\n")
  cat("\n")

# create dot plot
snake_plot <- data_zscored_plot_filt %>% 
  filter(Feature %in% c(feature)) %>% 
  ggplot(., aes(x = Index, 
                y = Robust_ZScore,
                label = Compound_Name)) +
  geom_point(aes(colour = Status,
                 text = paste('Compound_Name:', Compound_Name,
                              '<br>Raw:', Raw,
                              '<br>Norm:', Norm,
                              '<br>Robust Z-Score:', Robust_ZScore,
                              '<br>Barcode:', Barcode)), 
                 alpha = 0.8, size = 1.6) +
  geom_hline(aes(yintercept = -2), linetype = "dotted", size = 0.3) +
  geom_hline(aes(yintercept = 2), linetype = "dotted", size = 0.3) +
  labs(x = "Compound",
       y = "Z-Score To Sample") +
  #scale_y_continuous(breaks = seq(-6, 6, by = 1), limits=c(-6,6)) +
  theme_bw() +
  theme(#axis.title.y = element_text(margin = margin(t = 0, r = 30, b = 0, l = 30)), 
        axis.title.y = element_text(margin = margin(t = 0, r = 10, b = 0, l = 10)),
        axis.title.x = element_text(size = 1, colour = "white"),
        axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        axis.text.y = element_text(colour = "black"),
        plot.title = element_text(margin = margin(t = 10, r = 0, b = 5, l = 0), size = 14), 
        plot.margin = margin(t = 10, r = 30, b = 5, l = 30),
        legend.title = element_blank()) +
  scale_colour_manual(values = c("#bc3754", "#2a788e", "grey61"), drop = FALSE, name = "")

print(htmltools::tagList(plotly::ggplotly(snake_plot, tooltip = "text")))

cat("\n")
cat("\n")

data_zscored_filt <- zscored_comb %>%
  filter(Feature == feature)

hit_count_summary_2 <- data_zscored_filt %>%
  mutate(ZScore_Bin = ifelse(Robust_ZScore >= 2, ">= 2",
                      ifelse(Robust_ZScore <= -2, "<= -2",
                      "NonSig"))) %>% 
  filter(!ZScore_Bin == "NonSig") %>%
  group_by(ZScore_Bin) %>% 
  summarise(Equivalent_FC_max = max(Norm),
            Equivalent_FC_min = min(Norm),
            Hit_Count = n_distinct(QCL_Sample_Number))

hit_count_summary_3 <- data_zscored_filt %>%
  mutate(ZScore_Bin = ifelse(Robust_ZScore >= 3, ">= 3",
                      ifelse(Robust_ZScore <= -3, "<= -3",
                      "NonSig"))) %>% 
  filter(!ZScore_Bin == "NonSig") %>%
  group_by(ZScore_Bin) %>% 
  summarise(Equivalent_FC_max = max(Norm),
            Equivalent_FC_min = min(Norm),
            Hit_Count = n_distinct(QCL_Sample_Number))

hit_count_summary_4 <- data_zscored_filt %>%
  mutate(ZScore_Bin = ifelse(Robust_ZScore >= 4, ">= 4",
                      ifelse(Robust_ZScore <= -4, "<= -4",
                      "NonSig"))) %>% 
  filter(!ZScore_Bin == "NonSig") %>%
  group_by(ZScore_Bin) %>% 
  summarise(Equivalent_FC_max = max(Norm),
            Equivalent_FC_min = min(Norm),
            Hit_Count = n_distinct(QCL_Sample_Number))

hit_count_summary <- rbind(hit_count_summary_2, hit_count_summary_3, hit_count_summary_4) %>% 
  pivot_longer(c(Equivalent_FC_max, Equivalent_FC_min), 
               names_to = "Direction", values_to = "Equivalent_FC") %>% 
  filter(c(grepl('<', ZScore_Bin) & Direction == "Equivalent_FC_max") |
         c(grepl('>', ZScore_Bin) & Direction == "Equivalent_FC_min")) %>% 
  mutate(ZScore_Bin = factor(ZScore_Bin, levels = c(">= 2", ">= 3", ">= 4", "<= -2", "<= -3", "<= -4"))) %>%
  complete(ZScore_Bin, fill = list(Hit_Count = 0)) %>% 
  select(ZScore_Bin, Equivalent_FC, Hit_Count) %>%
  arrange(ZScore_Bin) %>% 
  mutate_if(is.numeric, round, 2) 
  
# display the table
print(htmltools::tagList(datatable(hit_count_summary, extensions = c('Scroller', "FixedColumns"), rownames = FALSE, 
  options = list(
  dom = 'rt',
  columnDefs = list(list(className = 'dt-center',targets="_all")),
          pageLength = -1,
  searching = FALSE,
  scrollX = TRUE))))

  cat("\n")
  } # end the loop

MeanAreaOT1

MeanAreaOT2

# }

Summary plots

Scatter plots with dots coloured based on viability bins (see cut-off in ‘Viability Binning’ section above) and a matching datatable with dynamic filters. Interactive check boxes and sliders allow for user control. Viability bins can be selected from a list of checkboxes and Robust Z-Score values can be filtered using a slider widget.

OT1 (MeanArea vs DAPI)

Mean Area of OT1 (y-axis) plotted against total DAPI count (x-axis).
Comments: A trend showing a positive correlation between total DAPI count and mean area for the majority of hits. Few outliers (e.g. SN02722430 Clobetasol propionate, SN02722427 Methylprednisolone succinate (sodium)) show a normal-high DAPI count with low Mean Area across all three concentrations.

#set colour palette
colour_palette <- c("High"="#fde725", "Normal"="#35b779", "Moderate"="#31688e", "Low"="#440154", "VeryLow" = "#2E013E")

# set plot order
zscored_comb$Viability_Bin <- factor(zscored_comb$Viability_Bin, levels = c("High", "Normal", "Moderate", "Low", "VeryLow"))

# add an id to not rely only on rownumbers to per well data
zscored_comb_ot1 <- zscored_comb %>% 
                   filter(grepl("MeanAreaOT1|DAPI", Feature)) %>% 
                   pivot_wider(id_cols = c(VCFG_Plate_ID, WellID, QCL_Sample_Number, Compound_Name, Concentration, Units, Viability_Bin), names_from = Feature, values_from = c(Raw, Norm, Robust_ZScore)) %>% 
  mutate(id = paste("id", 1:n()), 
       Compound_Name_Unique = if_else(duplicated(Compound_Name), "B", "A")) %>%  
  select(-"Robust_ZScore_Total_DAPI_Count")

# pivot to form per comp data
zscored_comb_ot1_percomp <- zscored_comb_ot1 %>% 
  pivot_wider(id_cols = c("Compound_Name", "QCL_Sample_Number"), names_from = Concentration, names_prefix = "Conc", values_from = c(8:12)) %>% 
  mutate(id = paste("id", 1:n()))

# combine df into a key
shared_zscored_comb_ot1 <- SharedData$new(zscored_comb_ot1, key = ~ id, group = "id")
shared_zscored_comb_ot1_percomp <- SharedData$new(zscored_comb_ot1_percomp, key = ~ id, group = "id")
  
# create filter check box & slider
filters <- bscols(
  list(
    filter_checkbox("Viability_Bin", "Viability Bin", shared_zscored_comb_ot1, ~Viability_Bin),
    filter_slider("Robust_ZScore_MeanAreaOT1", "OT1 Robust ZScore", shared_zscored_comb_ot1, ~Robust_ZScore_MeanAreaOT1, step = 0.1)))

# create ggplot
p <- ggplot(shared_zscored_comb_ot1, aes(x = Norm_Total_DAPI_Count, y = Norm_MeanAreaOT1, 
                             color = Viability_Bin)) +
  geom_point(aes(text = paste0('Compound: ', Compound_Name,'<br>QCL Sample Number: ', QCL_Sample_Number,'<br>WellID:', WellID))) +  scale_colour_manual(values = colour_palette, name = "Concentration (uM)", guide = "none") +
  xlim(0, 2) +
  facet_wrap(~Concentration, labeller = labeller(Concentration = ~ paste0(.x, "uM")))+
  theme_bw()

# convert to Plotly
plotly_plot <- ggplotly(p) %>%
  highlight("plotly_selected") %>% 
  layout(showlegend = F)

# combine filters and plot
bscols(widths = c(2, 10), filters, plotly_plot)

Total Dose Count:
Total Unique Compounds:

Per Well

datatable(shared_zscored_comb_ot1, extensions = c('Buttons', 'Scroller'), rownames = FALSE,
  options = list(
  dom = 'Blfrtip',
  columnDefs = list(list(className = 'dt-center',targets="_all")),
  lengthMenu = list(c(5, -1), c('5', 'All')),
          pageLength = 5,
  buttons = list(list(extend = 'csv',
                      filename = paste0(prefix, "_OT1vsDAPI_viability")),
                 list(extend = 'excel',
                      filename = paste0(prefix, "_OT1vsDAPI_viability"), title = NULL)),
  searching = TRUE,
  scrollX = TRUE))

Per Compound

datatable(shared_zscored_comb_ot1_percomp, extensions = c('Buttons', 'Scroller'), rownames = FALSE,
  options = list(
  dom = 'Blfrtip',
  columnDefs = list(list(className = 'dt-center',targets="_all")),
  lengthMenu = list(c(5, -1), c('5', 'All')),
          pageLength = 5,
  buttons = list(list(extend = 'csv',
                      filename = paste0(prefix, "_OT1vsDAPI_viability")),
                 list(extend = 'excel',
                      filename = paste0(prefix, "_OT1vsDAPI_viability"), title = NULL)),
  searching = TRUE,
  scrollX = TRUE))

OT2 (MeanArea vs DAPI)

Mean Area of OT2 (y-axis) plotted against total DAPI count (x-axis).
Comments: A trend showing a positive correlation between total DAPI count and mean area for the majority of hits. Few outliers (e.g. SN02722430 Clobetasol propionate) show a normal-high DAPI count with low Mean Area across all three concentrations.

#set colour palette
colour_palette <- c("High"="#fde725", "Normal"="#35b779", "Moderate"="#31688e", "Low"="#440154", "VeryLow" = "#2E013E")

# set plot order
zscored_comb$Viability_Bin <- factor(zscored_comb$Viability_Bin, levels = c("High", "Normal", "Moderate", "Low", "VeryLow"))

# add an id to not rely only on rownumbers to per well data
zscored_comb_ot2 <- zscored_comb %>% 
                   filter(grepl("MeanAreaOT2|DAPI", Feature)) %>% 
                   pivot_wider(id_cols = c(VCFG_Plate_ID, WellID, QCL_Sample_Number, Compound_Name, Concentration, Units, Viability_Bin), names_from = Feature, values_from = c(Raw, Norm, Robust_ZScore)) %>% 
  mutate(id = paste("id", 1:n()), 
       Compound_Name_Unique = if_else(duplicated(Compound_Name), "B", "A")) %>%  
  select(-"Robust_ZScore_Total_DAPI_Count")

# pivot to form per comp data
zscored_comb_ot2_percomp <- zscored_comb_ot2 %>% 
  pivot_wider(id_cols = c("Compound_Name", "QCL_Sample_Number"), names_from = Concentration, names_prefix = "Conc", values_from = c(8:12)) %>% 
  mutate(id = paste("id", 1:n()))

# combine df into a key
shared_zscored_comb_ot2 <- SharedData$new(zscored_comb_ot2, key = ~ id, group = "id")
shared_zscored_comb_ot2_percomp <- SharedData$new(zscored_comb_ot2_percomp, key = ~ id, group = "id")
  
# create filter check box & slider
filters <- bscols(
  list(
    filter_checkbox("Viability_Bin", "Viability Bin", shared_zscored_comb_ot2, ~Viability_Bin),
    filter_slider("Robust_ZScore_MeanAreaOT2", "OT2 Robust ZScore", shared_zscored_comb_ot2, ~Robust_ZScore_MeanAreaOT2, step = 0.1)))

# create ggplot
p <- ggplot(shared_zscored_comb_ot2, aes(x = Norm_Total_DAPI_Count, y = Norm_MeanAreaOT2, 
                             color = Viability_Bin)) +
  geom_point(aes(text = paste0('Compound: ', Compound_Name,'<br>QCL Sample Number: ', QCL_Sample_Number,'<br>WellID:', WellID))) +  scale_colour_manual(values = colour_palette, name = "Concentration (uM)", guide = "none") +
  xlim(0, 2) +
  facet_wrap(~Concentration, labeller = labeller(Concentration = ~ paste0(.x, "uM")))+
  theme_bw()

# convert to Plotly
plotly_plot <- ggplotly(p) %>%
  highlight("plotly_selected") %>% 
  layout(showlegend = F)

# combine filters and plot
bscols(widths = c(2, 10), filters, plotly_plot)

Total Dose Count:
Total Unique Compounds:

Per Well

datatable(shared_zscored_comb_ot2, extensions = c('Buttons', 'Scroller'), rownames = FALSE,
  options = list(
  dom = 'Blfrtip',
  columnDefs = list(list(className = 'dt-center',targets="_all")),
  lengthMenu = list(c(5, -1), c('5', 'All')),
          pageLength = 5,
  buttons = list(list(extend = 'csv',
                      filename = paste0(prefix, "_OT2vsDAPI_viability")),
                 list(extend = 'excel',
                      filename = paste0(prefix, "_OT2vsDAPI_viability"), title = NULL)),
  searching = TRUE,
  scrollX = TRUE))

Per Compound

datatable(shared_zscored_comb_ot2_percomp, extensions = c('Buttons', 'Scroller'), rownames = FALSE,
  options = list(
  dom = 'Blfrtip',
  columnDefs = list(list(className = 'dt-center',targets="_all")),
  lengthMenu = list(c(5, -1), c('5', 'All')),
          pageLength = 5,
  buttons = list(list(extend = 'csv',
                      filename = paste0(prefix, "_OT2vsDAPI_viability")),
                 list(extend = 'excel',
                      filename = paste0(prefix, "_OT2vsDAPI_viability"), title = NULL)),
  searching = TRUE,
  scrollX = TRUE))

OT1 vs OT2 (MeanArea)

Mean Area of OT2 (y-axis) plotted against Mean Area of OT1 (x-axis).
Comments: Overall decrease in mean area of OT1 is reflected in a decrease in mean area of OT2.

#set colour palette
colour_palette <- c("High"="#fde725", "Normal"="#35b779", "Moderate"="#31688e", "Low"="#440154", "VeryLow" = "#2E013E")

# set plot order
zscored_comb$Viability_Bin <- factor(zscored_comb$Viability_Bin, levels = c("High", "Normal", "Moderate", "Low", "VeryLow"))

# add an id to not rely only on rownumbers to per well data
zscored_comb_ot12 <- zscored_comb %>% 
                   filter(grepl("MeanAreaOT1|MeanAreaOT2|DAPI", Feature)) %>% 
                   pivot_wider(id_cols = c(VCFG_Plate_ID, WellID, QCL_Sample_Number, Compound_Name, Concentration, Units, Viability_Bin), names_from = Feature, values_from = c(Raw, Norm, Robust_ZScore)) %>% 
  mutate(id = paste("id", 1:n()), 
       Compound_Name_Unique = if_else(duplicated(Compound_Name), "B", "A")) %>%  
  select(-"Robust_ZScore_Total_DAPI_Count")

# pivot to form per comp data
zscored_comb_ot12_percomp <- zscored_comb_ot12 %>% 
  pivot_wider(id_cols = c("Compound_Name", "QCL_Sample_Number"), names_from = Concentration, names_prefix = "Conc", values_from = c(8:15)) %>% 
  mutate(id = paste("id", 1:n()))

# combine df into a key
shared_zscored_comb_ot12 <- SharedData$new(zscored_comb_ot12, key = ~ id, group = "id")
shared_zscored_comb_ot12_percomp <- SharedData$new(zscored_comb_ot12_percomp, key = ~ id, group = "id")
  
# create filter check box & slider
filters <- bscols(
  list(
    filter_checkbox("Viability_Bin", "Viability Bin", shared_zscored_comb_ot12, ~Viability_Bin),
    filter_slider("Robust_ZScore_MeanAreaOT1", "OT1 Robust ZScore", shared_zscored_comb_ot12, ~Robust_ZScore_MeanAreaOT1, step = 0.1),
    filter_slider("Robust_ZScore_MeanAreaOT2", "OT2 Robust ZScore", shared_zscored_comb_ot12, ~Robust_ZScore_MeanAreaOT2, step = 0.1)))

# create ggplot
p <- ggplot(shared_zscored_comb_ot12, aes(x = Norm_MeanAreaOT1, y = Norm_MeanAreaOT2, 
                             color = Viability_Bin)) +
  geom_point(aes(text = paste0('Compound: ', Compound_Name,'<br>QCL Sample Number: ', QCL_Sample_Number,'<br>WellID:', WellID))) +  scale_colour_manual(values = colour_palette, name = "Concentration (uM)", guide = "none") +
  # xlim(0, 2) +
  facet_wrap(~Concentration, labeller = labeller(Concentration = ~ paste0(.x, "uM")))+
  theme_bw()

# convert to Plotly
plotly_plot <- ggplotly(p) %>%
  highlight("plotly_selected") %>% 
  layout(showlegend = F)

# combine filters and plot
bscols(widths = c(2, 10), filters, plotly_plot)

Total Dose Count:
Total Unique Compounds:

Per Well

datatable(shared_zscored_comb_ot12, extensions = c('Buttons', 'Scroller'), rownames = FALSE,
  options = list(
  dom = 'Blfrtip',
  columnDefs = list(list(className = 'dt-center',targets="_all")),
  lengthMenu = list(c(5, -1), c('5', 'All')),
          pageLength = 5,
  buttons = list(list(extend = 'csv',
                      filename = paste0(prefix, "_OT1vsOT2_viability")),
                 list(extend = 'excel',
                      filename = paste0(prefix, "_OT1vsOT2_viability"), title = NULL)),
  searching = TRUE,
  scrollX = TRUE))

Per Compound

datatable(shared_zscored_comb_ot12_percomp, extensions = c('Buttons', 'Scroller'), rownames = FALSE,
  options = list(
  dom = 'Blfrtip',
  columnDefs = list(list(className = 'dt-center',targets="_all")),
  lengthMenu = list(c(5, -1), c('5', 'All')),
          pageLength = 5,
  buttons = list(list(extend = 'csv',
                      filename = paste0(prefix, "_OT1vsOT2_viability")),
                 list(extend = 'excel',
                      filename = paste0(prefix, "_OT1vsOT2_viability"), title = NULL)),
  searching = TRUE,
  scrollX = TRUE))



 

Analysed by Ann Rann Wong

Victorian Centre for Functional Genomics

 


sessionInfo()
R version 4.4.2 (2024-10-31)
Platform: x86_64-pc-linux-gnu
Running under: Rocky Linux 9.5 (Blue Onyx)

Matrix products: default
BLAS/LAPACK: FlexiBLAS OPENBLAS-OPENMP;  LAPACK version 3.9.0

locale:
 [1] LC_CTYPE=en_AU.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_AU.UTF-8        LC_COLLATE=en_AU.UTF-8    
 [5] LC_MONETARY=en_AU.UTF-8    LC_MESSAGES=en_AU.UTF-8   
 [7] LC_PAPER=en_AU.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_AU.UTF-8 LC_IDENTIFICATION=C       

time zone: Australia/Melbourne
tzcode source: system (glibc)

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] summarywidget_0.0.0.9000 htmltools_0.5.8.1        crosstalk_1.2.1         
 [4] plotly_4.10.4            flexdashboard_0.6.2      lubridate_1.9.4         
 [7] forcats_1.0.0            stringr_1.5.1            dplyr_1.1.4             
[10] purrr_1.0.4              readr_2.1.5              tidyr_1.3.1             
[13] tibble_3.2.1             ggplot2_3.5.1            tidyverse_2.0.0         
[16] viridis_0.6.5            viridisLite_0.4.2        reshape2_1.4.4          
[19] DT_0.33                  readxl_1.4.4             data.table_1.17.0       
[22] workflowr_1.7.1         

loaded via a namespace (and not attached):
 [1] gtable_0.3.6      xfun_0.51         bslib_0.9.0       htmlwidgets_1.6.4
 [5] processx_3.8.6    callr_3.7.6       tzdb_0.4.0        vctrs_0.6.5      
 [9] tools_4.4.2       ps_1.9.0          generics_0.1.3    parallel_4.4.2   
[13] pkgconfig_2.0.3   lifecycle_1.0.4   compiler_4.4.2    git2r_0.35.0     
[17] munsell_0.5.1     getPass_0.2-4     httpuv_1.6.15     sass_0.4.9       
[21] yaml_2.3.10       lazyeval_0.2.2    crayon_1.5.3      later_1.4.1      
[25] pillar_1.10.1     jquerylib_0.1.4   whisker_0.4.1     cachem_1.1.0     
[29] mime_0.12         tidyselect_1.2.1  digest_0.6.37     stringi_1.8.4    
[33] labeling_0.4.3    rprojroot_2.0.4   fastmap_1.2.0     grid_4.4.2       
[37] colorspace_2.1-1  cli_3.6.4         magrittr_2.0.3    withr_3.0.2      
[41] scales_1.3.0      promises_1.3.2    bit64_4.6.0-1     timechange_0.3.0 
[45] rmarkdown_2.29    httr_1.4.7        bit_4.5.0.1       gridExtra_2.3    
[49] cellranger_1.1.0  hms_1.1.3         memoise_2.0.1     shiny_1.10.0     
[53] evaluate_1.0.3    knitr_1.49        rlang_1.1.5       Rcpp_1.0.14      
[57] xtable_1.8-4      glue_1.8.0        vroom_1.6.5       rstudioapi_0.17.1
[61] jsonlite_1.9.1    R6_2.6.1          plyr_1.8.9        fs_1.6.5