Using the CalFluxTools Example Data

CalFluxTools provides a workflow for parsing, quality-controlling, visualizing, transforming, and analyzing calcium flux plate-reader data. It parses calcium flux data in the statAll format together with plate maps and a parameter manifest, organizes the data into plate and plate-set objects, applies processing rules such as masking, parameter filtering, categorical encoding, blank or missing-value replacement, and baseline-normalized transformations, and supports downstream outputs including data completeness reports, plate heatmaps, bar charts, z-prime factor summaries, PCA, and random-forest classification results.

This vignette walks through the analysis of example calcium flux files distributed with CalFluxTools. The package includes plate-reader output files in statAll format, matching plate maps, and Excel parameter manifests under inst/extdata.

library(CalFluxTools)
library(ggplot2)

Locate the Example Files

Use system.file() to locate packaged data after CalFluxTools has been installed. The helper below also works when knitting this vignette from the source tree.

example_file <- function(...) {
  installed_path <- system.file("extdata", ..., package = "CalFluxTools")
  if (nzchar(installed_path)) {
    return(installed_path)
  }

  source_paths <- c(
    file.path("..", "inst", "extdata", ...),
    file.path("inst", "extdata", ...)
  )
  existing_paths <- source_paths[file.exists(source_paths)]
  if (length(existing_paths) > 0) {
    return(normalizePath(existing_paths[[1]], mustWork = TRUE))
  }

  stop("Could not locate CalFluxTools example data.")
}

simulated_dir <- example_file("Simulated_data")
stemonix_dir <- example_file("Stemonix_05_2022")

list.files(simulated_dir)
#> [1] "CalFlux_Parameter_selection_Control_PC3_New_T5.xlsx"           
#> [2] "CalFluxData_Log_20260714_1711.log"                             
#> [3] "CalFluxData_Log_20260714_1715.log"                             
#> [4] "Control_Plate_map_1_PC3_New_T2.csv"                            
#> [5] "Control_plate1_Test1_30min_n001_Pro2_New_T1.statAll"           
#> [6] "Control_plate1_Test1_BL_n001_Pro2_New_T1.statAll"              
#> [7] "Updated_Control_PC3_New_T5_CalFluxData_Analysis_20260714_1715."
list.files(stemonix_dir)
#> [1] "05272022_Stemonix_plate3_60mins_n001_Pro2.statAll"
#> [2] "05272022_Stemonix_plate3_BL_n001_Pro2.statAll"    
#> [3] "CalFlux_Parameter_selection_MOE9_60_PC.xlsx"      
#> [4] "Stemonix_MOE9_60_PC_ON2_Full.csv"

The simulated dataset contains one baseline read, one post-dose read, one plate map, and one manifest describing the intended analysis parameters. This enables us to use the “paired analysis” pipeline provided by the package.

baseline_file <- example_file(
  "Simulated_data",
  "Control_plate1_Test1_BL_n001_Pro2_New_T1.statAll"
)
postdose_file <- example_file(
  "Simulated_data",
  "Control_plate1_Test1_30min_n001_Pro2_New_T1.statAll"
)
plate_map_file <- example_file(
  "Simulated_data",
  "Control_Plate_map_1_PC3_New_T2.csv"
)
parameter_file <- example_file(
  "Simulated_data",
  "CalFlux_Parameter_selection_Control_PC3_New_T5.xlsx"
)

Inspect the Parameter Manifest

The manifest stores analysis settings and per-parameter processing rules. The copy currently included with this source tree records the original development root directory, so the low-level examples below read the files directly from inst/extdata.

analysis_settings <- openxlsx::read.xlsx(
  parameter_file,
  sheet = "Analysis_Settings",
  colNames = FALSE
)
analysis_settings <- analysis_settings[!is.na(analysis_settings$X1), ]

