For loop in inside render UI

Is there any issue in the below sample code.I am trying this in Flexdashboard. What I am trying to do is. I have a dataframe df that has 4 columns(ColA, ColB, ColC and ColD).
ColA and ColB are factors and rest are numeric
I am putting 1 filter called "Variables" as shown below in the dashboard. Now I am planning to make some reactive inputs. Like if I select ColA from the 1st Filter, now 2nd filter should pop up called "Level" with all the values of ColA. Similarly for ColB. I tried with the below where I have incorporated for loop (Below code is only a part of the entire code set). What wrong I am doing here

```{r}
h6(selectInput("se1","Variables",choices = c("",names(filter(df))),width = 150))
w <- list()
output$filter2 <- renderUI({
  sa <- names(Filter(is.factor,df))
  for (i in sa)
  {
  if (input$se1 == i) {
    label = "Levels"
    h6(selectInput("b",label,choices = c("",levels(factor(df[,i]))),width = 150))
  }}
})
uiOutput("filter2")
```

Hello,

I don't fully understand what your goal is, but I can give you a few pointers. I do recommend that you create a Shiny reprex so you can more easily show what's going on and have a 'working' example of the issue. This guide will help you create one:

If you only want to only filter on columns with factors, your first choices of selectInput "sel1" should only be those columns (so ColA and ColB). You can then have your selectInput "b" update its choices based on the choice of "sel1".

I created a dummy example to illustrate:

library(shiny)

ui <- fluidPage(
  selectInput("filter1","Variables",choices = c(),width = 150),
  selectInput("filter2","Variables",choices = c(),width = 150)
)

server <- function(input, output, session) {
  
  #Generate some data
  myData = data.frame(ColA = LETTERS[sample(1:5, 10, replace = T)], 
             ColB = LETTERS[sample(6:10, 10, replace = T)], 
             ColC= runif(10))
  
  #For filter 1, only use the columns with factors
  updateSelectInput(session, "filter1", 
                    choices = colnames(myData)[sapply(myData, class) == "factor"])
  
  #Based on filter 1, show the factors in filter 2
  observeEvent(input$filter1, {
    updateSelectInput(session, "filter2",
                      choices = myData[,input$filter1])
  }, ignoreInit = T)
  
}

shinyApp(ui, server)

NOTE: the ignoreInit = T for the second filter's observeEvent is needed to prevent an error occurring when there is nothing assigned yet to filter 1 when the app loads.

Again, if this is not answering your question, please create a reprex using the guide.

Kind regards,
PJ

Yeah I thought of putting the entire code but I feel it is like asking the solution itself. However I have added the repex below

I have a dataframe df that has 4 columns(ColA, ColB, ColC and ColD).
ColA and ColB are factors and rest are numeric
I am putting 1 filter called "Variables" as shown below in the dashboard. Now I am planning to make some reactive inputs. Like if I select ColA from the 1st Filter, now 2nd filter should pop up called "Level" with all the values of ColA. Similarly for ColB. I tried with the below where I have incorporated for loop (Below code is only a part of the entire code set). What wrong I am doing here

---
title: "Untitled"
output: 
  flexdashboard::flex_dashboard:
    orientation: columns
    vertical_layout: scroll
    runtime: shiny
    source_code: embed
    theme: cosmo
---

```{r setup, include=FALSE}
library(flexdashboard)
library(magrittr)
library(ggplot2)
library(cowplot)
library(tidyverse)
library(dplyr)
library(tidyr)
```

