new working version with major updates

This commit is contained in:
Andreas Gammelgaard Damsbo 2024-12-17 11:30:17 +01:00
parent fb2569c647
commit e4633421aa
No known key found for this signature in database
16 changed files with 1662 additions and 69 deletions

View file

@ -43,7 +43,6 @@ Imports:
datamods,
toastui,
webshot,
matPkg,
lubridate
Suggests:
styler,

View file

@ -1,17 +1,23 @@
# Generated by roxygen2: do not edit by hand
S3method(cut,hms)
S3method(regression_table,default)
S3method(regression_table,list)
export(argsstring2list)
export(baseline_table)
export(cut_variable_server)
export(cut_variable_ui)
export(default_format_arguments)
export(factorize)
export(format_writer)
export(getfun)
export(index_embed)
export(is_any_class)
export(is_datetime)
export(m_datafileUI)
export(m_redcap_readServer)
export(m_redcap_readUI)
export(modal_cut_variable)
export(modify_qmd)
export(read_input)
export(regression_model)
@ -20,5 +26,45 @@ export(regression_table)
export(shiny_webResearch)
export(specify_qmd_format)
export(tbl_merge)
export(winbox_cut_variable)
export(write_quarto)
importFrom(classInt,classIntervals)
importFrom(graphics,abline)
importFrom(graphics,axis)
importFrom(graphics,hist)
importFrom(graphics,par)
importFrom(graphics,plot.new)
importFrom(graphics,plot.window)
importFrom(htmltools,tagList)
importFrom(rlang,"%||%")
importFrom(rlang,call2)
importFrom(rlang,expr)
importFrom(rlang,set_names)
importFrom(rlang,syms)
importFrom(shiny,NS)
importFrom(shiny,bindEvent)
importFrom(shiny,checkboxInput)
importFrom(shiny,column)
importFrom(shiny,fluidRow)
importFrom(shiny,modalDialog)
importFrom(shiny,moduleServer)
importFrom(shiny,numericInput)
importFrom(shiny,observeEvent)
importFrom(shiny,plotOutput)
importFrom(shiny,reactive)
importFrom(shiny,renderPlot)
importFrom(shiny,req)
importFrom(shiny,showModal)
importFrom(shiny,textInput)
importFrom(shiny,uiOutput)
importFrom(shinyWidgets,WinBox)
importFrom(shinyWidgets,noUiSliderInput)
importFrom(shinyWidgets,updateVirtualSelect)
importFrom(shinyWidgets,virtualSelectInput)
importFrom(shinyWidgets,wbControls)
importFrom(shinyWidgets,wbOptions)
importFrom(stats,as.formula)
importFrom(toastui,datagrid)
importFrom(toastui,datagridOutput2)
importFrom(toastui,grid_colorbar)
importFrom(toastui,renderDatagrid2)

View file