knitr::kable(
  analysis_settings[, c("X1", "X2", "X3", "X4", "X5")],
  col.names = c("Setting", "Value", "Value 2", "Value 3", "Value 4")
)
Setting Value Value 2 Value 3 Value 4
Analysis_Name Updated_Control_PC3_New_T5 NA NA NA
Root_Directory /Users/pattac/Desktop/aso_dev/Updated_Simulated_Data_Files NA NA NA
Plate_Format 384 NA NA NA
Plate_File_Pairs NA NA NA NA
Plate_Label File Plate_Map Plate_Pair Output Dir
Ref_for_Post-Dose Timepoint Control_plate1_Test1_BL_n001_Pro2_New_T1.statAll Control_Plate_map_1_PC3_New_T2.csv 1 Post-Dose Timepoint
Post-Dose Timepoint Control_plate1_Test1_30min_n001_Pro2_New_T1.statAll Control_Plate_map_1_PC3_New_T2.csv 1 Post-Dose Timepoint
Processing NA NA NA NA
Paired_Analysis TRUE NA NA NA
Median_Background_Correction FALSE NA NA NA
Imputation_Method per_param_pct_min NA NA NA
Imputation_Fraction_Min 0.01 NA NA NA
Transformation log2ratio (log2ratio, pct_control) NA NA
Export_QC_PDF TRUE NA NA NA
Export_Data_Dropout_Table TRUE NA NA NA
Export_Transformed_Averages_and_SD TRUE NA NA NA
Stat Paired_t_Test NA NA NA
Negative_Control_Key Negative Control NA NA NA
Positive_Control_Key Positive Control NA NA NA
Test_Sample_Key Test NA NA NA

parameter_info <- openxlsx::read.xlsx(parameter_file, sheet = "Parameter_Info")
knitr::kable(head(parameter_info[, c("Statistic", "Suggested_Abbreviation", "Use")]))
Statistic Suggested_Abbreviation Use
Mean Peak Amplitude (RFU) PkAmp 1
Peak Amplitude S.D. (RFU) PkAmp_SD 1
Number of Peaks PkCt 1
Mean Peak Rate (PpM) PkRate 1
Peak Rate S.D. (PpM) PkRate_SD 0
Peak Spacing (s) PkSp 1

If you want to run the full wrapper from this example data, first create a new manifest that points at the installed extdata directory and the filenames distributed with the package.

patched_parameter_file <- tempfile(fileext = ".xlsx")
file.copy(parameter_file, patched_parameter_file, overwrite = TRUE)

manifest <- openxlsx::loadWorkbook(patched_parameter_file)
settings <- openxlsx::read.xlsx(
  patched_parameter_file,
  sheet = "Analysis_Settings",
  colNames = FALSE
)

settings[settings$X1 == "Root_Directory", "X2"] <- paste0(
  normalizePath(simulated_dir),
  .Platform$file.sep
)
settings[
  settings$X1 == "Control_plate1_Test1_BL_n001_Pro2_New_T1.statAll",
  "X2"
] <- "Control_plate1_Test1_BL_n001_Pro2_New_T1.statAll"
settings[
  settings$X1 == "Control_plate1_Test1_30min_n001_Pro2_New_T1.statAll",
  "X2"
] <- "Control_plate1_Test1_30min_n001_Pro2_New_T1.statAll"

openxlsx::writeData(
  manifest,
  sheet = "Analysis_Settings",
  x = settings,
  colNames = FALSE
)
openxlsx::saveWorkbook(manifest, patched_parameter_file, overwrite = TRUE)

calflux_data <- processCalFluxData(patched_parameter_file)

Although it is easiest to run the package using the processCalFluxData function, lower-level functions are also available for data parsing, transformation and visualization.

Parse Plate Reads

Use read_calflux_data() to parse each statAll file into a plate S4 object. The metadata reader used by the package is currently internal, so this example uses CalFluxTools::: to attach the plate map in the same way as the analysis wrapper.

baseline_plate <- read_calflux_data(baseline_file)
postdose_plate <- read_calflux_data(postdose_file)

baseline_plate <- CalFluxTools:::read_calflux_metadata(
  plate_map_file,
  baseline_plate
)
postdose_plate <- CalFluxTools:::read_calflux_metadata(
  plate_map_file,
  postdose_plate
)

