GGPLOT2 behaving reactively in shiny

Please I am trying to convert the graphs in my shiny app into ggplot2 graphs. I use a reactive function to generate the values of financial ratios, which are plotted in bar graphs and pie charts, e.g. Profit/Turnover. When you load the app, by default, each input field (profit input fields and turnover input fields) contains values, whose ratios are plotted in the graphs. The ggplot2 graph displays when the app is loaded. But, when I clear an input field, say in profit, I get the following error message from my app:

Warning in profit/turnover :

  longer object length is not a multiple of shorter object length

Warning: Error in data.frame: arguments imply differing number of rows: 5, 4

  131: stop

  130: data.frame

  129: <reactive:profitability.df> [C:\Users\Idiaye\Documents\R data\mavisanalytic/app.R#446]

  113: profitability.df

   99: renderPlotly [C:\Users\Idiaye\Documents\R data\mavisanalytic/app.R#451]

   98: func

   95: func

   82: origRenderFunc

   81: output$bar

    1: shiny::runApp

The app is designed to compute and plot financial ratios over just five years; hence five data input fields each for profit and turnover. Nevertheless, I want a user to be able to analyse financial performance over any number of years they choose. But, when an input field is cleared, I get the above error message. I didn't experience this problem prior to converting my plots to ggplot2. Below is my code:

Years<-reactive({c(if(input$num>0){"Year 1"},
                 if(input$numb>0){"Year 2"},
                 if(input$number>0){"Year 3"},
                 if(input$numbers>0){"Year 4"},
                 if(input$numbered>0){"Year 5"})})
        
        profitability.df<-reactive({data.frame(profitability.ratio(),Years())})
        
        output$bar<-renderPlotly({
            
            input$go
            isolate(ggplot(profitability.df())+aes(x=Years(),y=profitability.ratio(),fill=Years())+
                      geom_bar(stat="identity")+scale_fill_manual(values=c("#5dade2","#ec7063","#48c9b0","#a569bd","#f4d03f"))+labs(x="Years",y="Margins (%)",fill="Years"))

profit<-reactive({
            as.numeric(c(if(input$num>0){input$num},
                         if(input$numb>0){input$numb},
                         if(input$number>0){input$number},
                         if(input$numbers>0){input$numbers},
                         if(input$numbered>0){input$numbered}))
        })
        
        turnover<-reactive({
            as.numeric(c(if(input$fig>0){input$fig},
                         if(input$figs>0){input$figs},
                         if(input$figu>0){input$figu},
                         if(input$figur>0){input$figur},
                         if(input$figure>0){input$figure}))
            
        })

profitability.ratio<-reactive({profitability(profit(),turnover())})
            
            
        })

Thanks in anticipation of your suggestions.

Please would anybody like to offer a suggestion?

Your chances of support would be increased if you provided a reprex

Hi, here is the reprex code:

library(shiny)

library(ggplot2)

library(plotly)

library(rvest)

ui<-navbarPage(

    windowTitle="Reprex",fluid=TRUE, title=strong("Reprex"),

    tabPanel(title="Reprex App",

        sidebarLayout(

            sidebarPanel(width=2,

                actionButton("delete",strong("Clear Fields"),icon=icon("broom")),br(),br(),  

                textInput("pro","Profit 1",value="100000",width=150),

                textInput("prof","Profit 2",value="150000",width=150),

                textInput("profs","Profit 3",value="200000",width=150),

                textInput("profit","Profit 4",value="250000",width=150),

                textInput("profits","Profit 5",value="300000",width=150),

                hr(),

                h4(strong("Turnover figures:")),

                actionButton("remove",strong("Clear Fields"),icon=icon("broom")),br(),br(),

                textInput("turn","Turnover 1",value="350000",width=150),

                textInput("turno","Turnover 2",value="300000",width=150),

                textInput("turnov","Turnover 3",value="420000",width=150),

                textInput("turnover","Turnover 4",value="600000",width=150),

                textInput("turnovers","Turnover 5",value="550000",width=150),

                actionButton("go",strong("Calculate Ratio"),icon=icon("caret-right"))

            ),mainPanel(

                plotlyOutput("plot")

            )

        )

    )

)

server<-function(input,output,session){

    observeEvent(input$delete,{

        updateTextInput(session,"pro",value="",placeholder="0")

        updateTextInput(session,"prof",value="",placeholder="0")

        updateTextInput(session,"profs",value="",placeholder="0")

        updateTextInput(session,"profit",value="",placeholder="0")

        updateTextInput(session,"profits",value="",placeholder="0")

    })

    observeEvent(input$remove,{

        updateTextInput(session,"turn",value="",placeholder="0")

        updateTextInput(session,"turno",value="",placeholder="0")

        updateTextInput(session,"turnov",value="",placeholder="0")

        updateTextInput(session,"turnover",value="",placeholder="0")

        updateTextInput(session,"turnovers",value="",placeholder="0")

    })

    profits<-reactive({

        as.numeric(c(

            if(input$pro>0){input$pro},

            if(input$prof>0){input$prof},

            if(input$profs>0){input$profs},

            if(input$profit>0){input$profit},

            if(input$profits>0){input$profits}

        ))

    })

    turnover<-reactive({

        as.numeric(c(

            if(input$turn>0){input$turn},

            if(input$turno>0){input$turno},

            if(input$turnov>0){input$turnov},

            if(input$turnover>0){input$turnover},

            if(input$turnovers>0){input$turnovers}

        ))

    })

    year<-reactive({

        c(

            if(input$pro>0){"Year 1"},

            if(input$prof>0){"Year 2"},

            if(input$profs>0){"Year 3"},

            if(input$profit>0){"Year 4"},

            if(input$profits>0){"Year 5"}

        )

    })

    profit.ratio<-function(profits,turnover){

        profitability<-round(profits/turnover*100,1)

        return(profitability)

    }

  profitability<-reactive({profit.ratio(profits(),turnover())})

  profit.df<-reactive({data.frame(profitability(),year())})

  output$plot<-renderPlotly({

      input$go

      isolate(profit.df()%>%

          ggplot()+aes(x=year(),y=profitability(),fill=year())+geom_bar(stat="identity")+

          labs(x="Years",y="Profitability ratios (%)",fill="Years")+scale_fill_brewer(palette="Set1")+

              geom_text(label=profitability(),position=position_stack(vjust=0.8)))

  })

}

shinyApp(ui=ui,server=server)

Thanks.

Thanks for that, its clear what your issue is now.
When you clean an input wether a profit or a turnover, you got from dividing two equally lengthed vectors by each other where they can be performed elementwise to 4 profits divided by 5 years.
What would you wish to happen instead? I assume eliminate the "partial" pair ?

library(shiny)
library(ggplot2)
library(plotly)
# library(rvest) ##not used

in_p_name <- paste0("p", 1:5)
in_t_name <- paste0("t", 1:5)

ui <- navbarPage(
  windowTitle = "Reprex", fluid = TRUE, title = strong("Reprex"),

  tabPanel(
    title = "Reprex App",

    sidebarLayout(
      sidebarPanel(
        width = 2,

        actionButton("delete", strong("Clear Fields"), icon = icon("broom")), br(), br(),
        textInput(in_p_name[1], "Profit 1", value = "100000", width = 150),
        textInput(in_p_name[2], "Profit 2", value = "150000", width = 150),
        textInput(in_p_name[3], "Profit 3", value = "200000", width = 150),
        textInput(in_p_name[4], "Profit 4", value = "250000", width = 150),
        textInput(in_p_name[5], "Profit 5", value = "300000", width = 150),

        hr(),

        h4(strong("Turnover figures:")),

        actionButton("remove", strong("Clear Fields"), icon = icon("broom")), br(), br(),

        textInput(in_t_name[1], "Turnover 1", value = "350000", width = 150),
        textInput(in_t_name[2], "Turnover 2", value = "300000", width = 150),
        textInput(in_t_name[3], "Turnover 3", value = "420000", width = 150),
        textInput(in_t_name[4], "Turnover 4", value = "600000", width = 150),
        textInput(in_t_name[5], "Turnover 5", value = "550000", width = 150),

        actionButton("go", strong("Calculate Ratio"), icon = icon("caret-right"))
      ), mainPanel(
        plotlyOutput("plot")
      )
    )
  )
)

server <- function(input, output, session) {
  observeEvent(input$delete, {
    purrr::walk(
      .x = in_p_name,
      .f = ~ updateTextInput(session, .x, value = "", placeholder = "0")
    )
  })

  observeEvent(input$remove, {
    purrr::walk(
      .x = in_t_name,
      .f = ~ updateTextInput(session, .x, value = "", placeholder = "0")
    )
  })

  profits <- reactive({
    purrr::map_dbl(
      in_p_name,
      ~ {
        num <- as.numeric(input[[.]])
        ifelse(num > 0, num, NA_real_)
      }
    )
  })

  turnover <- reactive({
    purrr::map_dbl(
      in_t_name,
      ~ {
        num <- as.numeric(input[[.]])
        ifelse(num > 0, num, NA_real_)
      }
    )
  })

  year <- reactive({
    years_in_p <- purrr::map2_chr(
      .x = in_p_name, .y = seq_along(in_p_name),
      ~ ifelse(as.numeric(input[[.x]]) > 0, paste0("Year ", .y), NA_character_)
    ) %>% na.omit()
    years_in_t <- purrr::map2_chr(
      .x = in_t_name, .y = seq_along(in_t_name),
      ~ ifelse(as.numeric(input[[.x]]) > 0, paste0("Year ", .y), NA_character_)
    ) %>% na.omit()
    commonyears <- intersect(years_in_p, years_in_t)
    return(commonyears)
  })

  profit.ratio <- function(profits, turnover) {
    profitability <- round(profits / turnover * 100, 1)

    return(profitability)
  }

  profitability <- reactive({
    profit.ratio(req(profits()), req(turnover()))
  })

  profit.df <- reactive({
    p <- req(profitability()) %>% na.omit()
    y <- req(year())
    data.frame(p, y)
  })

  output$plot <- renderPlotly({
    input$go

    isolate({
      req(profit.df()) %>%
        ggplot(aes(x = y, y = p, fill = y)) +
        geom_bar(stat = "identity") +
        labs(x = "Years", y = "Profitability ratios (%)", fill = "Years") +
        scale_fill_brewer(palette = "Set1") +
        geom_text(aes(label = p), position = position_stack(vjust = 0.8))
    })
  })
}

shinyApp(ui = ui, server = server)

Hi, thanks for giving my problem a looking. Prior to converting my plots to ggplot, I was able to clear individual input fields, either of profit or turnover, and still have my default graph on display. But after converting my plots to ggplot, once I clear a field, before entering a new value, my graph disappears and displays an error message.

What I want is for a user to be able to run an analysis over any length of time they choose, 1 year, 2 years, 3 years, etc, which means they can clear the entire field and input their data without the default plot turning into an error message.

By the way, I am a bit mesmerized by the editing you've done to my code, as I am not familiar with package Purr

Im surprised you get errors in my code when clearing a field.. can you tell me a series of steps to follow so I can duplicate ? I did test clearing fields and thought it worked fine.

Hi, sorry for my late reply. Truly, your code doesn't produce any error when I clear the profit input field. However, the turnover input field doesn't clear at all when I click the "Clear Fields" button, and when I tried to run a new analysis, it wouldn't run. Just keeps showing me a graph of equal values.

I remain confused because both clear fields buttons (the one for profits nad the one for turnovers, both work for me with the shared code. They have the effect of going to zero, and the plot disappears while there are no valid ratio's to plot...
You see a different behaviour ?

Yes. The profit fields clear, but the turnover fields do not, and I am not able to run a new analysis. Is there a way to clear the fields, and it outputs a message telling the user that a new graph will be generated when they run a new analysis? The funny thing is that I never had any issues using the base barplot graph. When I cleared any field(s) the default plot would remain displayed. When I inputed new values, and ran a new analysis, I got a new bar plot. I started experiencing problems when I tried to convert my plots into ggplots

I dont think I'm.able to help and advise you if the same code is having different results on our machines... ill have to say sorry and good luck.

Hi, the code now runs well on my machine. Thanks

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

If you have a query related to it or one of the replies, start a new topic and refer back with a link.