@ -90,7 +90,7 @@ library(shiny)
#'
#' @rdname cut
#'
#' @return
#' @return factor
#' @export
#'
#' @examples
@ -114,7 +114,11 @@ cut.hms <- function(x, breaks, ...) {
#' @rdname cut
#' @param x an object inheriting from class "POSIXt" or "Date"
cut.POSIXt <- function(x, breaks, right = FALSE, include.lowest = TRUE, ...) {
#'
#' @examples
#' readr::parse_datetime(c("1992-02-01 01:00:20", "1992-02-06 03:00:20", "1992-05-01 01:20:20", "1992-09-01 08:20:20", "1999-02-01 21:20:20", "1992-12-01 03:02:20")) |> cut(2)
#' readr::parse_datetime(c("1992-02-01 01:00:20", "1992-02-06 03:00:20", "1992-05-01 01:20:20", "1992-09-01 08:20:20", "1999-02-01 21:20:20", "1992-12-01 03:02:20")) |> cut(breaks="weekday")
cut.POSIXt <- function(x, breaks, right = FALSE, include.lowest = TRUE, start.on.monday=TRUE, ...) {
breaks_o <- breaks
# browser()
if (is.numeric(breaks)) {
@ -127,14 +131,22 @@ cut.POSIXt <- function(x, breaks, right = FALSE, include.lowest = TRUE, ...) {
)
}
if(identical(breaks,"weekday")){
days <- c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
"Sunday")
if (!start.on.monday){
days <- days[c(7,1:6)]
}
out <- factor(weekdays(x),levels=days) |> forcats::fct_drop()
} else {
## Doesn't really work very well for breaks other than the special character cases as right border is excluded
out <- base::cut.POSIXt(x, breaks=breaks,right=right,...) |> forcats::fct_drop()
# browser()
}
l <- levels(out)
if (is.numeric(breaks_o)) {
l <- breaks
} else if (is.character(breaks) && length(breaks) == 1) {
} else if (is.character(breaks) && length(breaks) == 1 && !identical(breaks,"weekday")) {
if (include.lowest) {
if (right) {
l <- c(l, min(as.character(x)))
@ -154,12 +166,34 @@ cut.POSIXt <- function(x, breaks, right = FALSE, include.lowest = TRUE, ...) {
#' @param x an object inheriting from class "POSIXct"
cut.POSIXct <- cut.POSIXt
#' @rdname cut
#' @param x an object inheriting from class "POSIXct"
#'
#' @examples
#' as.Date(c("1992-02-01 01:00:20", "1992-02-06 03:00:20", "1992-05-01 01:20:20", "1992-09-01 08:20:20", "1999-02-01 21:20:20", "1992-12-01 03:02:20")) |> cut(2)
#' as.Date(c("1992-02-01 01:00:20", "1992-02-06 03:00:20", "1992-05-01 01:20:20", "1992-09-01 08:20:20", "1999-02-01 21:20:20", "1992-12-01 03:02:20")) |> cut(breaks="weekday")
cut.Date <- function(x,breaks,start.on.monday=TRUE,...){
if(identical(breaks,"weekday")){
days <- c("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday",
"Sunday")
if (!start.on.monday){
days <- days[c(7,1:6)]
}
out <- factor(weekdays(x),levels=days) |> forcats::fct_drop()
} else {
## Doesn't really work very well for breaks other than the special character cases as right border is excluded
out <- base::cut.Date(x, breaks=breaks,...) |> forcats::fct_drop()
# browser()
}
out
}
#' Test class
#'
#' @param data data
#' @param class.vec vector of class names to test
#'
#' @return
#' @return factor
#' @export
#'
#' @examples
@ -174,7 +208,7 @@ is_any_class <- function(data, class.vec) {
#'
#' @param data data
#'
#' @return
#' @return factor
#' @export
#'
#' @examples
@ -200,7 +234,6 @@ is_datetime <- function(data) {
#'
#' @name cut-variable
#'
#' @example examples/cut_variable.R
cut_variable_ui <- function(id) {
ns <- NS(id)
tagList(
@ -343,7 +376,9 @@ cut_variable_server <- function(id, data_r = reactive(NULL)) {
choices <- c(choices, "hour")
} else if (any(c("POSIXt","Date") %in% class(data[[variable]]))) {
choices <- c(
choices, "day",
choices,
"day",
"weekday",
"week",
"month",
"quarter",
@ -412,6 +447,7 @@ cut_variable_server <- function(id, data_r = reactive(NULL)) {
}
} else if (input$method %in% c(
"day",
"weekday",
"week",
"month",
"quarter",
@ -447,7 +483,7 @@ cut_variable_server <- function(id, data_r = reactive(NULL)) {
variable <- req(input$variable)
data[[paste0(variable, "_cut")]] <- cut(
x = data[[variable]],
breaks = if (input$method %in% c("day", "week", "month", "quarter", "year", "hour")) input$method else breaks_r()$brks,
breaks = if (input$method %in% c("day", "weekday", "week", "month", "quarter", "year", "hour")) input$method else breaks_r()$brks,
include.lowest = input$include_lowest,
right = input$right
)

View file

@ -31,15 +31,19 @@ library(IDEAFilter)
# }
# library(webResearch)
if (file.exists(here::here("functions.R"))) {
source(here::here("functions.R"))
}
source(here::here("functions.R"))
server <- function(input, output, session) {
## Listing files in www in session start to keep when ending and removing
## everything else.
files.to.keep <- list.files("www/")
output$docs_file <- renderUI({
# shiny::includeHTML("www/docs.html")
HTML(readLines("www/docs.html"))
})
rv <- shiny::reactiveValues(
list = NULL,
ds = NULL,
@ -193,7 +197,7 @@ server <- function(input, output, session) {
)
output$original_str <- renderPrint({
str(ds())
str(rv$data_original)
})
output$modified_str <- renderPrint({
@ -290,25 +294,25 @@ server <- function(input, output, session) {
## Have a look at column filters at some point
## There should be a way to use the filtering the filter data for further analyses
## Disabled for now, as the JS is apparently not isolated
output$data_table <-
DT::renderDT(
{
DT::datatable(ds()[base_vars()])
},
server = FALSE
)
output$data.classes <- gt::render_gt({
shiny::req(input$file)
data.frame(matrix(sapply(ds(), \(.x){
class(.x)[1]
}), nrow = 1)) |>
stats::setNames(names(ds())) |>
gt::gt()
})
# output$data_table <-
# DT::renderDT(
# {
# DT::datatable(ds()[base_vars()])
# },
# server = FALSE
# )
#
# output$data.classes <- gt::render_gt({
# shiny::req(input$file)
# data.frame(matrix(sapply(ds(), \(.x){
# class(.x)[1]
# }), nrow = 1)) |>
# stats::setNames(names(ds())) |>
# gt::gt()
# })
shiny::observeEvent(input$act_start, {
bslib::nav_select(id = "main_panel", selected = "Overview and modifications")
bslib::nav_select(id = "main_panel", selected = "Modifications")
})
shiny::observeEvent(

View file

@ -30,11 +30,16 @@ ui_elements <- list(
shinyWidgets::radioGroupButtons(
inputId = "source",
# label = "Choice: ",
choices = c("File upload" = "file", "REDCap server" = "redcap","Sample data"="env"),
checkIcon = list(
yes = icon("square-check"),
no = icon("square")
)
choices = c(
"File upload" = "file",
"REDCap server" = "redcap",
"Sample data" = "env"
),
# checkIcon = list(
# yes = icon("square-check"),
# no = icon("square")
# ),
width = "100%"
),
shiny::conditionalPanel(
condition = "input.source=='file'",
@ -57,13 +62,7 @@ ui_elements <- list(
),
column(
width = 6,
shiny::markdown("
# Welcome
This is the ***freesearchR*** web data analysis tool. An opiniotaed tool for easy data analysis at the hands of the clinician.
By intention, this is a focused app, with only few data modification tools included to keep the workflow streamlined.
")
shiny::markdown(readLines("www/intro.md"))
)
),
shiny::conditionalPanel(
@ -82,7 +81,7 @@ ui_elements <- list(
# bslib::nav_panel_hidden(
bslib::nav_panel(
# value = "overview",
title = "Overview and modifications",
title = "Modifications",
bslib::navset_bar(
fillable = TRUE,
# bslib::nav_panel(
@ -260,7 +259,8 @@ ui_elements <- list(
##############################################################################
"docs" = bslib::nav_panel(
title = "Documentation",
shiny::markdown(readLines(here::here("inst/apps/data_analysis_modules/www/intro.md"))),
# shiny::tags$iframe("www/docs.html", height=600, width=535),
shiny::htmlOutput("docs_file"),
shiny::br()
)
)

View file

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
<meta charset="utf-8">
<meta name="generator" content="quarto-1.5.57">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>freesearcheR platform documentation</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
width: 0.8em;
margin: 0 0.8em 0.2em -1em; vertical-align: middle;
}
.display.math{display: block; text-align: center; margin: 0.5rem auto;}
</style>
</head>
<body>
<header id="title-block-header">
<h1 class="title"><strong><em>freesearcheR</em></strong> platform documentation</h1>
</header>
<h1 id="welcome">Welcome!</h1>
<p>So glad to see you here! Welcome to test this early concept of a platform to easily explore, manipulate and analyse clinical data.</p>
<p>Below will be a more detailed description of the included features and possibilities, as well as the planned and possible feature additions.</p>
<h2 id="contribute">Contribute</h2>
<p>Contributions are very welcome. If you find anything odd, or you think of features to add or remove, please <a href="https://github.com/agdamsbo/webResearch/issues">share and report on the project page on GitHub</a>.</p>
<h2 id="roadmap">Roadmap</h2>
<ul>
<li><p><label><input type="checkbox">Stratified analyses</label></p></li>
<li><p>Additional study designs:</p>
<ul class="task-list">
<li><p><label><input type="checkbox" checked>Cross-sectional data analyses</label></p></li>
<li><p><label><input type="checkbox">Longitudinal data analyses</label></p></li>
<li><p><label><input type="checkbox">Survival analysis</label></p></li>
</ul></li>
<li><p>More detailed variable browser</p>
<ul class="task-list">
<li><p><label><input type="checkbox">Add histograms for data distribution</label></p></li>
<li><p><label><input type="checkbox">Option to edit variable labels for nicer tables</label></p></li>
</ul></li>
<li><p><label><input type="checkbox">Plot regression analyses results</label></p></li>
<li><p><label><input type="checkbox">Export modified data</label></p></li>
<li><p><label><input type="checkbox">Include reproducible code for all steps</label></p></li>
<li><p><label><input type="checkbox">Modify factor levels (including naming, order, collapsing, removing)</label></p></li>
<li><p><label><input type="checkbox">More options for date/datetime/time grouping/factoring</label></p></li>
</ul>
</body></html>

View file

@ -0,0 +1,34 @@
# Documentation on the freesearcheR platform
Welcome! So glad to see you here! Welcome to test this early concept of a platform to easily explore, manipulate and analyse clinical data.
Below will be a more detailed description of the included features and possibilities, as well as the planned and possible feature additions.
## Roadmap
- [ ] Stratified analyses
- Additional study designs:
- [x] Cross-sectional data analyses
- [ ] Longitudinal data analyses
- [ ] Survival analysis
- More detailed variable browser
- [ ] Add histograms for datadistribution
- [ ] Option to edit labels
- [ ] Plot regression analyses results
- [ ] Export modified data
- [ ] Include reproducible code for all steps
- [ ] Modify factor levels
- [ ] More options for date/datetime/time grouping/factoring

View file

@ -0,0 +1,45 @@
---
title: "***freesearcheR*** platform documentation"
format:
html:
self-contained: true
minimal: true
---
# Welcome!
So glad to see you here! Welcome to test this early concept of a platform to easily explore, manipulate and analyse clinical data.
Below will be a more detailed description of the included features and possibilities, as well as the planned and possible feature additions.
## Contribute
Contributions are very welcome. If you find anything odd, or you think of features to add or remove, please [share and report on the project page on GitHub](https://github.com/agdamsbo/webResearch/issues).
## Roadmap
- [ ] Stratified analyses
- Additional study designs:
- [x] Cross-sectional data analyses
- [ ] Longitudinal data analyses
- [ ] Survival analysis
- More detailed variable browser
- [ ] Add histograms for data distribution
- [ ] Option to edit variable labels for nicer tables
- [ ] Plot regression analyses results
- [ ] Export modified data
- [ ] Include reproducible code for all steps
- [ ] Modify factor levels (including naming, order, collapsing, removing)
- [ ] More options for date/datetime/time grouping/factoring

View file

@ -1,3 +1,21 @@
# Intro to webResearch/freesearcheR/VOICE
# Welcome
This is just placeholder text.
This is the ***freesearchR*** web data analysis tool. The ***freesearchR*** is an opinioated tool for easy data evaluation and analysis at the hands of the clinician. We intend it to be a powerful to, that is easy and secure to use.
By intention, this tool has been designed to be simple to use with a minimum of mandatory options to keep the workflow streamlined, while also including a few options to go even further.
There are four simple steps to go through:
1. Import data (this can be a spreadsheet on your machine or direct export from a REDCap server)
2. A *optional* step of data modification (change variable classes and creating categorical variables (factors) from numeric or time data)
3. Data analysis of cross-sectionally designed studies
- Classic baseline charactieristics (options to stratify and compare variables)
- Linear, dichotomous or ordinal logistic regression will be used depending on specified outcome variable
- Evaluation of model assumptions
4. Export the the analyses results as .docx or .odt.

60
man/cut-variable.Rd Normal file
View file

@ -0,0 +1,60 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/cut-variable-dates.R
\name{cut-variable}
\alias{cut-variable}
\alias{cut_variable_ui}
\alias{cut_variable_server}
\alias{modal_cut_variable}
\alias{winbox_cut_variable}
\title{Module to Convert Numeric to Factor}
\usage{
cut_variable_ui(id)
cut_variable_server(id, data_r = reactive(NULL))
modal_cut_variable(
id,
title = i18n("Convert Numeric to Factor"),
easyClose = TRUE,
size = "l",
footer = NULL
)
winbox_cut_variable(
id,
title = i18n("Convert Numeric to Factor"),
options = shinyWidgets::wbOptions(),
controls = shinyWidgets::wbControls()
)
}
\arguments{
\item{id}{Module ID.}
\item{data_r}{A \code{\link[shiny:reactive]{shiny::reactive()}} function returning a \code{data.frame}.}
\item{title}{An optional title for the dialog.}
\item{easyClose}{If \code{TRUE}, the modal dialog can be dismissed by
clicking outside the dialog box, or be pressing the Escape key. If
\code{FALSE} (the default), the modal dialog can't be dismissed in those
ways; instead it must be dismissed by clicking on a \code{modalButton()}, or
from a call to \code{\link[shiny:removeModal]{removeModal()}} on the server.}
\item{size}{One of \code{"s"} for small, \code{"m"} (the default) for medium,
\code{"l"} for large, or \code{"xl"} for extra large. Note that \code{"xl"} only
works with Bootstrap 4 and above (to opt-in to Bootstrap 4+,
pass \code{\link[bslib:bs_theme]{bslib::bs_theme()}} to the \code{theme} argument of a page container
like \code{\link[shiny:fluidPage]{fluidPage()}}).}
\item{footer}{UI for footer. Use \code{NULL} for no footer.}
\item{options}{List of options, see \code{\link[shinyWidgets:wbOptions]{wbOptions()}}.}
\item{controls}{List of controls, see \code{\link[shinyWidgets:wbControls]{wbControls()}}.}
}
\value{
A \code{\link[shiny:reactive]{shiny::reactive()}} function returning the data.
}
\description{
This module contain an interface to cut a numeric into several intervals.
}

55
man/cut.Rd Normal file
View file

@ -0,0 +1,55 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/cut-variable-dates.R
\name{cut.hms}
\alias{cut.hms}
\alias{cut.POSIXt}
\alias{cut.POSIXct}
\alias{cut.Date}
\title{Title}
\usage{
\method{cut}{hms}(x, breaks, ...)
\method{cut}{POSIXt}(
x,
breaks,
right = FALSE,
include.lowest = TRUE,
start.on.monday = TRUE,
...
)
\method{cut}{POSIXct}(
x,
breaks,
right = FALSE,
include.lowest = TRUE,
start.on.monday = TRUE,
...
)
\method{cut}{Date}(x, breaks, start.on.monday = TRUE, ...)
}
\arguments{
\item{x}{an object inheriting from class "POSIXct"}
\item{...}{passed on}
}
\value{
factor
}
\description{
Title
}
\examples{
readr::parse_time(c("01:00:20", "03:00:20", "01:20:20", "08:20:20", "21:20:20", "03:02:20")) |> cut(2)
readr::parse_time(c("01:00:20", "03:00:20", "01:20:20", "08:20:20", "21:20:20", "03:02:20")) |> cut("min")
readr::parse_time(c("01:00:20", "03:00:20", "01:20:20", "08:20:20", "21:20:20", "03:02:20")) |> cut(breaks = "hour")
readr::parse_time(c("01:00:20", "03:00:20", "01:20:20", "08:20:20", "21:20:20", "03:02:20")) |> cut(breaks = hms::as_hms(c("01:00:00", "03:01:20", "9:20:20")))
d_t <- readr::parse_time(c("01:00:20", "03:00:20", "01:20:20", "03:02:20", NA))
f <- d_t |> cut(2)
readr::parse_time(c("01:00:20", "03:00:20", "01:20:20", "03:02:20", NA)) |> cut(breaks = lubridate::as_datetime(c(hms::as_hms(levels(f)), hms::as_hms(max(d_t, na.rm = TRUE) + 1))), right = FALSE)
readr::parse_datetime(c("1992-02-01 01:00:20", "1992-02-06 03:00:20", "1992-05-01 01:20:20", "1992-09-01 08:20:20", "1999-02-01 21:20:20", "1992-12-01 03:02:20")) |> cut(2)
readr::parse_datetime(c("1992-02-01 01:00:20", "1992-02-06 03:00:20", "1992-05-01 01:20:20", "1992-09-01 08:20:20", "1999-02-01 21:20:20", "1992-12-01 03:02:20")) |> cut(breaks="weekday")
as.Date(c("1992-02-01 01:00:20", "1992-02-06 03:00:20", "1992-05-01 01:20:20", "1992-09-01 08:20:20", "1999-02-01 21:20:20", "1992-12-01 03:02:20")) |> cut(2)
as.Date(c("1992-02-01 01:00:20", "1992-02-06 03:00:20", "1992-05-01 01:20:20", "1992-09-01 08:20:20", "1999-02-01 21:20:20", "1992-12-01 03:02:20")) |> cut(breaks="weekday")
}

24
man/is_any_class.Rd Normal file
View file

@ -0,0 +1,24 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/cut-variable-dates.R
\name{is_any_class}
\alias{is_any_class}
\title{Test class}
\usage{
is_any_class(data, class.vec)
}
\arguments{
\item{data}{data}
\item{class.vec}{vector of class names to test}
}
\value{
factor
}
\description{
Test class
}
\examples{
vapply(REDCapCAST::redcapcast_data, \(.x){
is_any_class(.x, c("hms", "Date", "POSIXct", "POSIXt"))
}, logical(1))
}

20
man/is_datetime.Rd Normal file
View file

@ -0,0 +1,20 @@
% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/cut-variable-dates.R
\name{is_datetime}
\alias{is_datetime}
\title{Test is date/datetime/time}
\usage{
is_datetime(data)
}
\arguments{
\item{data}{data}
}
\value{
factor
}
\description{
Test is date/datetime/time
}
\examples{
vapply(REDCapCAST::redcapcast_data, is_datetime, logical(1))
}

View file

@ -5,13 +5,15 @@
\alias{m_redcap_readServer}
\title{Shiny module to browser and export REDCap data}
\usage{
m_redcap_readUI(id)
m_redcap_readUI(id, include_title = TRUE)
m_redcap_readServer(id, output.format = "df")
m_redcap_readServer(id, output.format = c("df", "teal", "list"))
}
\arguments{
\item{id}{Namespace id}
\item{include_title}{logical to include title}
\item{output.format}{data.frame ("df") or teal data object ("teal")}
}
\value{

1208
renv.lock

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,5 @@
Version: 1.0
ProjectId: 88756fd6-a799-4963-955c-8216834cd504
RestoreWorkspace: No
SaveWorkspace: No