| Title: | Post-Processing Tools for GC-MS Volatile Organic Compound Data |
|---|---|
| Description: | Provides functions for processing Shimadzu gas chromatography-mass spectrometry (GC-MS) exported text files, extracting Chemical Abstracts Service (CAS) numbers and peak areas, building abundance matrices, annotating compounds, and screening based on occurrence frequency. The package is designed for organizing, identifying, and screening volatile compounds in metabolomics and environmental studies. |
| Authors: | Xiaotao Han [aut, cre] |
| Maintainer: | Xiaotao Han <[email protected]> |
| License: | GPL (>= 3) |
| Version: | 0.2.5 |
| Built: | 2026-10-14 08:06:58 UTC |
| Source: | https://github.com/hanxt97/postvocs |
This function takes a set of CAS numbers (from an abundance matrix) and retrieves chemical names and properties from user-provided libraries or from online databases (via 'webchem'). It supports caching and automatic rate-limiting to comply with PubChem API constraints.
annotate_compounds( abundance_data, lib_source = c("auto", "user", "webchem"), user_lib = NULL, webchem_config = list(rate_limit = 5, chunk_size = 100, cache_file = NULL), use_cache = FALSE, force_retrieve = FALSE )annotate_compounds( abundance_data, lib_source = c("auto", "user", "webchem"), user_lib = NULL, webchem_config = list(rate_limit = 5, chunk_size = 100, cache_file = NULL), use_cache = FALSE, force_retrieve = FALSE )
abundance_data |
Either:
|
lib_source |
Character. Source of chemical information:
Default is '"auto"'. |
user_lib |
Optional data.frame with at least two columns: 'CAS' and 'Name'. Used when 'lib_source = "user"' or '"auto"'. Must be provided if 'lib_source = "user"'. |
webchem_config |
A list controlling online queries:
|
use_cache |
Logical. Whether to use caching. Default is 'FALSE'. If 'FALSE', no cache file is read or written, and 'force_retrieve' is ignored. If 'TRUE', caching is enabled; the cache file path is printed to the console. |
force_retrieve |
Logical. If 'TRUE' and 'use_cache = TRUE', ignore existing cache and query all CAS again. Default 'FALSE'. Has no effect when 'use_cache = FALSE'. |
The function automatically determines the batch strategy based on the number of CAS to query:
1-50: direct individual queries (no chunking).
51-1000: chunked queries using 'chunk_size'.
1001-5000: chunked queries with smaller chunk size (200) and extra delay.
> 5000: a warning is issued recommending the PubChem PUG Download service; the function will still attempt chunked queries but may be slow or rate-limited.
A persistent cache is stored at the location given by 'cache_file' (or the default user cache directory) only when 'use_cache = TRUE'. The cache is a named list where each key is a CAS number and the value is a list of chemical properties, including a 'status' field ('"success"' or '"not_found"'). Cache is updated after each query when enabled.
The 'Annotation_Source' column indicates the origin of the compound name:
'"user_lib"' - from the user‑provided library.
'"web"' - newly queried from webchem in this session.
'"web(cache)"' - retrieved from the cache (previous webchem query).
CAS numbers are automatically cleaned before processing: leading/trailing single quotes are removed and all spaces are stripped.
A list with two components:
annotation |
A data.frame containing all CAS numbers and their retrieved information (ID, CAS, Name, MF, MW, IUPAC_Name, SMILES, InChIKey, InChI, QueryDate, Status, Source). |
abundance_updated |
The original abundance data.frame with two additional columns: 'Compound_Name' and 'Annotation_Source', inserted after the 'CAS' column. |
# Create a small abundance matrix ab <- data.frame( CAS = c("64-17-5", "67-64-1", "75-05-8"), Sample1 = c(100, 200, 300), Sample2 = c(150, 250, 350) ) # Create a user-provided compound library user_lib <- data.frame( CAS = c("64-17-5", "67-64-1"), Name = c("Ethanol", "Acetone"), stringsAsFactors = FALSE ) # Annotate using the user library (no network required) res <- annotate_compounds(ab, lib_source = "user", user_lib = user_lib) # View annotation table head(res$annotation) # Updated abundance matrix with Compound_Name and Annotation_Source head(res$abundance_updated)# Create a small abundance matrix ab <- data.frame( CAS = c("64-17-5", "67-64-1", "75-05-8"), Sample1 = c(100, 200, 300), Sample2 = c(150, 250, 350) ) # Create a user-provided compound library user_lib <- data.frame( CAS = c("64-17-5", "67-64-1"), Name = c("Ethanol", "Acetone"), stringsAsFactors = FALSE ) # Annotate using the user library (no network required) res <- annotate_compounds(ab, lib_source = "user", user_lib = user_lib) # View annotation table head(res$annotation) # Updated abundance matrix with Compound_Name and Annotation_Source head(res$abundance_updated)
Processes all TXT files in a given directory using process_gcms_txt(),
maps the raw file names to user-defined sample names via an Excel mapping file,
and returns two lists: one containing all PeakTables and one containing all
SearchResults, each named by the mapped sample name.
batch_process_gcms( txt_dir, sample_file, sheet = 1, pattern = "\\.txt$", encoding = "UTF-8", ... )batch_process_gcms( txt_dir, sample_file, sheet = 1, pattern = "\\.txt$", encoding = "UTF-8", ... )
txt_dir |
Character. Path to the directory containing the TXT files. |
sample_file |
Character. Path to an Excel file with two columns:
|
sheet |
Integer or character. Sheet name or index in the Excel file.
Default is |
pattern |
Character. Regular expression for file pattern; default
|
encoding |
Character. Encoding of the input TXT files. Default is
|
... |
Additional arguments passed to |
A list with four components:
PeakTables |
A named list of data frames, each representing the
PeakTable of a sample. Names are |
SearchResults |
A named list of data frames, each representing the
SearchResults of a sample. Names are |
failed_files |
Character vector of raw file names that failed to process. |
summary |
A data frame with total, success, and failed counts. |
# Get paths to example data txt_dir <- system.file("extdata/txt", package = "postvocs") sample_file <- system.file("extdata/SampleID.xlsx", package = "postvocs") # Process all .txt files in the example folder result <- batch_process_gcms( txt_dir = txt_dir, sample_file = sample_file, sheet = 1 ) # Access a specific sample's PeakTable peak_sample <- result$PeakTables[[1]] # View summary of processing result$summary # If you want debug output or different encoding, you can pass them: result <- batch_process_gcms(txt_dir, sample_file, debug = TRUE) result <- batch_process_gcms(txt_dir, sample_file, encoding = "GBK")# Get paths to example data txt_dir <- system.file("extdata/txt", package = "postvocs") sample_file <- system.file("extdata/SampleID.xlsx", package = "postvocs") # Process all .txt files in the example folder result <- batch_process_gcms( txt_dir = txt_dir, sample_file = sample_file, sheet = 1 ) # Access a specific sample's PeakTable peak_sample <- result$PeakTables[[1]] # View summary of processing result$summary # If you want debug output or different encoding, you can pass them: result <- batch_process_gcms(txt_dir, sample_file, debug = TRUE) result <- batch_process_gcms(txt_dir, sample_file, encoding = "GBK")
Takes a list of extracted CAS–area vectors (as returned by
extract_peak_areas) and constructs a matrix where rows are CAS
compounds and columns are samples. This is the standard abundance matrix
used for subsequent annotation and screening.
build_cas_abundance(area_list)build_cas_abundance(area_list)
area_list |
A named list of numeric vectors, each vector representing
one sample with CAS numbers as names and total peak areas as values.
Typically the output of |
The function collects all unique CAS numbers from all samples, sorts them, and creates a matrix with CAS as rows and samples as columns. If a sample does not contain a particular CAS, its area is set to 0.
A data frame with the first column "CAS" and subsequent
columns for each sample (in the order they appear in area_list).
Missing values are filled with 0.
# Get paths to example data txt_dir <- system.file("extdata/txt", package = "postvocs") sample_file <- system.file("extdata/SampleID.xlsx", package = "postvocs") # Batch process example files and extract peak areas batch <- batch_process_gcms(txt_dir, sample_file) areas <- extract_peak_areas(batch) # Build abundance matrix (CAS as rows, samples as columns) abund <- build_cas_abundance(areas) # View first few rows and columns head(abund[, 1:5])# Get paths to example data txt_dir <- system.file("extdata/txt", package = "postvocs") sample_file <- system.file("extdata/SampleID.xlsx", package = "postvocs") # Batch process example files and extract peak areas batch <- batch_process_gcms(txt_dir, sample_file) areas <- extract_peak_areas(batch) # Build abundance matrix (CAS as rows, samples as columns) abund <- build_cas_abundance(areas) # View first few rows and columns head(abund[, 1:5])
This function extracts CAS numbers and their corresponding total peak areas from GC-MS PeakTable and SearchResults data. It supports three input types:
A list from process_gcms_txt() (single sample)
A list from batch_process_gcms() (multiple samples)
A folder path containing PeakTable and SearchResults files (CSV or Excel)
extract_peak_areas(x, ...)extract_peak_areas(x, ...)
x |
Either:
|
... |
Additional arguments passed to |
The function automatically detects the input type:
If x is a character path to an existing folder, it scans for
files containing "PeakTable" and "SearchResults" in their names,
pairs them by common prefix, and processes all samples.
If x is a list containing PeakTable and SearchResults,
it processes as a single sample and returns a list with one element.
If x is a list containing PeakTables and SearchResults,
it processes all samples in batch.
A named list. Each element is a named numeric vector with CAS as names and total peak area as values. The list names are sample names.
# Get paths to example data txt_file <- system.file("extdata/txt/sample1.txt", package = "postvocs") txt_dir <- system.file("extdata/txt", package = "postvocs") sample_file <- system.file("extdata/SampleID.xlsx", package = "postvocs") # 1. From process_gcms_txt result (single sample) res <- process_gcms_txt(txt_file) result_single <- extract_peak_areas(res) # result_single is a list with one element named "sample1" # 2. From batch_process_gcms result (multiple samples) batch <- batch_process_gcms(txt_dir, sample_file) results_batch <- extract_peak_areas(batch) # results_batch is a list with elements named by mapped sample names # e.g., "Site1_TreatA_1", "Site1_TreatA_2", ... # 3. From a folder containing PeakTable and SearchResults CSV files # (This requires you to have saved the results from save_gcms_results first) # For example: save_gcms_results(res, tempdir(), format = "csv") results_folder <- extract_peak_areas(tempdir())# Get paths to example data txt_file <- system.file("extdata/txt/sample1.txt", package = "postvocs") txt_dir <- system.file("extdata/txt", package = "postvocs") sample_file <- system.file("extdata/SampleID.xlsx", package = "postvocs") # 1. From process_gcms_txt result (single sample) res <- process_gcms_txt(txt_file) result_single <- extract_peak_areas(res) # result_single is a list with one element named "sample1" # 2. From batch_process_gcms result (multiple samples) batch <- batch_process_gcms(txt_dir, sample_file) results_batch <- extract_peak_areas(batch) # results_batch is a list with elements named by mapped sample names # e.g., "Site1_TreatA_1", "Site1_TreatA_2", ... # 3. From a folder containing PeakTable and SearchResults CSV files # (This requires you to have saved the results from save_gcms_results first) # For example: save_gcms_results(res, tempdir(), format = "csv") results_folder <- extract_peak_areas(tempdir())
This function performs a two‑step filtering of compounds from an abundance matrix. It removes compounds detected in blank samples, then retains compounds based on occurrence frequency across all samples and within treatment groups. It returns a detailed summary table with flags for each compound.
filter_by_frequency( abundance_data, sample_group_file, sheet = 1, group_col = "Combined_Treatment", threshold_total = 0.25, threshold_treatment = 0.25, blank_indicators = c("Species", "Treatment", "Combined_Treatment") )filter_by_frequency( abundance_data, sample_group_file, sheet = 1, group_col = "Combined_Treatment", threshold_total = 0.25, threshold_treatment = 0.25, blank_indicators = c("Species", "Treatment", "Combined_Treatment") )
abundance_data |
Either a data.frame with a first column named "CAS" and
a second column named "Compound_Name", a character path to a CSV or Excel
file with the same structure, or a list returned by |
sample_group_file |
Either a data.frame or a character path to the sample grouping file (CSV or Excel). The first column must be "FileID" (ignored) and the second column must be "SampleName" (used to match abundance columns). |
sheet |
Integer or character. Sheet number or name to read from Excel 'sample_group_file'. Default is '1' (first sheet). Ignored if 'sample_group_file' is a data.frame or CSV file. |
group_col |
Character. Name of the column in 'sample_group_file' that defines treatment groups. Default is '"Combined_Treatment"'. |
threshold_total |
Numeric. Minimum fraction of total samples in which a compound must appear to pass the first filter. Default is '0.25' (25 percent). |
threshold_treatment |
Numeric. Minimum fraction of samples within a treatment group for the second filter. Default is '0.25' (25 percent). |
blank_indicators |
Character vector. Columns in 'sample_group_file' that must all equal "0" to identify blank samples. Default is 'c("Species", "Treatment", "Combined_Treatment")'. If some columns are not present, they are skipped with a warning. |
The function expects the abundance matrix to contain at least two columns: 'CAS' and 'Compound_Name'. The 'Compound_Name' column should have been added by 'annotate_compounds()'; if missing, a warning is issued and 'Compound_Name' is filled with 'NA'.
Blank samples (identified by 'blank_indicators' all equal "0") are used to remove contaminant compounds, but are excluded from frequency calculations. Compounds that appear (area > 0) in any blank sample are removed entirely and flagged in the summary table.
Frequency is calculated as the proportion of samples (or samples within a treatment group) where the compound area is strictly greater than zero.
The filtering proceeds in two rounds:
Compounds with total frequency >= 'threshold_total' are kept.
For compounds not kept in round 1, those with frequency >= 'threshold_treatment' in at least one treatment group are additionally kept.
A list with five components:
A data frame with filtering statistics.
The full abundance matrix with added frequency columns.
Data frame of compounds kept in round 1.
Data frame of compounds kept in round 2.
A comprehensive table for all original compounds with flags.
# Load pre-annotated example data (generated by the package maintainer) annotated <- readRDS(system.file("extdata/annotated_example.rds", package = "postvocs")) # Path to sample grouping file sample_file <- system.file("extdata/SampleID.xlsx", package = "postvocs") # Run frequency-based filtering result <- filter_by_frequency( abundance_data = annotated, sample_group_file = sample_file, group_col = "Combined_Factor", blank_indicators = c("Factor1", "Factor2", "Combined_Factor") ) # View summary result$summary# Load pre-annotated example data (generated by the package maintainer) annotated <- readRDS(system.file("extdata/annotated_example.rds", package = "postvocs")) # Path to sample grouping file sample_file <- system.file("extdata/SampleID.xlsx", package = "postvocs") # Run frequency-based filtering result <- filter_by_frequency( abundance_data = annotated, sample_group_file = sample_file, group_col = "Combined_Factor", blank_indicators = c("Factor1", "Factor2", "Combined_Factor") ) # View summary result$summary
Extracts the [MC Peak Table] and
[MS Similarity Search Results for Spectrum Process Table] from
a Shimadzu GC-MS workstation text export. The function returns the two
tables as data frames in a list, with element names derived from the input
file name. No files are written to disk.
process_gcms_txt(txt_file, debug = FALSE, encoding = "UTF-8")process_gcms_txt(txt_file, debug = FALSE, encoding = "UTF-8")
txt_file |
Character. Path to a single GC-MS exported TXT file
(e.g., |
debug |
Logical. If |
encoding |
Character. Encoding of the input TXT file. Default is
|
The function expects the TXT file to have the following structure:
A header line starting with "Data File Name" (used only for
debug and not for naming).
After [MC Peak Table], a line like # of Peaks\t38 or
# of Peaks,38 giving the number of peaks.
The column name line of the peak table must start with "Peak#"
(e.g., Peak#\tRet.Time\tArea... or Peak#,Ret.Time,Area...).
The mass spectral search results section starts with
[MS Similarity Search Results ...], and its column name line
must start with "Spectrum#".
The delimiter (tab or comma) is auto-detected from the column name line of each table and used for parsing all rows of that table. The two tables may use different delimiters.
A list with two data frames, named as
"<basename>_PeakTable" and "<basename>_SearchResults",
where <basename> is the input file name without extension.
PeakTable: The extracted peak table from
[MC Peak Table], containing columns like Peak#,
Ret.Time, Area, etc.
SearchResults: The extracted similarity search results
from [MS Similarity Search Results ...], containing
columns like Spectrum#, CAS#, Name, etc.
CAS numbers are cleaned by removing all spaces.
# Get path to the example data directory txt_dir <- system.file("extdata/txt", package = "postvocs") # Construct the full path to a sample TXT file txt_file <- file.path(txt_dir, "sample1.txt") # Process the file (default encoding UTF-8) res <- process_gcms_txt(txt_file, debug = TRUE) # If encountering encoding warnings, try GBK res <- process_gcms_txt(txt_file, encoding = "GBK") # Access the two tables using dynamically generated names peak_table <- res[["sample1_PeakTable"]] search_results <- res[["sample1_SearchResults"]] # See all names names(res)# Get path to the example data directory txt_dir <- system.file("extdata/txt", package = "postvocs") # Construct the full path to a sample TXT file txt_file <- file.path(txt_dir, "sample1.txt") # Process the file (default encoding UTF-8) res <- process_gcms_txt(txt_file, debug = TRUE) # If encountering encoding warnings, try GBK res <- process_gcms_txt(txt_file, encoding = "GBK") # Access the two tables using dynamically generated names peak_table <- res[["sample1_PeakTable"]] search_results <- res[["sample1_SearchResults"]] # See all names names(res)
Saves the data frames from process_gcms_txt() or
batch_process_gcms() to individual files. For SearchResults data
frames, the CAS column is prefixed with a single quote to prevent Excel
from interpreting CAS numbers as dates.
save_gcms_results(results, output_dir, format = c("csv", "xlsx"), ...)save_gcms_results(results, output_dir, format = c("csv", "xlsx"), ...)
results |
A list returned by |
output_dir |
Character. Path to the directory where files will be saved. Created if it does not exist. |
format |
Character. Output format: |
... |
Additional arguments passed to |
The function detects data frames that contain CAS-related columns (based on the data frame name containing "SearchResults") and adds a single quote prefix to the CAS column to avoid Excel date conversion. For PeakTable data frames, no modification is applied.
If format = "xlsx", the openxlsx package is required.
If not installed, the function will prompt to install it.
Invisibly, a character vector of saved file paths.
# Get paths to example data txt_dir <- system.file("extdata/txt", package = "postvocs") txt_file <- file.path(txt_dir, "sample1.txt") sample_file <- system.file("extdata/SampleID.xlsx", package = "postvocs") # (1) Process a single file and save as CSV res <- process_gcms_txt(txt_file) save_gcms_results(res, output_dir = tempdir(), format = "csv") # (2) Batch process and save as Excel (all in one file) batch <- batch_process_gcms(txt_dir, sample_file) save_gcms_results(batch, output_dir = tempdir(), format = "xlsx") # (3) Save only PeakTables (custom extraction) save_gcms_results(batch$PeakTables, output_dir = tempdir(), format = "csv")# Get paths to example data txt_dir <- system.file("extdata/txt", package = "postvocs") txt_file <- file.path(txt_dir, "sample1.txt") sample_file <- system.file("extdata/SampleID.xlsx", package = "postvocs") # (1) Process a single file and save as CSV res <- process_gcms_txt(txt_file) save_gcms_results(res, output_dir = tempdir(), format = "csv") # (2) Batch process and save as Excel (all in one file) batch <- batch_process_gcms(txt_dir, sample_file) save_gcms_results(batch, output_dir = tempdir(), format = "xlsx") # (3) Save only PeakTables (custom extraction) save_gcms_results(batch$PeakTables, output_dir = tempdir(), format = "csv")
This function saves the results from 'build_cas_abundance()', 'annotate_compounds()', or 'filter_by_frequency()' to CSV or Excel files. It automatically adds a single quote prefix to CAS columns to prevent Excel date conversion. It checks whether the CAS column already contains a leading single quote; if so, it does not add another one.
save_postvocs_results( x, output_dir, prefix = NULL, format = c("csv", "xlsx"), what = NULL, ... )save_postvocs_results( x, output_dir, prefix = NULL, format = c("csv", "xlsx"), what = NULL, ... )
x |
Either:
|
output_dir |
Character. Directory where files will be saved. Created if it does not exist. **Must be specified** (no default). |
prefix |
Character. Optional prefix for output file names. If 'NULL', a prefix is automatically derived from the input:
|
format |
Character. Output format: '"csv"' or '"xlsx"'. Default is '"csv"'. |
what |
Character vector. Which components to save when 'x' is a list. For 'annotate_compounds' results, can be '"annotation"' and/or '"abundance_updated"' (default both). For 'filter_by_frequency' results, can be any of '"summary"', '"abundance"', '"round1"', '"round2"', ‘"summary_table"' (default all except 'summary' because it’s just a small stats table). Set to '"all"' to save all components. |
... |
Additional arguments passed to 'write.csv' (if 'format = "csv"') or 'openxlsx::writeData' (if 'format = "xlsx"'). Common arguments include 'row.names = FALSE'. |
For Excel output ('format = "xlsx"'), if the input is a list containing multiple data frames (e.g., from 'filter_by_frequency()' or 'annotate_compounds()'), all tables are saved as separate worksheets in a single Excel file. For CSV output, each table is saved as an independent file.
For data.frames that contain a column named "CAS", the function adds a single quote prefix to each CAS value to prevent Excel from interpreting them as dates. It does not add a second quote if the value already starts with a single quote.
If 'format = "xlsx"', the 'openxlsx' package is required.
Invisibly, a character vector of saved file paths.
# Load pre-annotated example data annotated <- readRDS(system.file("extdata/annotated_example.rds", package = "postvocs")) # Path to sample grouping file sample_file <- system.file("extdata/SampleID.xlsx", package = "postvocs") # Perform frequency-based screening (if needed) result <- filter_by_frequency( abundance_data = annotated, sample_group_file = sample_file, group_col = "Combined_Factor", blank_indicators = c("Factor1", "Factor2", "Combined_Factor") ) # Save results save_postvocs_results(result, output_dir = tempdir(), format = "csv")# Load pre-annotated example data annotated <- readRDS(system.file("extdata/annotated_example.rds", package = "postvocs")) # Path to sample grouping file sample_file <- system.file("extdata/SampleID.xlsx", package = "postvocs") # Perform frequency-based screening (if needed) result <- filter_by_frequency( abundance_data = annotated, sample_group_file = sample_file, group_col = "Combined_Factor", blank_indicators = c("Factor1", "Factor2", "Combined_Factor") ) # Save results save_postvocs_results(result, output_dir = tempdir(), format = "csv")