Nothing
#' Note: any variable whose name starts with . and ends with _ was defined in the enclosing
#' environment of server when calling interactive_clustering().
server <- function(input, output, session) {
# ==============================================================================================
# reactive values
result <- reactiveVal(NA)
pair_ids <- reactiveVal(NA)
best_window <- reactiveVal(NA_integer_)
constraints <- reactiveVal(data.frame())
window_flags <- reactiveVal(data.frame())
# non-reactive values
pair_tracker <- NA
cluster_ids <- NA
# functions
score_fun <- function(obj_list, ...) {
df_list <- lapply(obj_list, function(obj) {
df <- as.data.frame(rbind(obj@cluster))
colnames(df) <- paste0(".cl_", 1L:length(obj@cluster))
df
})
dplyr::bind_rows(df_list)
}
enable_buttons <- function() {
shinyjs::enable("cluster__must_link")
shinyjs::enable("cluster__cannot_link")
shinyjs::enable("cluster__dont_know")
}
disable_buttons <- function() {
shinyjs::disable("cluster__must_link")
shinyjs::disable("cluster__cannot_link")
shinyjs::disable("cluster__dont_know")
}
get_new_pair <- function() {
new_pair <- pair_tracker$get_unseen_pair()
if (is.null(new_pair)) {
pair_ids(NULL)
disable_buttons()
shinyjs::alert("No unlinked pairs left.")
}
else {
enable_buttons()
pair_ids(new_pair)
}
}
majority <- function(x) {
ux <- sort(unique(x))
ux[which.max(tabulate(match(x, ux)))]
}
if (is.null(complexity))
complexity <- function(flags) {
if (length(flags) <= 1L) return(0)
sign_changes <- sum(abs(diff(flags)))
max_consecutive_true <- rle(flags)
if (any(max_consecutive_true$values))
max_consecutive_true <- max(max_consecutive_true$lengths[max_consecutive_true$values])
else
max_consecutive_true <- 0
# return
cmp <- sign_changes / (length(flags) - 1L) / max_consecutive_true
if (is.na(cmp)) cmp <- Inf
cmp
}
feedback_handler <- function(pair, type) {
cmp <- switch(
type,
"must_link" = `==`,
"cannot_link" = `!=`
)
df <- data.frame(
series1 = pair[1L],
series2 = pair[2L],
link_type = type,
stringsAsFactors = FALSE
)
flags <- logical(nrow(cluster_ids))
for (i in 1L:nrow(cluster_ids)) {
# + 2L due to cluster_ids having config_id and window_size as first two columns
flags[i] <- cmp(cluster_ids[i, pair[1L] + 2L],
cluster_ids[i, pair[2L] + 2L])
}
df$complexity <- complexity(flags)
df$best_window <- cluster_ids[which.max(flags), 2L]
# update reactive values
constraints(rbind(constraints(), df))
window_flags({
rbind(window_flags(), data.frame(
constraint = paste0(type, "=", paste0(pair, collapse = ",")),
window_size = cluster_ids$window_size,
flag = as.integer(flags),
stringsAsFactors = FALSE
))
})
}
# ==============================================================================================
# Explore tab
# characteristics table
output$explore__characteristics <- renderTable(characteristics_table, quoted = TRUE)
# plot
observeEvent(input$explore__trigger_plot, explore_plot, handler.quoted = TRUE)
# ==============================================================================================
# Cluster tab
# ----------------------------------------------------------------------------------------------
# distance options
observe({
if (input$cluster__clus_type == "t") {
shinyjs::disable("cluster__dist_args")
}
else {
shinyjs::enable("cluster__dist_args")
}
})
# ----------------------------------------------------------------------------------------------
# centroid options
observe({
if (input$cluster__cent_custom) {
shinyjs::show("cluster__cent_func")
shinyjs::hide("cluster__cent")
}
else {
shinyjs::hide("cluster__cent_func")
shinyjs::show("cluster__cent")
}
})
observe({
choices <- switch(
input$cluster__clus_type,
"p" = as.list(centroids_nonfuzzy),
"h" = list("default"),
"t" = list("default")
)
selected <- switch(
input$cluster__clus_type,
"p" = "pam",
"h" = "default",
"t" = "default"
)
updateSelectInput(
session,
"cluster__cent",
label = "Centroid",
choices = choices,
selected = selected
)
})
# ----------------------------------------------------------------------------------------------
# control options
observe({
if (!input$cluster__cent_custom && input$cluster__cent == "pam") {
shinyjs::enable("cluster__part_pam")
}
else {
shinyjs::disable("cluster__part_pam")
}
})
observe({
if (input$cluster__hier_method_custom) {
shinyjs::show("cluster__hier_method_func")
shinyjs::hide("cluster__hier_method")
}
else {
shinyjs::hide("cluster__hier_method_func")
shinyjs::show("cluster__hier_method")
}
})
observe({
if (input$cluster__part_nrep > 1L) {
shinyjs::enable("cluster__part_agg")
}
else {
shinyjs::disable("cluster__part_agg")
}
})
observe({
if (!input$cluster__hier_method_custom && input$cluster__hier_method == "all") {
shinyjs::enable("cluster__hier_agg")
}
else {
shinyjs::disable("cluster__hier_agg")
}
})
# ----------------------------------------------------------------------------------------------
# cluster
observeEvent(input$cluster__cluster, main, handler.quoted = TRUE)
# main plot
observeEvent(c(pair_ids(), input$cluster__plot_height), cluster_plot, handler.quoted = TRUE)
# ----------------------------------------------------------------------------------------------
# annotation feedback
observe({
cnst <- constraints()
if (nrow(cnst) > 0L) {
threshold <- input$cluster__complexity
df <- dplyr::filter(cnst, complexity > 0 & complexity < threshold)
trivial <- all(sapply(cnst$complexity, function(cx) {
isTRUE(all.equal(cx, 0)) | is.infinite(cx)
}))
if (nrow(df) > 0L)
best_window(majority(df$best_window))
else if (trivial)
best_window(min(cnst$best_window))
else
best_window(NA)
}
})
observeEvent(input$cluster__must_link, {
ids <- pair_ids()
connected <- pair_tracker$link(ids[1L], ids[2L], 1L)
feedback_handler(ids, "must_link")
if (connected) {
pair_ids(NULL)
disable_buttons()
if (connected)
shinyjs::alert(paste(
"No unlinked pairs left.",
"Based on your feedback,",
"all series should go in 1 cluster."
))
}
else {
get_new_pair()
}
})
observeEvent(input$cluster__cannot_link, {
ids <- pair_ids()
complete <- pair_tracker$link(ids[1L], ids[2L], 0L)
feedback_handler(ids, "cannot_link")
if (complete) {
pair_ids(NULL)
disable_buttons()
if (complete)
shinyjs::alert(paste(
"No unlinked pairs left.",
"Based on your feedback,",
"each series should go in its own cluster."
))
}
else {
get_new_pair()
}
})
observeEvent(input$cluster__dont_know, {
ids <- pair_ids()
complete <- pair_tracker$link(ids[1L], ids[2L], -1L)
if (complete) {
pair_ids(NULL)
disable_buttons()
if (complete)
shinyjs::alert(paste(
"No unlinked pairs left.",
"Based on your feedback,",
"nothing can be inferred."
))
}
else {
get_new_pair()
}
})
output$cluster__best_window <- renderText({
paste("Best window size so far:", best_window())
})
# ==============================================================================================
# Evaluate tab
# ----------------------------------------------------------------------------------------------
# summary
output$evaluate__summary <- renderText({
cnst <- constraints()
out <- paste0("Suggested window: ", best_window(), "<br>\n",
"Annotations so far: ", nrow(cnst), "<br>\n")
if (nrow(cnst) > 0L) {
out <- paste0(
out,
"Number of 'must link': ", sum(cnst$link_type == "must_link"), "<br>\n",
"Number of 'cannot link': ", sum(cnst$link_type == "cannot_link"), "<br>\n"
)
}
out
})
# ----------------------------------------------------------------------------------------------
# save
observeEvent(input$evaluate__save, {
res <- result()
if (inherits(res, "list")) {
res$ensembles <- cluster_ids
res$constraints <- constraints()
res$constraints_plot_df <- window_flags()
res$best_window <- best_window()
out_name <- input$evaluate__save_name
if (nzchar(out_name)) {
tryCatch({
assign(out_name, res, .GlobalEnv)
shinyjs::alert("Saved! Exit shiny app to update the global environment.")
},
error = function(e) {
shinyjs::alert(paste("Could not save:", e$message))
})
}
}
})
# ----------------------------------------------------------------------------------------------
# constraints table
output$evaluate__constraints <- renderTable(constraints())
# ----------------------------------------------------------------------------------------------
# constraints plots
observe(constraints_plot, quoted = TRUE)
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.