FreesearchR/R/data-summary.R

362 lines
8 KiB
R
Raw Normal View History

2025-01-16 11:24:26 +01:00
#' Data summary module
#'
#' @param id Module id. (Use 'ns("id")')
#'
#' @name data-summary
#' @returns Shiny ui module
#' @export
2025-01-15 16:21:38 +01:00
data_summary_ui <- function(id) {
ns <- NS(id)
2025-01-16 11:24:26 +01:00
toastui::datagridOutput(outputId = ns("tbl_summary"))
2025-01-15 16:21:38 +01:00
}
#'
2025-01-16 11:24:26 +01:00
#' @param data data
#' @param color.main main color
#' @param color.sec secondary color
2025-04-09 12:31:08 +02:00
#' @param ... arguments passed to create_overview_datagrid
2025-01-16 11:24:26 +01:00
#'
#' @name data-summary
#' @returns shiny server module
#' @export
2025-01-15 16:21:38 +01:00
data_summary_server <- function(id,
2025-01-16 11:24:26 +01:00
data,
color.main,
color.sec,
...) {
2025-01-15 16:21:38 +01:00
shiny::moduleServer(
id = id,
module = function(input, output, session) {
ns <- session$ns
2025-01-16 11:24:26 +01:00
output$tbl_summary <-
2025-01-15 16:21:38 +01:00
toastui::renderDatagrid(
2025-01-17 15:59:24 +01:00
{
shiny::req(data())
data() |>
2025-01-16 11:24:26 +01:00
overview_vars() |>
2025-04-09 12:31:08 +02:00
create_overview_datagrid(...) |>
2025-01-16 11:24:26 +01:00
add_sparkline(
column = "vals",
color.main = color.main,
color.sec = color.sec
)
2025-01-17 15:59:24 +01:00
}
)
2025-01-16 11:24:26 +01:00
2025-01-15 16:21:38 +01:00
}
)
}
#' Add sparkline to datagrid
#'
#' @param grid grid
#' @param column clumn to transform
#'
#' @returns datagrid
#' @export
#'
#' @examples
#' grid <- mtcars |>
#' default_parsing() |>
#' overview_vars() |>
#' toastui::datagrid() |>
#' add_sparkline()
#' grid
add_sparkline <- function(grid, column = "vals", color.main = "#2a8484", color.sec = "#84EF84") {
out <- toastui::grid_sparkline(
grid = grid,
column = column,
renderer = function(data) {
data_cl <- class(data)
2025-04-08 13:45:07 +02:00
if (all(sapply(data,is.na))){
type <- "line"
ds <- data.frame(x = NA, y = NA)
horizontal <- FALSE
} else if (identical(data_cl, "factor")) {
2025-01-15 16:21:38 +01:00
type <- "column"
s <- summary(data)
ds <- data.frame(x = names(s), y = s)
horizontal <- FALSE
2025-03-11 13:42:57 +01:00
} else if (identical(data_cl, "logical")) {
type <- "column"
s <- table(data)
ds <- data.frame(x = names(s), y = as.vector(s))
horizontal <- FALSE
2025-01-15 16:21:38 +01:00
} else if (any(c("numeric", "integer") %in% data_cl)) {
2025-01-16 11:24:26 +01:00
if (is_consecutive(data)) {
2025-01-15 16:21:38 +01:00
type <- "line"
ds <- data.frame(x = NA, y = NA)
horizontal <- FALSE
} else {
type <- "box"
ds <- data.frame(x = 1, y = data)
horizontal <- TRUE
}
} else if (any(c("Date", "POSIXct", "POSIXt", "hms", "difftime") %in% data_cl)) {
type <- "line"
ds <- data.frame(x = seq_along(data), y = data)
horizontal <- FALSE
} else {
type <- "line"
ds <- data.frame(x = NA, y = NA)
horizontal <- FALSE
}
apexcharter::apex(
ds,
apexcharter::aes(x, y),
type = type,
auto_update = TRUE
) |>
apexcharter::ax_chart(sparkline = list(enabled = TRUE)) |>
apexcharter::ax_plotOptions(
boxPlot = apexcharter::boxplot_opts(color.upper = color.sec, color.lower = color.main),
bar = apexcharter::bar_opts(horizontal = horizontal)
) |>
apexcharter::ax_colors(
c(color.main, color.sec)
)
}
)
toastui::grid_columns(
grid = out,
columns = column,
minWidth = 200
)
}
2025-01-16 11:24:26 +01:00
#' Checks if elements in vector are equally spaced as indication of ID
#'
#' @param data vector
#'
2025-01-16 11:45:04 +01:00
#' @returns logical
2025-01-16 11:24:26 +01:00
#' @export
#'
#' @examples
#' 1:10 |> is_consecutive()
#' sample(1:100,40) |> is_consecutive()
is_consecutive <- function(data){
suppressWarnings(length(unique(diff(as.numeric(data))))==1)
}
2025-01-15 16:21:38 +01:00
#' Create a data overview data.frame ready for sparklines
#'
#' @param data data
#'
#' @returns data.frame
#' @export
#'
#' @examples
#' mtcars |> overview_vars()
overview_vars <- function(data) {
data <- as.data.frame(data)
dplyr::tibble(
icon = data_type(data),
type = icon,
2025-01-15 16:21:38 +01:00
name = names(data),
n_missing = unname(colSums(is.na(data))),
p_complete = 1 - n_missing / nrow(data),
n_unique = get_n_unique(data),
vals = as.list(data)
)
}
#' Create a data overview datagrid
#'
#' @param data data
#'
#' @returns datagrid
#' @export
#'
#' @examples
#' mtcars |>
#' overview_vars() |>
#' create_overview_datagrid()
2025-04-09 12:31:08 +02:00
create_overview_datagrid <- function(data,...) {
2025-01-15 16:21:38 +01:00
# browser()
gridTheme <- getOption("datagrid.theme")
if (length(gridTheme) < 1) {
datamods:::apply_grid_theme()
}
on.exit(toastui::reset_grid_theme())
col.names <- names(data)
std_names <- c(
"Name" = "name",
"Icon" = "icon",
2025-02-26 12:18:46 +01:00
"Type" = "type",
"Missings" = "n_missing",
2025-01-15 16:21:38 +01:00
"Complete" = "p_complete",
"Unique" = "n_unique",
2025-02-26 12:18:46 +01:00
"Distribution" = "vals"
2025-01-15 16:21:38 +01:00
)
headers <- lapply(col.names, \(.x){
if (.x %in% std_names) {
names(std_names)[match(.x, std_names)]
} else {
.x
}
}) |> unlist()
grid <- toastui::datagrid(
data = data,
theme = "default",
2025-04-09 12:31:08 +02:00
colwidths = "fit",
...
2025-01-15 16:21:38 +01:00
)
grid <- toastui::grid_columns(
grid = grid,
columns = col.names,
header = headers,
2025-02-26 12:18:46 +01:00
resizable = TRUE
)
grid <- toastui::grid_columns(
grid = grid,
columns = "vals",
width = 120
)
grid <- toastui::grid_columns(
grid = grid,
columns = "icon",
2025-02-26 12:18:46 +01:00
header = " ",
align = "center",sortable = FALSE,
width = 40
2025-01-15 16:21:38 +01:00
)
grid <- add_class_icon(
grid = grid,
column = "icon",
fun = type_icons
2025-01-15 16:21:38 +01:00
)
2025-01-16 11:24:26 +01:00
grid <- toastui::grid_format(
grid = grid,
"p_complete",
formatter = toastui::JS("function(obj) {return (obj.value*100).toFixed(0) + '%';}")
)
2025-01-15 16:21:38 +01:00
2025-01-20 13:18:36 +01:00
## This could obviously be extended, which will added even more complexity.
grid <- toastui::grid_filters(
grid = grid,
2025-01-20 13:18:36 +01:00
column = "name",
# columns = unname(std_names[std_names!="vals"]),
showApplyBtn = FALSE,
showClearBtn = TRUE,
type = "text"
)
2025-01-15 16:21:38 +01:00
return(grid)
}
#' Convert class grid column to icon
#'
#' @param grid grid
#' @param column column
#'
#' @returns datagrid
#' @export
#'
#' @examples
2025-01-16 11:45:04 +01:00
#' mtcars |>
#' overview_vars() |>
#' toastui::datagrid() |>
#' add_class_icon()
add_class_icon <- function(grid, column = "class", fun=class_icons) {
2025-01-15 16:21:38 +01:00
out <- toastui::grid_format(
grid = grid,
column = column,
formatter = function(value) {
lapply(
X = value,
FUN = fun
2025-01-15 16:21:38 +01:00
)
}
)
toastui::grid_columns(
grid = out,
header = NULL,
columns = column,
width = 60
)
}
#' Get data class icons
#'
#' @param x character vector of data classes
#'
#' @returns
#' @export
#'
#' @examples
#' "numeric" |> class_icons()
#' default_parsing(mtcars) |> sapply(class) |> class_icons()
class_icons <- function(x) {
if (length(x)>1){
sapply(x,class_icons)
} else {
if (identical(x, "numeric")) {
shiny::icon("calculator")
} else if (identical(x, "factor")) {
shiny::icon("chart-simple")
} else if (identical(x, "integer")) {
shiny::icon("arrow-down-1-9")
} else if (identical(x, "character")) {
shiny::icon("arrow-down-a-z")
} else if (identical(x, "logical")) {
shiny::icon("toggle-off")
} else if (any(c("Date", "POSIXct", "POSIXt") %in% x)) {
shiny::icon("calendar-days")
} else if ("hms" %in% x) {
shiny::icon("clock")
} else {
shiny::icon("table")
}}
}
#' Get data type icons
#'
#' @param x character vector of data classes
#'
#' @returns
#' @export
#'
#' @examples
#' "ordinal" |> type_icons()
#' default_parsing(mtcars) |> sapply(data_type) |> type_icons()
type_icons <- function(x) {
if (length(x)>1){
sapply(x,class_icons)
} else {
if (identical(x, "continuous")) {
shiny::icon("calculator")
} else if (identical(x, "categorical")) {
shiny::icon("chart-simple")
} else if (identical(x, "ordinal")) {
shiny::icon("arrow-down-1-9")
} else if (identical(x, "text")) {
shiny::icon("arrow-down-a-z")
} else if (identical(x, "dichotomous")) {
shiny::icon("toggle-off")
} else if (identical(x,"datetime")) {
shiny::icon("calendar-days")
} else if (identical(x,"id")) {
shiny::icon("id-card")
} else {
shiny::icon("table")
}
}
}