::install_github("dionnecargy/SeroTrackR") # To download the package
devtoolslibrary(SeroTrackR) # To load the package
library(tidyverse) # for data wrangling and visualisation
library(knitr) # for RMarkdown visualisation and PDF generation
SeroTrackR
Load the Package and Data
File Name Convention
The following files are required for this app. You might like to organise your folder as follows:
my_R_project
└── raw_data_files/
├── raw_MAGPIX_data/
│ ├── raw_data_plate1.csv
│ ├── raw_data_plate2.csv
│ └── raw_data_plate3.csv
├── raw_BioPlex_data/
│ ├── raw_data_plate4.xlsx
│ ├── raw_data_plate5.xlsx
│ └── raw_data_plate6.xlsx
├── platelayout.xlsx
└── outputs/
Raw Data Files
The raw data
files should all contain “plate1”, “plate2”, “plate3”, etc., in the file name. Ensure there are no special characters or spaces between the “plate” and number. To keep things simple, please also do not write any leading 0’s before the number e.g., do not write “plate01”.
For the MAGPIX and Intelliflex Machines:
You can pre-program the MAGPIX machine so that you can export all the raw data directly from the machine once the plate reading is completed. There is no need to edit the raw data file that comes from the MAGPIX.
Within your plate layout in the MAGPIX, you can use the “U” button for all unknown samples, “B” button for Background or Blank samples, and “S” for Standard Curve samples. For the control wells, please feel free to edit these labels so that the ID is just “B”, “S1”, “S2”, “S3”….”S10”.
You can also write in the Operator i.e., who ran the assay! This is useful to track variation in plates between experiments.
For the Bioplex Machines:
Isolate names will tend to be written as ‘X1’, ‘X2’, ‘X3’… and saved as an .xlsx file. Specifics on Bio-Plex machines will be added shortly.
Plate Layout File
The plate layout
file should contain all of the plate layouts in each tab. For each 96-well plate that you run on the Luminex machine, prepare a plate layout that includes the sample labels that will match your raw data. The application will match the raw data to the corresponding sample based on the plate layout that you import.
Make sure that your sample labels in the plate layout are as follows:
- Standards: Labels start with “S” and then a number as required (e.g. S1, S2, S3 or Standard1, Standard2, Standard3).
- Blanks: Labels start with “B” and then a number as required if there is more than one blank sample (.e.g ‘B1’, ‘B2’, or ‘Blank 1’, ‘Blank2’ etc).
- Unknown Samples: Label your unknown samples according to your specific sample codes (e.g. ABC001, ABC002).
The package expects standards to start with “S” and blanks to start with “B”, but everything else with a label will be considered an unknown sample. If you have other types of samples, for example a positive control, you can use a different sample label to the other unknown study samples (i.e. “PositiveControl” in addition to the “ABC” study codes).
The standards S1-10 correspond to the following dilution concentrations:
S1 | S2 | S3 | S4 | S5 | S6 | S7 | S8 | S9 | S10 |
---|---|---|---|---|---|---|---|---|---|
1/50 | 1/100 | 1/200 | 1/400 | 1/800 | 1/1600 | 1/3200 | 1/6400 | 1/12800 | 1/25600 |
The standards S1-5 correspond to the following dilution concentrations:
S1 | S2 | S3 | S4 | S5 |
---|---|---|---|---|
1/50 | 1/250 | 1/1250 | 1/6250 | 1/31250 |
Data Analysis: runPvSeroPipeline()
Run this global function runPvSeroPipeline()
embedded within the {SeroTrackR}
R package! This function contains all of the steps in order of how to perform the Plasmodium vivax serology test and treat protocol as found in our application!
Using Tutorial Dataset: Load the Data
We will be using the build-in files in the R package for this tutorial, as shown below.
<- c(
your_raw_data system.file("extdata", "example_MAGPIX_plate1.csv", package = "SeroTrackR"),
system.file("extdata", "example_MAGPIX_plate2.csv", package = "SeroTrackR"),
system.file("extdata", "example_MAGPIX_plate3.csv", package = "SeroTrackR")
)<- system.file("extdata", "example_platelayout_1.xlsx", package = "SeroTrackR") your_plate_layout
To run your OWN data, follow the code below and uncomment (i.e., remove the hashtags):
# your_raw_data <- c(
# "PATH/TO/YOUR/FILE/plate1.csv",
# "PATH/TO/YOUR/FILE/plate2.csv",
# "PATH/TO/YOUR/FILE/plate3.csv"
# )
# your_plate_layout <- "PATH/TO/YOUR/FILE/plate_layout.xlsx"
Run Classification: Yes
<- runPvSeroPipeline(
final_analysis raw_data = your_raw_data,
plate_layout = your_plate_layout,
platform = "magpix",
location = "ETH",
experiment_name = "experiment1",
classify = "Yes",
algorithm_type = "antibody_model",
sens_spec = "maximised"
)
Classification
This is a table containing the classification results (seropositive or seronegative) for each SampleID
. In this case, the classification results are stored in the pred_class_max
column as we chose the sens_spec = "maximised"
. If you change it to another type of threshold, then the suffix of that column will change accordingly.
You will also see the relative antibody unit (RAU) values (columns with antigen names), whether the sample passed QC check (QC_total
) and the plate
that they were run on.
1]] %>%
final_analysis[[head() %>%
kable()
SampleID | Plate | QC_total | EBP | LF005 | LF010 | LF016 | MSP8 | PTEX150 | PvCSS | RBP2b.P87 | pred_class_max |
---|---|---|---|---|---|---|---|---|---|---|---|
ABC013 | plate1 | pass | 0.0003339 | 0.0015045 | 0.0002163 | 0.0014567 | 0.0000195 | 0.0001591 | 0.0000772 | 0.0003714 | seropositive |
ABC097 | plate2 | pass | 0.0004324 | 0.0015615 | 0.0001944 | 0.0013373 | 0.0000195 | 0.0001549 | 0.0000705 | 0.0009189 | seropositive |
ABC181 | plate3 | pass | 0.0003822 | 0.0015832 | 0.0002144 | 0.0013711 | 0.0000195 | 0.0001582 | 0.0000710 | 0.0002070 | seropositive |
ABC022 | plate1 | pass | 0.0200000 | 0.0200000 | 0.0007373 | 0.0194885 | 0.0006247 | 0.0003145 | 0.0006008 | 0.0004895 | seropositive |
ABC106 | plate2 | pass | 0.0057123 | 0.0193731 | 0.0007458 | 0.0195240 | 0.0006263 | 0.0003077 | 0.0006480 | 0.0126203 | seropositive |
ABC190 | plate3 | pass | 0.0098260 | 0.0200000 | 0.0007400 | 0.0200000 | 0.0006020 | 0.0003171 | 0.0006555 | 0.0175253 | seropositive |
Standard Curve Plot
The standard curve plots are generated from the antibody data from the standards you indicated in your plate layout (e.g. S1-S10) and Median Fluorescent Intensity (MFI) units are displayed in log10-scale. In the case of the PvSeroTaT multi-antigen panel, the antigens will be displayed and in general your standard curves should look relatively linear (only when the y-axis is on logarithmic scale).
2]] final_analysis[[
Bead Counts QC Plot
A summary of the bead counts for each plate well are displayed, with blue indicating there are sufficient beads (≥15) or red when there are not enough. If any of the wells are red, they should be double-checked manually and re-run on a new plate if required.
The function will inform you whether there are “No repeats necessary” or provide a list of samples to be re-run. In the example data, the beads in plate 2 wells A1 and A2 will need to be repeated
3]] # Plot final_analysis[[
4]] # Samples to repeat final_analysis[[
# A tibble: 2 × 4
Location SampleID Plate QC
<chr> <chr> <chr> <chr>
1 A1 Blank1 plate2 fail
2 A2 Blank2 plate2 fail
Blanks QC Plot
The Median Fluorescent Intensity (MFI) units for each antigen is displayed for your blank samples. In general, each blank sample should have ≤50 MFI for each antigen, if they are higher they should be cross-checked manually.
In the example data, blank samples recorded higher MFI values for LF005 on plate 1 and should be checked to confirm this is expected from the assay.
5]] final_analysis[[
Model Output Plot
The automated data processing in this app allows you to convert your Median Fluorescent Intensity (MFI) data into Relative Antibody Units (RAU) by fitting a 5-parameter logistic function to the standard curve on a per-antigen level. The results from this log-log conversion should look relatively linear for each antigen.
6]] final_analysis[[
$plate1
$plate2
$plate3
Run Classification: No
<- runPvSeroPipeline(
no_classification_final_analysis raw_data = your_raw_data,
plate_layout = your_plate_layout,
platform = "magpix",
location = "ETH",
experiment_name = "experiment1",
classify = "No", ########################## key if you do NOT want any classification performed i.e., you do not have PvSeroTaT antigens
algorithm_type = "antibody_model",
sens_spec = "maximised"
)
MFI and RAU Data
1]] %>%
no_classification_final_analysis[[head() %>%
kable()
SampleID | Plate | QC_total | EBP_MFI | EBP_Dilution | LF005_MFI | LF005_Dilution | LF010_MFI | LF010_Dilution | LF016_MFI | LF016_Dilution | MSP8_MFI | MSP8_Dilution | PTEX150_MFI | PTEX150_Dilution | PvCSS_MFI | PvCSS_Dilution | RBP2b.P87_MFI | RBP2b.P87_Dilution |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ABC013 | plate1 | pass | 2712 | 0.0003339 | 1569.0 | 0.0015045 | 673 | 0.0002163 | 327 | 0.0014567 | 182.0 | 1.95e-05 | 936.0 | 0.0001591 | 223 | 0.0000772 | 1068.0 | 0.0003714 |
ABC014 | plate1 | pass | 134 | 0.0000216 | 378.0 | 0.0004204 | 117 | 0.0000277 | 197 | 0.0010331 | 58.0 | 1.95e-05 | 122.0 | 0.0000225 | 93 | 0.0000298 | 465.5 | 0.0001536 |
ABC015 | plate1 | pass | 182 | 0.0000232 | 209.0 | 0.0002466 | 208 | 0.0000616 | 374 | 0.0015985 | 221.5 | 1.95e-05 | 293.0 | 0.0000535 | 868 | 0.0002235 | 463.0 | 0.0001527 |
ABC016 | plate1 | pass | 152 | 0.0000222 | 229.5 | 0.0002688 | 101 | 0.0000244 | 89 | 0.0005994 | 48.0 | 1.95e-05 | 109.0 | 0.0000220 | 110 | 0.0000383 | 591.0 | 0.0001989 |
ABC017 | plate1 | pass | 1135 | 0.0001478 | 236.0 | 0.0002758 | 299 | 0.0000950 | 507 | 0.0019840 | 209.5 | 1.95e-05 | 1665.5 | 0.0002668 | 266 | 0.0000893 | 671.0 | 0.0002277 |
ABC018 | plate1 | pass | 174 | 0.0000229 | 395.0 | 0.0004370 | 175 | 0.0000460 | 78 | 0.0005452 | 70.0 | 1.95e-05 | 294.5 | 0.0000537 | 92 | 0.0000296 | 103.0 | 0.0000238 |
QC Plots
Repeat the same steps as above to find the QC plots!
#### Standard Curve Plot
2]] no_classification_final_analysis[[
#### Bead Counts QC Plot
3]] # Plot no_classification_final_analysis[[
4]] # Samples to repeat no_classification_final_analysis[[
# A tibble: 2 × 4
Location SampleID Plate QC
<chr> <chr> <chr> <chr>
1 A1 Blank1 plate2 fail
2 A2 Blank2 plate2 fail
#### Blanks QC Plot
5]] no_classification_final_analysis[[
#### Model Output Plot
6]] no_classification_final_analysis[[
$plate1
$plate2
$plate3
Create a PDF Report
renderQCReport(
your_raw_data,
your_plate_layout, "magpix",
location = "ETH"
)
|
| | 0%
|
|... | 7%
|
|...... | 13% [setup]
|
|......... | 20%
|
|............. | 27% [standard curves plot]
|
|................ | 33%
|
|................... | 40% [model results plot]
|
|...................... | 47%
|
|......................... | 53% [bead counts plot]
|
|............................ | 60%
|
|............................... | 67% [check repeats table]
|
|.................................. | 73%
|
|...................................... | 80% [blank samples plot]
|
|......................................... | 87%
|
|............................................ | 93% [plate layouts]
|
|...............................................| 100%
/Applications/RStudio.app/Contents/Resources/app/quarto/bin/tools/x86_64/pandoc +RTS -K512m -RTS template.knit.md --to latex --from markdown+autolink_bare_uris+tex_math_single_backslash --output /Users/Dionne/Documents/GitHub/SeroTrackR/experiment1_20250801_ETH_v1.3.1_QCreport.tex --lua-filter /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/library/rmarkdown/rmarkdown/lua/pagebreak.lua --lua-filter /Library/Frameworks/R.framework/Versions/4.4-x86_64/Resources/library/rmarkdown/rmarkdown/lua/latex-div.lua --embed-resources --standalone --highlight-style tango --pdf-engine pdflatex --variable graphics --include-in-header /var/folders/bh/0yzt0_x97vj_zktb_39c1xvh0000gn/T//RtmpvzoQUs/rmarkdown-str77cf2a8ab2d7.html --variable 'geometry:margin=1in' --include-in-header /var/folders/bh/0yzt0_x97vj_zktb_39c1xvh0000gn/T//RtmpvzoQUs/rmarkdown-str77cf77e35712.html
Data Analysis: Pk/Pv/Pf
5-Point Standard Curve
Step 1: Load your data!
Firstly, we will be using our example data that’s in-built in the package. Here replace the system.file()
argument with the file path for your package.
<- c(
your_raw_data_5std system.file("extdata", "example_MAGPIX_pk_5std_plate1.csv", package = "SeroTrackR"),
system.file("extdata", "example_MAGPIX_pk_5std_plate2.csv", package = "SeroTrackR")
)<- system.file("extdata", "example_platelayout_pk_5std.xlsx", package = "SeroTrackR") your_plate_layout_5std
Step 2: Read your data and process MFI to RAU
Caitlin and Dionne have worked on a function to (a) process raw Serological data and (b) convert MFI to RAU. The runPlasmoSero5point()
function will output three data frames: (i) Results from MAGPIX (MFI), (ii), Counts for each sample, (iii) Processed RAU values for each sample. These dataframes can be used for further analysis and can be transformed as you wish.
<- runPlasmoSero5point(
pk_analysis_1 raw_data = your_raw_data_5std,
platform = "magpix",
plate_layout = your_plate_layout_5std,
date = "2025-07-30" # Optional: This will default to today's date
)
Standard Curve Plot: One Curve
As requested, this is a plot of ONE standard curve which you will specify. You can modify this plot as you see fit by piping other ggplot2()
arguments.
plotOneStdCurve(pk_analysis_1, "plate1")
Standard Curve Plot: Compare Two Curves
This function allows you to plot all of your standard curves.
plotManyStdCurves(pk_analysis_1)
10-Point Standard Curve
These steps are very similar to the 5-point standard curve, except here we use the runPlasmoSero10point()
function.
Step 1: Load your data!
<- c(
your_raw_data_10std system.file("extdata", "example_MAGPIX_pk_10std_plate1.csv", package = "SeroTrackR"),
system.file("extdata", "example_MAGPIX_pk_10std_plate2.csv", package = "SeroTrackR")
)<- system.file("extdata", "example_platelayout_pk_10std.xlsx", package = "SeroTrackR") your_plate_layout_10std
Step 2: Read your data and process MFI to RAU
<- runPlasmoSero10point(
pk_analysis_2 raw_data = your_raw_data_10std,
platform = "magpix",
plate_layout = your_plate_layout_10std,
date = "2025-07-30" # Optional: This will default to today's date
)
Standard Curve Plot: One Curve
plotOneStdCurve(pk_analysis_2, "plate1")
Standard Curve Plot: Compare Two Curves
plotManyStdCurves(pk_analysis_2)
Visualisation of the {SeroTrackR}
R Package
We have used the {targets}
R package to generate a pipeline! This allows us to:
- Automatically detect the dependencies of each step
- One-command execution
- Automatic caching
- Automatic detection of changes in data and/or code
For more information on {targets}
see this tutorial.
here() starts at /Users/Dionne/Documents/GitHub/SeroTrackR
✔ skipped pipeline [47ms, 15 skipped]
Warning message:
package ‘targets’ was built under R version 4.4.1
here() starts at /Users/Dionne/Documents/GitHub/SeroTrackR
Warning message:
package ‘targets’ was built under R version 4.4.1
FAQs
I have multiple plate layout files. How can I input them?
Use the getPlateLayout()
function to create a master plate layout file to then input into the other functions in the package!
getPlateLayout("../inst/extdata/")
Here replace “../inst/extdata/” with the main file that contains your folders. For example, if your folder looks like this:
my_R_project/
└── raw_data_files/
├── plate_1/
│ ├── raw_magpix_data_plate1.csv
│ └── plate_layout_1.xlsx
├── plate_2/
│ ├── raw_magpix_data_plate2.csv
│ └── plate_layout_2.xlsx
└── plate_3/
├── raw_magpix_data_plate3.csv
└── plate_layout_3.xlsx
you would write:
# getPlateLayout("raw_data_files/")