From 2b389a6b48e862861f249e35d091ca1e18beae36 Mon Sep 17 00:00:00 2001 From: Md Aminul Islam Prodhan Date: Tue, 31 Dec 2024 12:25:27 -0600 Subject: [PATCH] updated --- ...del .R => get_14reprtree_from_rf_model .R} | 0 R/{get_2bw_score.R => get_bw_score.R} | 52 ++++-- R/{get_1compile_data.R => get_compile_data.R} | 30 ++-- R/{get_4lb_score.R => get_lb_score.R} | 48 ++++-- ....R => get_liver_om_lb_mi_tox_score_list.R} | 58 +++++-- ...ivertobw_score.R => get_livertobw_score.R} | 73 +++++--- R/{get_5mi_score.R => get_mi_score.R} | 43 +++-- R/get_prediction_plot.R | 160 ------------------ README.Rmd | 118 +++++++++---- README.md | 44 ----- inst/SENDQSAR_DOCUMENTATION.docx | Bin 0 -> 21177 bytes vignettes/get_bw_score.Rmd | 72 ++++++++ vignettes/get_compile_data.Rmd | 128 ++++++++++++++ vignettes/get_lb_score.Rmd | 72 ++++++++ .../get_liver_om_lb_mi_tox_score_list.Rmd | 113 +++++++++++++ vignettes/get_livertobw_score.Rmd | 109 ++++++++++++ vignettes/get_mi_score.Rmd | 97 +++++++++++ 17 files changed, 877 insertions(+), 340 deletions(-) rename R/{get_reprtree_from_rf_model .R => get_14reprtree_from_rf_model .R} (100%) rename R/{get_2bw_score.R => get_bw_score.R} (88%) rename R/{get_1compile_data.R => get_compile_data.R} (93%) rename R/{get_4lb_score.R => get_lb_score.R} (93%) rename R/{get_6liver_om_lb_mi_tox_score_list.R => get_liver_om_lb_mi_tox_score_list.R} (92%) rename R/{get_3livertobw_score.R => get_livertobw_score.R} (79%) rename R/{get_5mi_score.R => get_mi_score.R} (93%) delete mode 100644 R/get_prediction_plot.R delete mode 100644 README.md create mode 100644 inst/SENDQSAR_DOCUMENTATION.docx create mode 100644 vignettes/get_bw_score.Rmd create mode 100644 vignettes/get_compile_data.Rmd create mode 100644 vignettes/get_lb_score.Rmd create mode 100644 vignettes/get_liver_om_lb_mi_tox_score_list.Rmd create mode 100644 vignettes/get_livertobw_score.Rmd create mode 100644 vignettes/get_mi_score.Rmd diff --git a/R/get_reprtree_from_rf_model .R b/R/get_14reprtree_from_rf_model .R similarity index 100% rename from R/get_reprtree_from_rf_model .R rename to R/get_14reprtree_from_rf_model .R diff --git a/R/get_2bw_score.R b/R/get_bw_score.R similarity index 88% rename from R/get_2bw_score.R rename to R/get_bw_score.R index 9ea3141..9e39e20 100644 --- a/R/get_2bw_score.R +++ b/R/get_bw_score.R @@ -1,33 +1,49 @@ - -#' @title get BW score for a given studyid +#' @title Calculate BW Score for a Given Study ID +#' +#' @description +#' The `get_bw_score` function calculates the Bayesian Weighted (BW) score for a specified study ID using data from a provided database. +#' It supports optional parameters for fine-tuning the analysis and offers the flexibility to return individual scores or z-scores by `USUBJID`. +#' #' @param studyid Mandatory, character \cr -#' Studyid number +#' The study ID for which the BW score is to be calculated. If `NULL`, all studies in the database are analyzed. #' @param path_db Mandatory, character \cr -#' path of database -#' @param fake_study optional, Boolean \cr -#' whether study generated by SENDsanitizer package -#' @param use_xpt_file Mandatory, character \cr -#' Studyid number -#' @param master_compiledata Mandatory, character \cr -#' path of database -#' @param return_individual_scores optional, Boolean \cr -#' whether study generated by SENDsanitizer package -#' @param return_zscore_by_USUBJID optional, Boolean \cr -#' whether study generated by SENDsanitizer package -#' @return dataframe +#' The path to the database file containing the study data. +#' @param fake_study Optional, Boolean \cr +#' Indicates whether the study was generated by the `SENDsanitizer` package. Default is `FALSE`. +#' @param use_xpt_file Mandatory, Boolean \cr +#' If `TRUE`, the function uses `.xpt` files for processing the study data. Default is `FALSE`. +#' @param master_compiledata Optional, character \cr +#' The path to an additional database or compiled data file for analysis. If `NULL`, only the primary database is used. +#' @param return_individual_scores Optional, Boolean \cr +#' If `TRUE`, the function returns individual scores for each record in the study. Default is `FALSE`. +#' @param return_zscore_by_USUBJID Optional, Boolean \cr +#' If `TRUE`, the function returns z-scores calculated by `USUBJID` (unique subject identifiers). Default is `FALSE`. +#' +#' @return +#' A `data.frame` containing the calculated BW scores. The structure of the output depends on the provided parameters: +#' - If `return_individual_scores = TRUE`: Returns individual scores for each record. +#' - If `return_zscore_by_USUBJID = TRUE`: Returns z-scores by `USUBJID`. +#' - Otherwise, a summarized BW score for the specified `studyid`. #' #' @examples #' \dontrun{ -#' get_bw_score(studyid='1234123', path_db='path/to/database.db') +#' # Example 1: Basic usage +#' get_bw_score(studyid = '1234123', path_db = 'path/to/database.db') +#' +#' # Example 2: Include individual scores +#' get_bw_score(studyid = '1234123', path_db = 'path/to/database.db', return_individual_scores = TRUE) +#' +#' # Example 3: Include z-scores by USUBJID +#' get_bw_score(studyid = '1234123', path_db = 'path/to/database.db', return_zscore_by_USUBJID = TRUE) #' } +#' #' @export - - #' @importFrom RSQLite dbConnect #' @importFrom RSQLite SQLite + get_bw_score <- function(studyid = NULL, path_db, fake_study = FALSE, diff --git a/R/get_1compile_data.R b/R/get_compile_data.R similarity index 93% rename from R/get_1compile_data.R rename to R/get_compile_data.R index 4ee6604..ebd2e97 100644 --- a/R/get_1compile_data.R +++ b/R/get_compile_data.R @@ -1,22 +1,24 @@ -#' @title filter out tk and recovery animal -#' @param studyid Mandatory, character \cr -#' Studyid number -#' @param path_db Mandatory, character \cr -#' path of database -#' @param fake_study optional, Boolean \cr -#' whether study generated by SENDsanitizer package -##' @param use_xpt_file optional, Boolean \cr -#' whether study generated by SENDsanitizer package -#' @return dataframe +#' @title Retrieve Compiled Data from SQLite Database or XPT File +#' @description This function retrieves and compiles data for a given study ID +#' from either a SQLite database or XPT file. +#' @param studyid Character. Study ID number. Defaults to `NULL`. +#' If `NULL`, all available studies may be retrieved (behavior depends on the database structure). +#' @param path_db Character. Path to the SQLite database file. Mandatory. +#' @param fake_study Logical. Whether the study data is generated by the `SENDsanitizer` package. Defaults to `FALSE`. +#' @param use_xpt_file Logical. Whether to retrieve study data from an XPT file format instead of the database. Defaults to `FALSE`. +#' @return A data frame containing the compiled study data. The structure of the returned data frame depends on the database or XPT file contents. #' #' @examples #' \dontrun{ -#' get_compile_data(studyid='1234123', path_db='path/to/database.db') +#' # Retrieve data for a specific study ID from the database +#' get_compile_data(studyid = '1234123', path_db = 'path/to/database.db') +#' +#' # Retrieve data from an XPT file +#' get_compile_data(path_db = 'path/to/file.xpt', use_xpt_file = TRUE) #' } #' @export - - -#' @importFrom magrittr %>% +#' +#' @importFrom magrittr %> get_compile_data <- function(studyid = NULL, path_db, diff --git a/R/get_4lb_score.R b/R/get_lb_score.R similarity index 93% rename from R/get_4lb_score.R rename to R/get_lb_score.R index 50657a4..967e3c9 100644 --- a/R/get_4lb_score.R +++ b/R/get_lb_score.R @@ -1,25 +1,39 @@ - -#' @title get LB score for a given studyid -#' @param studyid Mandatory, character \cr -#' Studyid number -#' @param path_db Mandatory, character \cr -#' path of database -#' @param fake_study optional, Boolean \cr -#' whether study generated by SENDsanitizer package -#' @param use_xpt_file Mandatory, character \cr -#' Studyid number -#' @param master_compiledata Mandatory, character \cr -#' path of database -#' @param return_individual_scores optional, Boolean \cr -#' whether study generated by SENDsanitizer package -#' @param return_zscore_by_USUBJID optional, Boolean \cr -#' whether study generated by SENDsanitizer package -#' @return score +#' @title Get LB Score for a Given Study ID +#' +#' @description +#' This function computes the LB score for a given study ID using data stored in a specified database. +#' It offers various optional parameters to customize the output, such as whether to return individual scores or Z-scores by `USUBJID`. +#' +#' @param studyid Mandatory, character +#' The study ID number for which the LB score is calculated. +#' +#' @param path_db Mandatory, character +#' The path to the database containing the necessary data for the calculation. +#' +#' @param fake_study Optional, boolean +#' Indicates whether the study is generated by the SENDsanitizer package. Defaults to `FALSE`. +#' +#' @param use_xpt_file Mandatory, character +#' Specifies the path to the XPT (SAS transport) file if it is being used for the study. +#' +#' @param master_compiledata Mandatory, character +#' The path to the compiled master dataset that will be used to calculate the LB score. +#' +#' @param return_individual_scores Optional, boolean +#' If `TRUE`, the function will return individual scores for each subject. Defaults to `FALSE`. +#' +#' @param return_zscore_by_USUBJID Optional, boolean +#' If `TRUE`, the function will return Z-scores by `USUBJID`. Defaults to `FALSE`. +#' +#' @return numeric +#' The calculated LB score based on the provided data and parameters. #' #' @examples #' \dontrun{ +#' # Example usage of the function #' get_lb_score(studyid='1234123', path_db='path/to/database.db') #' } +#' #' @export get_lb_score <- function(studyid = NULL, diff --git a/R/get_6liver_om_lb_mi_tox_score_list.R b/R/get_liver_om_lb_mi_tox_score_list.R similarity index 92% rename from R/get_6liver_om_lb_mi_tox_score_list.R rename to R/get_liver_om_lb_mi_tox_score_list.R index deb5af5..b9aa559 100644 --- a/R/get_6liver_om_lb_mi_tox_score_list.R +++ b/R/get_liver_om_lb_mi_tox_score_list.R @@ -1,26 +1,48 @@ - - #' @title get_liver_om_lb_mi_tox_score_list -#' @param studyid_or_studyids Mandatory, character \cr -#' Studyid number -#' @param path_db Mandatory, character \cr -#' path of database -#' @param fake_study optional, Boolean \cr -#' whether study generated by SENDsanitizer package -#' @param use_xpt_file Mandatory, character \cr -#' path of database -#' @param multiple_xpt_folder Mandatory, character \cr -#' path of database -#' @param output_individual_scores optional, Boolean \cr -#' whether study generated by SENDsanitizer package -#' @param output_zscore_by_USUBJID optional, Boolean \cr -#' whether study generated by SENDsanitizer package -#' @return dataframe +#' +#' @description +#' This function processes liver organ toxicity scores, body weight z-scores, and other related metrics +#' for a set of studies or XPT files. It can output individual scores, z-scores by USUBJID, or averaged scores +#' for multiple studies, and handles errors during the processing steps. +#' +#' @param studyid_or_studyids A character vector or a single study ID to process. +#' If multiple studies are provided, the function processes each study sequentially. (Mandatory) +#' +#' @param path_db A character string specifying the path to the database or directory containing the data files. +#' (Mandatory) +#' +#' @param fake_study A boolean flag indicating if the study data is simulated (`TRUE`) or real (`FALSE`). Default is `FALSE`. (Optional) +#' +#' @param use_xpt_file A boolean flag indicating whether to use an XPT file for the study data. Default is `FALSE`. (Mandatory) +#' +#' @param multiple_xpt_folder A character string specifying the path to the folder containing multiple XPT files. +#' (Optional) +#' +#' @param output_individual_scores A boolean flag indicating whether individual scores should be returned (`TRUE`) or averaged scores (`FALSE`). Default is `FALSE`. (Optional) +#' +#' @param output_zscore_by_USUBJID A boolean flag indicating whether to output z-scores by `USUBJID` (`TRUE`) or averaged scores (`FALSE`). Default is `FALSE`. (Optional) +#' +#' @return A data frame containing the calculated scores for each study. The type of result depends on the flags passed: +#' - If `output_individual_scores` is `TRUE`, a data frame with individual scores for each study is returned. +#' - If `output_zscore_by_USUBJID` is `TRUE`, a data frame with z-scores by `USUBJID` for each study is returned. +#' - If neither flag is set, the function returns a data frame with averaged scores for each study. #' #' @examples #' \dontrun{ -#' get_compile_data(studyid='1234123', path_db='path/to/database.db') +#' # Get averaged scores for a single study +#' result <- get_liver_om_lb_mi_tox_score_list( +#' studyid_or_studyids = "Study_001", +#' path_db = "path/to/database" +#' ) +#' +#' # Get individual scores for multiple studies +#' result_individual_scores <- get_liver_om_lb_mi_tox_score_list( +#' studyid_or_studyids = c("Study_001", "Study_002"), +#' path_db = "path/to/database", +#' output_individual_scores = TRUE +#' ) #' } +#' #' @export diff --git a/R/get_3livertobw_score.R b/R/get_livertobw_score.R similarity index 79% rename from R/get_3livertobw_score.R rename to R/get_livertobw_score.R index 041c52b..5f52217 100644 --- a/R/get_3livertobw_score.R +++ b/R/get_livertobw_score.R @@ -1,33 +1,62 @@ -#' @title get_liver_livertobw_score -#' @param studyid Mandatory, character \cr -#' Studyid number -#' @param path_db Mandatory, character \cr -#' path of database -#' @param fake_study optional, Boolean \cr -#' whether study generated by SENDsanitizer package -#' @param use_xpt_file optional, Boolean \cr -#' whether use_xpt_file is used on not -#' @param master_compiledata optional, Boolean \cr -#' whether use_xpt_file is used on not -#' @param bwzscore_BW optional, Boolean \cr -#' whether use_xpt_file is used on not -#' @param return_individual_scores optional, logical \cr -#' whether use_xpt_file is used on not -#' @param return_zscore_by_USUBJID optional, logical \cr -#' whether use_xpt_file is used on not -#' @return dataframe +#' @title Calculate Liver-to-Body-Weight Scores and Z-Scores +#' +#' @description +#' This function computes liver-to-body-weight (Liver:BW) ratios and their corresponding z-scores from study data. +#' It supports retrieving data from SQLite databases or `.xpt` files and provides flexible options for output formats. +#' +#' @param studyid Optional, character. \cr +#' Study ID for which the calculations are performed. If `NULL`, data for all studies in the database is used. +#' @param path_db Mandatory, character. \cr +#' Path to the SQLite database or directory containing `.xpt` files. +#' @param fake_study Optional, logical. \cr +#' Indicates whether the study is a fake/test study generated by the `SENDsanitizer` package. Default is `FALSE`. +#' @param use_xpt_file Optional, logical. \cr +#' Specifies whether to use `.xpt` files instead of a SQLite database. Default is `FALSE`. +#' @param master_compiledata Optional, data.frame. \cr +#' Precompiled dataset of study information. If `NULL`, the function fetches the data using `get_compile_data`. +#' @param bwzscore_BW Optional, data.frame. \cr +#' Precomputed body weight z-scores. If `NULL`, they are calculated using `get_bw_score`. +#' @param return_individual_scores Optional, logical. \cr +#' If `TRUE`, returns individual z-scores averaged by study. Default is `FALSE`. +#' @param return_zscore_by_USUBJID Optional, logical. \cr +#' If `TRUE`, returns z-scores grouped by `USUBJID`. Default is `FALSE`. +#' +#' @return +#' A data frame containing liver-to-body-weight z-scores: +#' - Averaged by study (default). +#' - Individual scores averaged by study (`return_individual_scores = TRUE`). +#' - Z-scores grouped by `USUBJID` (`return_zscore_by_USUBJID = TRUE`). #' #' @examples #' \dontrun{ -#' get_compile_data(studyid='1234123', path_db='path/to/database.db') +#' # Example 1: Default averaged scores +#' result <- get_livertobw_score( +#' studyid = '1234123', +#' path_db = 'path/to/database.db' +#' ) +#' head(result) +#' +#' # Example 2: Individual scores by study +#' result <- get_livertobw_score( +#' studyid = '1234123', +#' path_db = 'path/to/database.db', +#' return_individual_scores = TRUE +#' ) +#' head(result) +#' +#' # Example 3: Z-scores by USUBJID +#' result <- get_livertobw_score( +#' studyid = '1234123', +#' path_db = 'path/to/database.db', +#' return_zscore_by_USUBJID = TRUE +#' ) +#' head(result) #' } +#' #' @export - - - get_livertobw_score <- function (studyid = NULL, path_db, fake_study = FALSE, diff --git a/R/get_5mi_score.R b/R/get_mi_score.R similarity index 93% rename from R/get_5mi_score.R rename to R/get_mi_score.R index 536ef65..29e25b4 100644 --- a/R/get_5mi_score.R +++ b/R/get_mi_score.R @@ -1,27 +1,40 @@ - -#' @title get MI score for a given studyid +#' @title Get MI score for a given studyid +#' +#' @description +#' This function calculates the MI score for a given study using the provided study ID and database. It allows flexibility in terms of returning individual scores, Z-scores, and more. The function is compatible with both SENDsanitizer-generated datasets and standard clinical study databases. +#' #' @param studyid Mandatory, character \cr -#' Studyid number +#' The study ID number for the clinical study. +#' #' @param path_db Mandatory, character \cr -#' path of database -#' @param fake_study optional, Boolean \cr -#' whether study generated by SENDsanitizer package -#' @param use_xpt_file Mandatory, character \cr -#' Studyid number +#' The file path to the database that contains the study data. +#' +#' @param fake_study Optional, logical \cr +#' If TRUE, the function assumes that the study data was generated by the SENDsanitizer package. Default is FALSE. +#' +#' @param use_xpt_file Mandatory, logical \cr +#' If TRUE, indicates that an XPT file should be used instead of a database for analysis. +#' #' @param master_compiledata Mandatory, character \cr -#' path of database -#' @param return_individual_scores optional, Boolean \cr -#' whether study generated by SENDsanitizer package -#' @param return_zscore_by_USUBJID optional, Boolean \cr -#' whether study generated by SENDsanitizer package -#' @return score +#' The path to the master compile data, often used to supplement or compile data from multiple sources. +#' +#' @param return_individual_scores Optional, logical \cr +#' If TRUE, the function returns individual MI scores for each participant. Default is FALSE. +#' +#' @param return_zscore_by_USUBJID Optional, logical \cr +#' If TRUE, the function returns the Z-scores by `USUBJID` (subject identifier). Default is FALSE. +#' +#' @return A numeric vector or data frame containing the MI scores. The format depends on the specified parameters, such as individual scores or aggregated scores. #' #' @examples #' \dontrun{ -#' get_mi_score(studyid='1234123', path_db='path/to/database.db') +#' # Example usage of get_mi_score +#' get_mi_score(studyid = '1234123', path_db = 'path/to/database.db') #' } +#' #' @export + get_mi_score <- function(studyid = NULL, path_db, fake_study=FALSE, diff --git a/R/get_prediction_plot.R b/R/get_prediction_plot.R deleted file mode 100644 index 3ed7aa8..0000000 --- a/R/get_prediction_plot.R +++ /dev/null @@ -1,160 +0,0 @@ -get_prediction_plot <- function(Data=NULL, - path_db, - rat_studies=FALSE, - studyid_metadata=NULL, - fake_study = FALSE, - use_xpt_file = FALSE, - Round = FALSE, - Impute = FALSE, - reps, - holdback, - Undersample = FALSE, - hyperparameter_tuning = FALSE, - error_correction_method, - testReps){ - - - if(is.null(Data)){ - data_and_best.m <- get_Data_formatted_for_ml_and_best.m(path_db=path_db, - rat_studies=rat_studies, - studyid_metadata=studyid_metadata, - fake_study = fake_study, - use_xpt_file = use_xpt_file, - Round = Round, - Impute = Impute, - reps=reps, - holdback=holdback, - Undersample = Undersample, - hyperparameter_tuning = hyperparameter_tuning, - error_correction_method=error_correction_method) # = must be 'Flip' or "Prune' or 'None' - - } - - Data <- data_and_best.m[["Data"]] - best.m <- data_and_best.m[["best.m"]] - - - - - rfData <- Data - #--------------------------------------------------------------------- - # Initialize model performance metric trackers------------------------ - #--------------------------------------------------------------------- - - # custom function definition - `%ni%` <- Negate('%in%') - - Sensitivity <- NULL - Specificity <- NULL - PPV <- NULL - NPV <- NULL - Prevalence <- NULL - Accuracy <- NULL - #nRemoved <- NULL - - - #-----------------doing cross-validation-------------------------- - #----------------------------------------------------------------- - #------------------------------------------------------------------ - - #-----create and prepare "`rfTestData data` frame" for storing predictions---- - rfTestData <- rfData - - #replaces the existing column names with simple numeric identifiers - colnames(rfTestData) <- seq(ncol(rfTestData)) - - #emptying the data frame. - for (j in seq(ncol(rfTestData))) { - rfTestData[,j] <- NA - } - - #prepares rfTestData to maintain a consistent structure with the necessary - #columns for storing predictions in subsequent iterations of the loop - rfTestData <- rfTestData[,1:2] # Keep structure for predictions - - #remove 'gini' from the previous iteration - #if (exists('gini')) {rm(gini)} - - - #------------------------------------------------------------------- - # model building and testing---------------------------------------- - #------------------------------------------------------------------- - - - # Iterate through test repetitions---------------------------------- - for (i in seq(testReps)) { - if (i == 1) { - sampleIndicies <- seq(nrow(rfData)) - } - if (i < testReps) { - ind <- sample(seq(nrow(rfData)), floor((nrow(rfData)/testReps)-1), replace = F) - sampleIndicies <- sampleIndicies[which(sampleIndicies %ni% ind)] - } else { - ind <- sampleIndicies - } - - trainIndex <- which(seq(nrow(rfData)) %ni% ind) - testIndex <- ind - - # ind <- sample(2, nrow(rfData), replace = T, prob = c((1- testHoldBack), testHoldBack)) - train <- rfData[trainIndex,] - - #train_data_two <- train - - test <- rfData[testIndex,] - - # rfAll <- randomForest::randomForest(Target_Organ ~ ., data=rfData, mytry = best.m, - # importance = F, ntree = 500, proximity = T) - - - # Perform under sampling if enabled - if (Undersample == T) { - posIndex <- which(train[,1] == 1) - nPos <- length(posIndex) - # trainIndex <- c(posIndex, sample(which(train[,1] == 0), nPos, replace = F)) - trainIndex <- c(posIndex, sample(which(train[,1] == 0), nPos, replace = T)) - train <- train[trainIndex,] - test <- rbind(train[-trainIndex,], test) - } - - #train_data_two <- train - - - #model building with current iteration train data - # Train Random Forest model-------------------------------------------- - rf <- randomForest::randomForest(Target_Organ ~ ., data=train, mytry = best.m, - importance = T, ntree = 500, proximity = T) - - print(rf) - - #---------------------------------------------------------------------- - #predictions with current model with current test data - # @___________________this_line_has_problems_______ - # Predict probabilities on test data - #---------------------------------------------------------------------- - - p2r <- stats::predict(rf, test, type = 'prob')[,1] - - #Store these predictions in a structured data frame - rfTestData[names(p2r), i] <- as.numeric(p2r) - - #Rounding the Predictions: - p2r <- round(p2r) - } - - - #------------------------------------------------------- - histoData <- as.data.frame(cbind(rowMeans(rfTestData, na.rm = T), rfData[,1])) - histoData[which(histoData[,2] == 1), 2] <- 'Y' - histoData[which(histoData[,2] == 2), 2] <- 'N' - colnames(histoData) <- c('Probability', 'LIVER') - - H <- p <- histoData %>% - ggplot2::ggplot( ggplot2::aes(x=Probability, fill=LIVER)) + - ggplot2::geom_histogram( color="#e9ecef", alpha=0.6, position = 'identity') + - ggplot2::scale_fill_manual(values=c("#69b3a2", "#404080")) + - ggplot2::labs(fill = "LIVER", x = "Model Prediction P(LIVER)", y = "Count") - -print(H) - - } diff --git a/README.Rmd b/README.Rmd index 5a60d9a..3299e88 100644 --- a/README.Rmd +++ b/README.Rmd @@ -2,56 +2,110 @@ title: "SENDQSAR" output: github_document --- -SENDQSAR is an R package to generateQSAR model from SEND data... - - -```{r, include = FALSE} -knitr::opts_chunk$set( - collapse = TRUE, - comment = "#>", - fig.path = "man/figures/README-", - out.width = "100%" -) -``` -# Overview +# SENDQSAR: QSAR Modeling with SEND Database - - - +## About -The goal of SENDQSAR is to ... +This package facilitates developing Quantitative Structure-Activity Relationship (QSAR) models using the SEND database. It streamlines data acquisition, preprocessing, descriptor calculation, and model evaluation, enabling researchers to efficiently explore molecular descriptors and create robust predictive models. -## Installation +## Features + +- **Automated Data Processing**: Simplifies data acquisition and preprocessing steps. +- **Comprehensive Analysis**: Provides z-score calculations for various parameters such as body weight, liver-to-body weight ratio, and laboratory tests. +- **Machine Learning Integration**: Supports classification modeling, hyperparameter tuning, and performance evaluation. +- **Visualization Tools**: Includes histograms, bar plots, and AUC curves for better data interpretation. + +## Functions Overview + +### Data Acquisition and Processing + +- `get_compile_data` - Fetches data from the database specified by the database path into a structured data frame for analysis. +- `get_bw_score` - Calculates body weight (BW) z-scores for each animal. +- `get_livertobw_zscore` - Computes liver-to-body weight z-scores. +- `get_lb_score` - Calculates z-scores for laboratory test (LB) results. +- `get_mi_score` - Computes z-scores for microscopic findings (MI). +- `get_liver_om_lb_mi_tox_score_list` - Combines z-scores of LB, MI, and liver-to-BW into a single data frame. +- `get_col_harmonized_scores_df` - Harmonizes column names across studies. + +### Machine Learning Preparation and Modeling + +- `get_ml_data_and_tuned_hyperparameters` - Prepares data and tunes hyperparameters for machine learning. +- `get_rf_model_with_cv` - Builds a random forest model with cross-validation and outputs performance metrics. +- `get_zone_exclusioned_rf_model_with_cv` - Introduces an indeterminate zone for improved classification accuracy. +- `get_imp_features_from_rf_model_with_cv` - Computes feature importance for model interpretation. +- `get_auc_curve_with_rf_model` - Generates AUC curves to evaluate model performance. + +### Visualization and Reporting + +- `get_histogram_barplot` - Creates bar plots for target variable classes. +- `get_reprtree_from_rf_model` - Builds representative decision trees for interpretability. +- `get_prediction_plot` - Visualizes prediction probabilities with histograms. + +### Automated Pipelines + +- `get_Data_formatted_for_ml_and_best.m` - Formats data for machine learning pipelines. +- `get_rf_input_param_list_output_cv_imp` - Automates preprocessing, modeling, and evaluation in one step. +- `get_zone_exclusioned_rf_model_cv_imp` - Similar to the above function, but excludes uncertain predictions based on thresholds. -You can install the development version of SENDQSAR from [GitHub](https://github.com/) with: +## Workflow -``` r -# install.packages("devtools") +1. **Input Database Path**: Provide the database path containing nonclinical study results for each STUDYID. +2. **Preprocessing**: Use functions 1-8 to clean, harmonize, and prepare data. +3. **Model Building**: Employ machine learning functions (9-18) for training, validation, and evaluation. +4. **Visualization**: Generate plots and performance metrics for better interpretation. + +## Dependencies + +- `randomForest` +- `ROCR` +- `ggplot2` +- `reprtree` + +## Installation + +```R +# Install from GitHub devtools::install_github("aminuldu07/SENDQSAR") ``` -## Example +## Examples -need to fill up the descriptons here : +### Example 1: Basic Data Compilation -```{r example} +```R library(SENDQSAR) -## basic example code +data <- get_compile_data("/path/to/database") ``` -What is special about using : +### Example 2: Z-Score Calculation -```{r cars} -#summary(cars) +```R +bw_scores <- get_bw_score(data) +liver_scores <- get_livertobw_zscore(data) ``` - skjdkdfkd . +### Example 3: Machine Learning Model + +```R +model <- get_rf_model_with_cv(data, n_repeats=10) +print(model$confusion_matrix) +``` -You can also embed plots, for example: +### Example 4: Visualization -```{r pressure, echo = FALSE} -#plot(pressure) +```R +get_histogram_barplot(data, target_col="target_variable") ``` -In that case, don't forget to commit and push the resulting figure files, so they display on GitHub and CRAN. +## Contribution + +Contributions are welcome! Feel free to submit issues or pull requests via GitHub. + +## License + +This project is licensed under the MIT License - see the LICENSE file for details. + +## Contact + +For more information, visit the project GitHub Page or contact email@example.com. diff --git a/README.md b/README.md deleted file mode 100644 index 655fb7c..0000000 --- a/README.md +++ /dev/null @@ -1,44 +0,0 @@ -SENDQSAR -================ - -SENDQSAR is an R package to generateQSAR model from SEND data… - - -# Overview - - - - - -The goal of SENDQSAR is to facilitates developing Quantitative Structure-Activity Relationship (QSAR) models using the SEND database. -It streamlines data acquisition, preprocessing, descriptor calculation, and model evaluation, enabling researchers to efficiently -explore molecular descriptors and create robust predictive models. - -## Installation - -You can install the development version of SENDQSAR from -[GitHub](https://github.com/) with: - -``` r -# install.packages("devtools") -devtools::install_github("aminuldu07/SENDQSAR") -``` - -## Example - -need to fill up the descriptions here : - -``` r -library(SENDQSAR) -## basic example code -``` - -What is special about using : - -``` r -#summary(cars) -``` - - - - diff --git a/inst/SENDQSAR_DOCUMENTATION.docx b/inst/SENDQSAR_DOCUMENTATION.docx new file mode 100644 index 0000000000000000000000000000000000000000..820cfefe54c0f2810fd7aabead9df51f176a1f6a GIT binary patch literal 21177 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