# 方法(分开包裹,不用等号) ff_method <- c("PCA", "maxlik") names(ff_method) <- c(i18n$t("Principal components"), i18n$t("Maximum Likelihood")) # 旋转(分开包裹,不用等号) ff_rotation <- c("none", "varimax", "quartimax", "equamax", "promax", "oblimin", "simplimax") names(ff_rotation) <- c( i18n$t("None"), i18n$t("Varimax"), i18n$t("Quartimax"), i18n$t("Equamax"), i18n$t("Promax"), i18n$t("Oblimin"), i18n$t("Simplimax") ) ## list of function arguments ff_args <- as.list(formals(full_factor)) ## list of function inputs selected by user ff_inputs <- reactive({ ff_args$data_filter <- if (input$show_filter) input$data_filter else "" ff_args$dataset <- input$dataset ## loop needed because reactive values don't allow single bracket indexing for (i in r_drop(names(ff_args))) { ff_args[[i]] <- input[[paste0("ff_", i)]] } ff_args }) ############################### # Factor analysis ############################### output$ui_ff_vars <- renderUI({ vars <- varnames() toSelect <- .get_class() %in% c("numeric", "integer", "date", "factor") vars <- vars[toSelect] selectInput( inputId = "ff_vars", label = i18n$t("Variables:"), choices = vars, selected = state_multiple("ff_vars", vars, input$pf_vars), multiple = TRUE, size = min(10, length(vars)), selectize = FALSE ) }) output$ui_ff_store_name <- renderUI({ req(input$dataset) textInput("ff_store_name", i18n$t("Store factor scores:"), "", placeholder = i18n$t("Provide single variable name")) }) ## add a spinning refresh icon if the tabel needs to be (re)calculated run_refresh(ff_args, "ff", init = "vars", tabs = "tabs_full_factor", label = i18n$t("Estimate model"), relabel = i18n$t("Re-estimate model")) output$ui_full_factor <- renderUI({ req(input$dataset) tagList( conditionalPanel( condition = "input.tabs_full_factor == 'Plot'", wellPanel( checkboxGroupInput( "ff_plots", NULL, { ch <- c("resp", "attr") names(ch) <- c(i18n$t("Respondents"), i18n$t("Attributes")) ch }, selected = state_group("ff_plots", "attr"), inline = TRUE ) # conditionalPanel( # condition = "input.tabs_full_factor == 'Plot'", # tags$table( # tags$td(numericInput("ff_scaling", "Respondent scale:", state_init("ff_scaling", 0.5), .5, 4, .1, width = "117px")), # tags$td(numericInput("ff_fontsz", "Font size:", state_init("ff_fontsz", 5), 1, 20, 1, width = "117px")), # width = "100%" # ) # ) ) ), conditionalPanel( condition = "input.tabs_full_factor == 'Summary'", wellPanel( actionButton("ff_run", i18n$t("Estimate model"), width = "100%", icon = icon("play", verify_fa = FALSE), class = "btn-success") ), wellPanel( uiOutput("ui_ff_vars"), selectInput( "ff_method", label = i18n$t("Method:"), choices = ff_method, selected = state_single("ff_method", ff_method, "PCA") ), checkboxInput("ff_hcor", i18n$t("Adjust for {factor} variables"), value = state_init("ff_hcor", FALSE)), tags$table( tags$td(numericInput("ff_nr_fact", label = i18n$t("Nr. of factors:"), min = 1, value = state_init("ff_nr_fact", 1))), tags$td(numericInput("ff_cutoff", label = i18n$t("Cutt-off:"), min = 0, max = 1, value = state_init("ff_cutoff", 0), step = .05, width = "117px")) ), checkboxInput("ff_fsort", i18n$t("Sort factor loadings"), value = state_init("ff_fsort", FALSE)), selectInput( "ff_rotation", label = i18n$t("rotation:"), ff_rotation, selected = state_single("ff_rotation", ff_rotation, "varimax") ), conditionalPanel( condition = "input.ff_vars != null", tags$table( # tags$td(textInput("ff_store_name", "Store scores:", state_init("ff_store_name", "factor"))), tags$td(uiOutput("ui_ff_store_name")), tags$td(actionButton("ff_store", i18n$t("Store"), icon = icon("plus", verify_fa = FALSE)), class = "top") ) ) ) ), help_and_report( modal_title = i18n$t("Factor"), fun_name = "full_factor", help_file = inclMD(file.path(getOption("radiant.path.multivariate"), "app/tools/help/full_factor.md")) ) ) }) ff_plot <- reactive({ if (pressed(input$ff_run) && length(input$ff_vars) > 1 && isolate(input$ff_nr_fact) > 1) { plot_height <- plot_width <- 350 nrFact <- min(isolate(input$ff_nr_fact), length(input$ff_vars)) nrPlots <- (nrFact * (nrFact - 1)) / 2 if (nrPlots > 2) { plot_height <- 350 * ceiling(nrPlots / 2) } if (nrPlots > 1) { plot_width <- 700 } } else { plot_height <- plot_width <- 700 } list(plot_width = plot_width, plot_height = plot_height) }) ff_plot_width <- function() { ff_plot() %>% { if (is.list(.)) .$plot_width else 650 } } ff_plot_height <- function() { ff_plot() %>% { if (is.list(.)) .$plot_height else 400 } } output$full_factor <- renderUI({ register_print_output("summary_full_factor", ".summary_full_factor") register_plot_output( "plot_full_factor", ".plot_full_factor", width_fun = "ff_plot_width", height_fun = "ff_plot_height" ) ff_output_panels <- tabsetPanel( id = "tabs_full_factor", tabPanel( i18n$t("Summary"), value = "Summary", download_link("dl_ff_loadings"), br(), verbatimTextOutput("summary_full_factor") ), tabPanel( i18n$t("Plot"), value = "Plot", download_link("dlp_full_factor"), plotOutput("plot_full_factor", height = "100%") ) ) stat_tab_panel( menu = i18n$t("Multivariate > Factor"), tool = i18n$t("Factor"), tool_ui = "ui_full_factor", output_panels = ff_output_panels ) }) .ff_available <- reactive({ if (not_pressed(input$ff_run)) { i18n$t("** Press the Estimate button to generate factor analysis results **") } else if (not_available(input$ff_vars)) { i18n$t("This analysis requires multiple variables of type numeric or integer.\nIf these variables are not available please select another dataset.") %>% suggest_data("toothpaste") } else if (length(input$ff_vars) < 2) { i18n$t("Please select two or more variables") } else { "available" } }) .full_factor <- eventReactive(input$ff_run, { withProgress(message = i18n$t("Estimating factor solution"), value = 1, { ffi <- ff_inputs() ffi$envir <- r_data do.call(full_factor, ffi) }) }) .summary_full_factor <- eventReactive( { c(input$ff_run, input$ff_cutoff, input$ff_fsort) }, { if (not_pressed(input$ff_run)) { return(i18n$t("** Press the Estimate button to generate factor analysis results **")) } if (.ff_available() != "available") { return(.ff_available()) } if (is_not(input$ff_nr_fact)) { return(i18n$t("Number of factors should be >= 1")) } validate( need( input$ff_cutoff >= 0 && input$ff_cutoff <= 1, i18n$t("Provide a correlation cutoff value in the range from 0 to 1") ) ) summary(.full_factor(), cutoff = input$ff_cutoff, fsort = input$ff_fsort) } ) .plot_full_factor <- eventReactive( { c(input$ff_run, !is.null(input$ff_plots)) }, { if (not_pressed(input$ff_run)) { i18n$t("** Press the Estimate button to generate factor analysis results **") } else if (.ff_available() != "available") { .ff_available() } else if (is_not(input$ff_nr_fact) || input$ff_nr_fact < 2) { i18n$t("Plot requires 2 or more factors.\nChange the number of factors in the Summary tab and re-estimate") } else { withProgress(message = i18n$t("Generating factor plots"), value = 1, { plot(.full_factor(), plots = input$ff_plots, shiny = TRUE) }) } } ) full_factor_report <- function() { outputs <- c("summary", "plot") inp_out <- list(list(cutoff = input$ff_cutoff, fsort = input$ff_fsort, dec = 2), list(custom = FALSE)) if (!is.empty(input$ff_store_name)) { fixed <- fix_names(input$ff_store_name) updateTextInput(session, "ff_store_name", value = fixed) xcmd <- glue('{input$dataset} <- store({input$dataset}, result, name = "{fixed}")') } else { xcmd <- "" } # xcmd <- paste0(xcmd, "# clean_loadings(result$floadings, cutoff = ", input$ff_cutoff, ", fsort = ", input$ff_fsort, ", dec = 8) %>%\n# write.csv(file = \"~/loadings.csv\")") # xcmd <- paste0("# store(result, name = \"", input$ff_store_name, "\")\n# clean_loadings(result$floadings, cutoff = ", input$ff_cutoff, ", fsort = ", input$ff_fsort, ", dec = 8) %>% write.csv(file = \"~/loadings.csv\")") update_report( inp_main = clean_args(ff_inputs(), ff_args), fun_name = "full_factor", inp_out = inp_out, fig.width = ff_plot_width(), fig.height = ff_plot_height(), xcmd = xcmd ) } ## store factor scores observeEvent(input$ff_store, { req(input$ff_store_name, input$ff_run) fixed <- fix_names(input$ff_store_name) updateTextInput(session, "ff_store_name", value = fixed) robj <- .full_factor() 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_ff_loadings <- function(path) { if (pressed(input$ff_run)) { .full_factor() %>% { if (is.list(.)) .$floadings else return() } %>% clean_loadings(input$ff_cutoff, input$ff_fsort) %>% write.csv(file = path) } else { cat(i18n$t("No output available. Press the Estimate button to generate factor loadings"), file = path) } } download_handler( id = "dl_ff_loadings", fun = dl_ff_loadings, fn = function() paste0(input$dataset, "_loadings"), type = "csv", caption = i18n$t("Save factor loadings") ) download_handler( id = "dlp_full_factor", fun = download_handler_plot, fn = function() paste0(input$dataset, "_factor"), type = "png", caption = i18n$t("Save factor plots"), plot = .plot_full_factor, width = ff_plot_width, height = ff_plot_height ) observeEvent(input$full_factor_report, { r_info[["latest_screenshot"]] <- NULL full_factor_report() }) observeEvent(input$full_factor_screenshot, { r_info[["latest_screenshot"]] <- NULL radiant_screenshot_modal("modal_full_factor_screenshot") }) observeEvent(input$modal_full_factor_screenshot, { full_factor_report() removeModal() ## remove shiny modal after save })