```{r}
  df <- structure(list(ColA = c("gf", "dfg", "er", "gfs", "fdg", "sdf", 
"er", "dgh", "dfg", "sfdg", "jyfj", "asgfg", "jgh", "ghjhg", 
"ghj", "gjgj", "dgrert", "tyew", "ewt", "tyu", "hgj", "hjghj", 
"dsgdg", "yt", "ryuy", "tyutyu", "uiuy", "yoiy", "ret", "e", 
"dsgdfg", "hgdhg", "gfdg", "dghgd", "hdsger", "gdfgd", "gt", 
"fdgd", "sgdf", "gdfsh", "sdfh", "dfh", "dgsh", "fg", "dds", 
"gh", "rth"), ColB = c("A", "A", "A", "A", "A", "A", "A", "A", 
"A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", "A", 
"B", "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", 
"B", "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", "B", "B"
), ColC = 1:47, ColD = 2:48), class = "data.frame", row.names = c(NA, 
-47L))
  df <- as.data.frame(df)
  df <- as.data.frame(unclass(df))
  # percentage of count for categorical univariate ------------------------
  p1 <- list()
  p <- list()
  bs <- names(Filter(is.factor, df))
  for(i in bs)
  {
    p1[[i]] <- as.data.frame(round(prop.table(table(df[,i]))*100,1))
    do.call(rbind,p1) %>% as.data.frame()
  }
  
  # mean for numeric variable univariate ------------------------------------
  bs1 <- c("mean","median","standard_deviation")
  p2 <- df %>% summarise_if(is.numeric, list(mean=mean), na.rm = TRUE)
  p2 <- round(p2,1)
  p2 <- stack(p2)
  p2$ind <- as.character(p2$ind)
  p2 <- p2 %>% extract(ind, into = c("Measurement", "stat"), "(.*)_([^_]+)$")
  # median for numeric variable univariate ------------------------------------
  p3 <- df %>% summarise_if(is.numeric, list(median=median), na.rm = TRUE)
  p3 <- round(p3,1)
  p3 <- stack(p3)
  p3$ind <- as.character(p3$ind)
  p3 <- p3 %>% extract(ind, into = c("Measurement", "stat"), "(.*)_([^_]+)$")
  # sd for numeric variable univariate ------------------------------------
  p4 <- df %>% summarise_if(is.numeric, list(sd=sd), na.rm = TRUE)
  p4 <- round(p4,1)
  p4 <- stack(p4)
  p4$ind <- as.character(p4$ind)
  p4 <- p4 %>% extract(ind, into = c("Measurement", "stat"), "(.*)_([^_]+)$")
  # Data Distribution -------------------------------------------------------
  df <- as.data.frame(df)
  p10 <- list()
  fg <- list()
  zs <- names(Filter(is.numeric, df))
  dis <- function(data,variable)
  {
    fg <- qplot(variable, data = data, geom = "density")
    fg
  }
  # Box plot -------------------------------------------------------
  fg1 <- list()
  box_plot <- function(data,variable)
  {
    fg1 <- ggplot(data = data, aes(x = "", y = variable)) + geom_boxplot()
    fg1
  }
  p11 <- list()
  ps <- names(Filter(is.numeric, df))
  # Bi Variate --------------------------------------------------------------
  d1 <- stack(lapply(df, class))[2:1] #df should be dataframe
  s1 <- as.data.frame(combn(names(df),2,paste,collapse="&"))
  colnames(s1) <- "d"
  s1$d1 <- sub("\\&.*", "", s1$d)
  s1$d2 <- sapply(strsplit(s1$d,"\\&"),'[',2)
  s1$d3 <- d1$values[match(s1$d1,d1$ind)]
  s1$d4 <- d1$values[match(s1$d2,d1$ind)]
  s1$d3 <- replace(s1$d3,s1$d3=="integer","numeric")
  s1$d4 <- replace(s1$d4,s1$d4=="integer","numeric")
  s1$d5 <- paste(s1$d3,s1$d4,sep = "&")
  # created a class called class1
  class1 <- function(data,var) {
    data[which(data$d==var),"d5"]}
  # if the variable is equal to "numeric&numeric" and so on  ----------------
  fv <- list()
  for(i in s1$d)
  {
    while (class1(s1,i)=="numeric&numeric") {
      fv[i] <- paste(s1[which(s1$d==i),"d1"],s1[which(s1$d==i),"d2"],sep = "&")
      break
    }}
  fv <- stack(fv)
  fv$ind <- as.character(fv$ind)
  fv$v1 <- sapply(strsplit(fv$ind,"\\&"),'[',1)
  fv$v2 <- sapply(strsplit(fv$ind,"\\&"),'[',2)
  p96 <- list()
  # if the variable is equal to "factor&factor" and so on  ----------------
  fv1 <- list()
  for(i in s1$d)
  {
    while (class1(s1,i)=="factor&factor") {
      fv1[i] <- paste(s1[which(s1$d==i),"d1"],s1[which(s1$d==i),"d2"],sep = "&")
      break
    }}
  fv1 <- stack(fv1)
  fv1$ind <- as.character(fv1$ind)
  fv1$v1 <- sapply(strsplit(fv1$ind,"\\&"),'[',1)
  fv1$v2 <- sapply(strsplit(fv1$ind,"\\&"),'[',2)
  fv1$v3 <- c("percentage")
  io <- list()
  asd <- list()
  for(i in 1:nrow(fv1))
  { io[[i]] <- tapply(df[,fv1$v1[i]], df[,fv1$v2[i]], FUN = function(x) length(unique(x)))
  }
  for(i in 1:nrow(fv1))
  { asd[[i]] <- stack(unlist(io[i]))
  colnames(asd[[i]]) <- c(fv1$v1[i],fv1$v2[i])
  asd[[i]] <- data.frame(asd[[i]]) }
  for(i in 1:nrow(fv1))
  {asd[[i]]$percentage <- round((asd[[i]][1]/sum(asd[[i]][1]))*100,1)}
  p97 <- list()
  # Filtering code ----------------------------------------------------------
  theNames <- names(Filter(is.factor,df))
  MyList  <- vector(mode = "list")
  for(i in theNames){
    MyList[[i]] <- levels(df[,i])
  }
```