plate_summary <- data.frame(
  read = c("Baseline", "Post-dose"),
  plate_id = c(baseline_plate@plateId, postdose_plate@plateId),
  wells = c(length(baseline_plate@wellIds), length(postdose_plate@wellIds)),
  parameters = c(length(baseline_plate@parameters), length(postdose_plate@parameters))
)
knitr::kable(plate_summary)
read plate_id wells parameters
Baseline 05272022_Stemonix_plate3_BL_n001 384 63
Post-dose 05272022_Stemonix_plate3_BL_n001 384 63

The plate map provides the well-level sample annotations used for masking, controls, test samples, concentrations, and plotting.

plate_map <- baseline_plate@plateAnnotMap

well_type_summary <- as.data.frame(table(plate_map$WellType))
names(well_type_summary) <- c("WellType", "Wells")
knitr::kable(well_type_summary)
WellType Wells
Empty 325
Negative Control 5
Positive Control 6
Test 48

compound_summary <- aggregate(
  Well ~ Compound + WellType,
  data = plate_map,
  FUN = length
)
names(compound_summary)[names(compound_summary) == "Well"] <- "Wells"
compound_summary <- compound_summary[
  order(compound_summary$WellType, compound_summary$Compound),
]
knitr::kable(head(compound_summary, 12))
Compound WellType Wells
Empty Empty 307
PC1 Empty 6
PC2 Empty 6
PC4 Empty 6
Veh Negative Control 5
PC3 Positive Control 6
Non-Toxic#1 Test 24
Toxic#1 Test 24

Check Data Completeness

missingDataReport() summarizes missing, blank, empty, masked, and usable wells per parameter. calculateParamCV() provides a quick coefficient of variation screen on the same plate object.

missing_report <- missingDataReport(baseline_plate)
missing_report <- missing_report[
  order(missing_report$Missing_Count, decreasing = TRUE),
]
knitr::kable(head(missing_report, 10))
Parameter Well_Count Empty_Wells Masked_Wells Keeper_Wells NA_Count Blank_Count Missing_Count Good_Count
Mean EAD-like peak Rate (PpM) Mean EAD-like peak Rate (PpM) 384 0 0 384 384 0 384 0
EAD-like peak Rate S.D. (PpM) EAD-like peak Rate S.D. (PpM) 384 0 0 384 384 0 384 0
Number of EAD-like peaks S.D. Number of EAD-like peaks S.D. 384 0 0 384 384 0 384 0
Mean Peak Amplitude (RFU) Mean Peak Amplitude (RFU) 384 0 0 384 0 0 0 384
Peak Amplitude S.D. (RFU) Peak Amplitude S.D. (RFU) 384 0 0 384 0 0 0 384
Number of Peaks Number of Peaks 384 0 0 384 0 0 0 384
Mean Peak Rate (PpM) Mean Peak Rate (PpM) 384 0 0 384 0 0 0 384
Peak Rate S.D. (PpM) Peak Rate S.D. (PpM) 384 0 0 384 0 0 0 384
Peak Spacing (s) Peak Spacing (s) 384 0 0 384 0 0 0 384
Peak Spacing S.D. (s) Peak Spacing S.D. (s) 384 0 0 384 0 0 0 384

cv_report <- calculateParamCV(baseline_plate)
cv_report <- cv_report[order(cv_report$CV, decreasing = TRUE, na.last = TRUE), ]
knitr::kable(head(cv_report, 10))
Parameter CV
Number of EAD-like peaks per well Number of EAD-like peaks per well 161.18996
Mean Number of EAD-like peaks Mean Number of EAD-like peaks 147.46672
Peak Rate S.D. (PpM) Peak Rate S.D. (PpM) 59.28361
Decay time S.D. (s) Decay time S.D. (s) 41.36705
Linear time to % of peak S.D. (RFU/s) Linear time to % of peak S.D. (RFU/s) 40.57299
Rise time S.D. (s) Rise time S.D. (s) 39.88877
Decay slope S.D. (RFU/s) Decay slope S.D. (RFU/s) 20.49616
CTDP @ 10% S.D. CTDP @ 10% S.D. 20.47385
Rise slope S.D. (RFU/s) Rise slope S.D. (RFU/s) 19.86301
CTD @ 10% S.D. CTD @ 10% S.D. 19.69281

