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.
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"
)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.
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 |
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 |
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())The same workflow can be applied to the Stemonix May 2022 files in
inst/extdata/Stemonix_05_2022 by swapping the file
paths:
| 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.