######################################### # Perceptual map using factor analysis ######################################### pm_nr_dim <- c(2, 3) names(pm_nr_dim) <- c(i18n$t("2 dimensions"), i18n$t("3 dimensions")) ## list of function arguments pm_args <- as.list(formals(prmap)) ## list of function inputs selected by user pm_inputs <- reactive({ ## loop needed because reactive values don't allow single bracket indexing pm_args$data_filter <- if (input$show_filter) input$data_filter else "" pm_args$dataset <- input$dataset for (i in r_drop(names(pm_args))) { pm_args[[i]] <- input[[paste0("pm_", i)]] } pm_args }) pm_plot_args <- as.list(if (exists("plot.prmap")) { formals(plot.prmap) } else { formals(radiant.multivariate:::plot.prmap) }) ## list of function inputs selected by user pm_plot_inputs <- reactive({ ## loop needed because reactive values don't allow single bracket indexing for (i in names(pm_plot_args)) { pm_plot_args[[i]] <- input[[paste0("pm_", i)]] } pm_plot_args }) output$ui_pm_brand <- renderUI({ isLabel <- "character" == .get_class() | "factor" == .get_class() vars <- varnames()[isLabel] selectInput( inputId = "pm_brand", label = i18n$t("Brand:"), choices = vars, selected = state_single("pm_brand", vars), multiple = FALSE ) }) output$ui_pm_attr <- renderUI({ vars <- varnames() ## can't get valid factor scores with PCA and {factor} variables # toSelect <- .get_class() %in% c("numeric", "integer", "date", "factor") toSelect <- .get_class() %in% c("numeric", "integer", "date") vars <- vars[toSelect] selectInput( inputId = "pm_attr", label = i18n$t("Attributes:"), choices = vars, selected = state_multiple("pm_attr", vars), multiple = TRUE, size = min(10, length(vars)), selectize = FALSE ) }) output$ui_pm_pref <- renderUI({ if (not_available(input$pm_attr)) { return() } vars <- varnames() toSelect <- .get_class() %in% c("numeric", "integer", "date", "factor") vars <- vars[toSelect] if (length(vars) > 0) vars <- vars[-which(vars %in% c(input$pm_brand, input$pm_attr))] selectInput( inputId = "pm_pref", label = i18n$t("Preferences:"), choices = vars, selected = state_multiple("pm_pref", vars), multiple = TRUE, size = max(1, min(5, length(vars))), selectize = FALSE ) }) output$ui_pm_plots <- renderUI({ plot_list <- c("brand", "attr") names(plot_list) <- c(i18n$t("Brands"), i18n$t("Attributes")) if (!is.empty(input$pm_pref)) { plot_list <- c(plot_list, "pref") names(plot_list)[length(plot_list)] <- i18n$t("Preferences") } checkboxGroupInput( "pm_plots", NULL, plot_list, selected = state_group("pm_plots"), inline = TRUE ) }) output$ui_pm_store_name <- renderUI({ req(input$dataset) textInput("pm_store_name", i18n$t("Store factor scores:"), "", placeholder = i18n$t("Provide single variable name")) }) ## add a spinning refresh icon if the factors need to be updated run_refresh(pm_args, "pm", init = "attr", tabs = "tabs_prmap", label = i18n$t("Estimate model"), relabel = i18n$t("Re-estimate model")) output$ui_prmap <- renderUI({ req(input$dataset) tagList( conditionalPanel( condition = "input.tabs_prmap == 'Summary'", wellPanel( actionButton("pm_run", i18n$t("Estimate model"), width = "100%", icon = icon("play", verify_fa = FALSE), class = "btn-success") ) ), wellPanel( conditionalPanel( condition = "input.tabs_prmap == 'Summary'", uiOutput("ui_pm_brand"), uiOutput("ui_pm_attr"), uiOutput("ui_pm_pref"), radioButtons( inputId = "pm_nr_dim", label = NULL, pm_nr_dim, selected = state_init("pm_nr_dim", 2), inline = TRUE ), # checkboxInput("pm_hcor", "Adjust for {factor} variables", value = state_init("pm_hcor", FALSE)), numericInput( "pm_cutoff", label = i18n$t("Loadings cutoff:"), min = 0, max = 1, state_init("pm_cutoff", 0), step = .05 ), conditionalPanel( condition = "input.pm_attr != null", tags$table( tags$td(uiOutput("ui_pm_store_name")), tags$td(actionButton("pm_store", i18n$t("Store"), icon = icon("plus", verify_fa = FALSE)), class = "top") ) ) ), conditionalPanel( condition = "input.tabs_prmap == 'Plot'", uiOutput("ui_pm_plots"), tags$table( tags$td(numericInput("pm_scaling", i18n$t("Attribute scale:"), state_init("pm_scaling", 2), .5, 4, .1, width = "117px")), tags$td(numericInput("pm_fontsz", i18n$t("Font size:"), state_init("pm_fontsz", 5), 1, 20, 1, width = "117px")), width = "100%" ) ) ), help_and_report( modal_title = i18n$t("Attribute based brand maps"), fun_name = "prmap", help_file = inclMD(file.path(getOption("radiant.path.multivariate"), "app/tools/help/prmap.md")) ) ) }) pm_plot <- eventReactive(input$pm_run, { req(input$pm_nr_dim) nrDim <- as.numeric(input$pm_nr_dim) nrPlots <- (nrDim * (nrDim - 1)) / 2 list(plot_width = 650, plot_height = 650 * nrPlots) }) pm_plot_width <- function() { pm_plot() %>% { if (is.list(.)) .$plot_width else 650 } } pm_plot_height <- function() { pm_plot() %>% { if (is.list(.)) .$plot_height else 650 } } output$prmap <- renderUI({ register_print_output("summary_prmap", ".summary_prmap") register_plot_output( "plot_prmap", ".plot_prmap", width_fun = "pm_plot_width", height_fun = "pm_plot_height" ) pm_output_panels <- tabsetPanel( id = "tabs_prmap", tabPanel( i18n$t("Summary"), value = "Summary", download_link("dl_pm_loadings"), br(), verbatimTextOutput("summary_prmap") ), tabPanel( i18n$t("Plot"), value = "Plot", download_link("dlp_prmap"), plotOutput("plot_prmap", height = "100%") ) ) stat_tab_panel( menu = i18n$t("Multivariate > Maps"), tool = i18n$t("Attributes"), tool_ui = "ui_prmap", output_panels = pm_output_panels ) }) .prmap_available <- reactive({ if (not_pressed(input$pm_run)) { i18n$t("** Press the Estimate button to generate perceptual maps **") } else if (not_available(input$pm_brand) || not_available(input$pm_attr)) { i18n$t("This analysis requires a brand variable of type factor or character and multiple attribute variables\nof type numeric or integer. If these variables are not available please select another dataset.") %>% suggest_data("retailers") } else if (length(input$pm_attr) < 2) { i18n$t("Please select two or more attribute variables") } else { # brand <- .get_data()[[input$pm_brand]] # if (length(unique(brand)) < length(brand)) { # "Number of observations and unique IDs for the brand variable do not match.\nPlease choose another brand variable or another dataset.\n\n" %>% # suggest_data("retailers") # } else { "available" # } } }) .prmap <- eventReactive(input$pm_run, { withProgress(message = i18n$t("Generating perceptual map"), value = 1, { pmi <- pm_inputs() pmi$envir <- r_data do.call(prmap, pmi) }) }) .summary_prmap <- reactive({ if (.prmap_available() != "available") { return(.prmap_available()) } validate( need( input$pm_cutoff >= 0 && input$pm_cutoff <= 1, i18n$t("Provide a correlation cutoff value in the range from 0 to 1") ) ) summary(.prmap(), cutoff = input$pm_cutoff) }) .plot_prmap <- eventReactive( { c(input$pm_run, pm_plot_inputs()) }, { if (.prmap_available() != "available") { return(.prmap_available()) } req("pm_plots" %in% names(input)) robj <- .prmap() if (is.character(robj)) { return(robj) } withProgress(message = i18n$t("Generating brand maps"), value = 1, { do.call(plot, c(list(x = robj), pm_plot_inputs(), shiny = TRUE)) }) } ) prmap_report <- function() { outputs <- c("summary", "plot") inp_out <- list(list(cutoff = input$pm_cutoff, dec = 2), "") inp_out[[2]] <- clean_args(pm_plot_inputs(), pm_plot_args[-1]) inp <- clean_args(pm_inputs(), pm_args) if (!is.empty(inp$nr_dim)) inp$nr_dim <- as_integer(inp$nr_dim) if (!is.empty(input$pm_store_name)) { fixed <- fix_names(input$pm_store_name) updateTextInput(session, "pm_store_name", value = fixed) xcmd <- glue('{input$dataset} <- store({input$dataset}, result, name = "{fixed}")') } else { xcmd <- "" } update_report( inp_main = inp, fun_name = "prmap", inp_out = inp_out, fig.width = pm_plot_width(), fig.height = pm_plot_height(), xcmd = xcmd ) } ## store factor scores observeEvent(input$pm_store, { req(input$pm_store_name, input$pm_run) fixed <- fix_names(input$pm_store_name) updateTextInput(session, "pm_store_name", value = fixed) robj <- .prmap() if (!is.character(robj)) { withProgress( message = i18n$t("Storing factor scores"), value = 1, r_data[[input$dataset]] <- store(r_data[[input$dataset]], robj, name = fixed) ) } }) dl_pm_loadings <- function(path) { if (pressed(input$pm_run)) { .prmap() %>% { if (is.list(.)) .$fres$loadings else return() } %>% clean_loadings(input$pm_cutoff, fsort = FALSE) %>% write.csv(file = path) } else { cat(i18n$t("No output available. Press the Estimate button to generate the factor analysis results"), file = path) } } download_handler( id = "dl_pm_loadings", fun = dl_pm_loadings, fn = function() paste0(input$dataset, "_prmap_loadings"), type = "csv", caption = i18n$t("Save factor loadings") ) download_handler( id = "dlp_prmap", fun = download_handler_plot, fn = function() paste0(input$dataset, "_prmap"), type = "png", caption = i18n$t("Save preceptual map plot"), plot = .plot_prmap, width = pm_plot_width, height = pm_plot_height ) observeEvent(input$prmap_report, { r_info[["latest_screenshot"]] <- NULL prmap_report() }) observeEvent(input$prmap_screenshot, { r_info[["latest_screenshot"]] <- NULL radiant_screenshot_modal("modal_prmap_screenshot") }) observeEvent(input$modal_prmap_screenshot, { prmap_report() removeModal() ## remove shiny modal after save })