Transform and Visualize data

Create a plateset, add the baseline and post-dose plates, then compute the log2 ratio for each parameter. Because read_calflux_data() retains well_id as the first column, set firstDataCol = 2.

plate_set <- new("plateset")
plate_set <- addPlate(plate_set, baseline_plate, "Baseline")
plate_set <- addPlate(plate_set, postdose_plate, "PostDose")

plate_set <- dataTransform(
  plate_set,
  platePair = c("Baseline", "PostDose"),
  method = "log2ratio",
  firstDataCol = 2
)

names(plate_set@transformedPlateData)
#> [1] "PostDose_vs_Baseline_(log2ratio)"

log2ratio <- plate_set@transformedPlateData[[1]]
preview_parameters <- colnames(log2ratio)[seq_len(min(3, ncol(log2ratio)))]

knitr::kable(
  head(
    cbind(Well = baseline_plate@wellIds, log2ratio[, preview_parameters, drop = FALSE]),
    10
  )
)
Well Mean Peak Amplitude (RFU) Peak Amplitude S.D. (RFU) Number of Peaks
A01 0.0028574 0.0348581 -0.0349850
A02 -0.0085636 0.1108237 -0.0200264
A03 0.0109181 0.0922941 0.0165130
A04 0.0013695 -0.1212015 -0.0306598
A05 -0.0024235 0.0127922 -0.0065665
A06 0.0171634 0.0987989 -0.0179437
A07 0.0089921 -0.0079388 0.0186731
A08 -0.0186825 -0.1908555 0.0157524
A09 -0.0089014 0.0194016 -0.0020857
A10 0.0037831 -0.0197916 0.0174043

Plotting the transformed values by row and column helps check whether a signal is spatially patterned across the plate.

plot_parameter <- "Rise slope S.D. (RFU/s)"

plot_df <- data.frame(
  Well = baseline_plate@wellIds,
  Row = substr(baseline_plate@wellIds, 1, 1),
  Column = as.integer(sub("^[A-P]", "", baseline_plate@wellIds)),
  Value = as.numeric(log2ratio[[plot_parameter]])
)

ggplot(plot_df, aes(x = Column, y = Row, fill = Value)) +
  geom_tile(color = "white", linewidth = 0.1) +
  coord_fixed() +
  scale_y_discrete(limits = rev(LETTERS[1:16])) +
  scale_x_continuous(breaks = seq(1, 24, by = 2)) +
  scale_fill_gradient2(
    low = "#2C7BB6",
    mid = "white",
    high = "#D7191C",
    midpoint = 0,
    na.value = "grey90"
  ) +
  labs(
    title = paste("Log2 ratio:", plot_parameter),
    x = "Column",
    y = "Row",
    fill = "Log2 ratio"
  ) +
  theme_minimal(base_size = 11) +
  theme(panel.grid = element_blank())

Reuse the Workflow

The same workflow can be applied to the Stemonix May 2022 files in inst/extdata/Stemonix_05_2022 by swapping the file paths:

stemonix_manifest <- data.frame(file = list.files(stemonix_dir))
knitr::kable(stemonix_manifest)
file
05272022_Stemonix_plate3_60mins_n001_Pro2.statAll
05272022_Stemonix_plate3_BL_n001_Pro2.statAll
CalFlux_Parameter_selection_MOE9_60_PC.xlsx
Stemonix_MOE9_60_PC_ON2_Full.csv

For full report generation, use a patched manifest with processCalFluxData(). For focused exploratory work, parse the statAll files, attach the plate map, and call the lower-level QC and transformation helpers shown above.