Summary
=================

Inputs {.sidebar} {data-width=140}
-----------------------------------------------------------------------

```{r}
h6(selectInput("se1","Variables",choices = c("",names(filter(df))),width = 150))
w <- list()
output$filter2 <- renderUI({
  sa <- names(Filter(is.factor,df))
  for (i in sa)
  {
  if (input$se1 == i) {
    label = "Levels"
    h6(selectInput("b",label,choices = c("",levels(factor(df[,i]))),width = 150))
  }}
})
uiOutput("filter2")
```

Column {data-width=650}
-----------------------------------------------------------------------

### Chart A

```{r}

```

Column {data-width=350}
-----------------------------------------------------------------------

### Chart B

```{r}

```

### Chart C

```{r}

```

I also tried replacing that reactive part with below code but did not work out

h6(selectInput("se1","Variables",choices = c("",names(filter(df))),width = 150))
res <- list()
output$filter2 <- renderUI({
  sa <- names(Filter(is.factor,df))
  for (i in sa)
  {
  if (input$se1 == i) {
    label = "Levels"
    res <- levels(factor(df[,i]))
    h6(selectInput("b",label,choices = c("",res[[i]]),width = 150))
  }}
})
uiOutput("filter2")

You shouldn't put the entire code, a reprex has to be minimal, you should include just enough code to reproduce your specific issue and nothing else.

Could you explain what you think the for loop should be doing in your code? I don't understand why are you using it and what do you expect to get as a result from it.

1 Like

Yeah. In the first go I did not put the entire code :slight_smile:. Well I will explain you. What my for loop is doing is,

  1. It is taking factors of my data frame df (in this example it is ColA and ColB)
  2. When ever I Select ColA or ColB from 1st Filter, 2nd filter should pop up showing the its respective levels. For Example when I select ColA, 2nd Filter should show all the levels of ColA. Similarly for ColB. Hope this makes sense. Reason why I am building this is I do not have manually enter all the factor names. I want this is to be executed under for loop so whatever dataset I put in, it should take all the factors into consideration and based on its selection in 1st filter, the 2nd filter should pop up showing all its respective levels. Hope this makes very clear :slight_smile:
h6(selectInput("se1","Variables",choices = c("",names(filter(df))),width = 150))
res <- list()
output$filter2 <- renderUI({
  sa <- names(Filter(is.factor,df))
  for (i in sa)
  {
  if (input$se1 == i) {
    label = "Levels"
    res <- levels(factor(df[,i]))
    h6(selectInput("b",label,choices = c("",res[[i]]),width = 150))
  }}
})
uiOutput("filter2")

Also request you to let me know if I am communicating properly. This community is helping me a lot in building my application. So I am trying my level best to do it myself. In case not, I am seeking help. I am really thankful for all you in guiding me. Thanks a lot

That is true but your code was not reproducible, you have to find a middle point between posting an unreproducible chunk of code and your whole code including a lot of unrelated stuff.
Making a reprex implies to make the effort of putting together a minimal example that reproduces your issue not just to copy some code you already have, after all, you are asking people to invest their time helping you, the least you can do is make it as easy as possible.

I still don't understand why you want to use a loop for that, why you don't just do something like this?

selectInput("se1", "Variables", choices = names(df))
selectInput("b", "", choices = "")
observeEvent(input$se1, {
    updateSelectInput(session, inputId = "b", label = input$se1, choices = unique(df[,input$se1]))
})

You have been asked multiple times, across multiple questions, to provide a reprex. You are taking advantage of the good will of the people on this forum, and if you ask another question without doing the minimal work of first creating a reprex, I will ban you from this forum.

Thanks for the feedback. I will follow it from now on. I will give proper reprex. Apologies again

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.