Search code examples
rshinyreactivewordcloud2

How to use a reactive function to create a DF in R Shiny


I am trying to create a wordcloud shiny app after going through a DataCamp course. In the course they use a custom create_wordcloud() function to make the app. If someone has the code for it, it would make my life easier.

Anyways, I am trying to go about my own way since I don't have the custom function and will be using wordcloud2() function.

I am having trouble with using reactive functions to make the Shiny app. Essentially I am trying to make it so that the user can select the number of words and change the background in the wordcloud using the UI. For this to happen, I need to convert the text provided by the user a dataframe, order the df by word count, and then subset the df into what ever number the user selects in the app UI.

Here is my code:

library(shiny)
library(colourpicker)
library(tidyverse)


ui <- fluidPage(
  h1("Word Cloud"),
  sidebarLayout(
    sidebarPanel(
      # Add a textarea input
      textAreaInput("text", "Enter text", rows = 7),
      numericInput("num", "Maximum number of words", 25),
      colourInput("col", "Background color", value = "white")
    ),
    mainPanel(
      wordcloud2Output("cloud")
    )
  )
)

server <- function(input, output) {
  df_data <- reactive({
    input$text %>% 
      term_stats(., drop_punct = TRUE, drop = stopwords_en) %>% 
      order_by(desc(count))
  })
  
  output$cloud <- renderWordcloud2({
    # Use the textarea's value as the word cloud data source
    wordcloud2(data = df_data()[1:input$num, ],
                     backgroundColor = input$col)
  })
}

shinyApp(ui = ui, server = server)

The error that I get is:

Warning: Error in : Input must be a vector, not a function.

I really look forward to hearing answers from the community and improve my reactive programming skills!

Thank you!


Solution

  • Solution 1: Obtaining actual code create_wordcloud function as follows:

    create_wordcloud <- function(data, num_words = 100, background = "white") {
      
      # If text is provided, convert it to a dataframe of word frequencies
      if (is.character(data)) {
        corpus <- Corpus(VectorSource(data))
        corpus <- tm_map(corpus, tolower)
        corpus <- tm_map(corpus, removePunctuation)
        corpus <- tm_map(corpus, removeNumbers)
        corpus <- tm_map(corpus, removeWords, stopwords("english"))
        tdm <- as.matrix(TermDocumentMatrix(corpus))
        data <- sort(rowSums(tdm), decreasing = TRUE)
        data <- data.frame(word = names(data), freq = as.numeric(data))
      }
      
      # Make sure a proper num_words is provided
      if (!is.numeric(num_words) || num_words < 3) {
        num_words <- 3
      }  
      
      # Grab the top n most common words
      data <- head(data, n = num_words)
      if (nrow(data) == 0) {
        return(NULL)
      }
      
      wordcloud2(data, backgroundColor = background)
    }
    

    Solution 2: Provided by @Xiang in the comments section:

    ui <- fluidPage(
      h1("Word Cloud"),
      sidebarLayout(
        sidebarPanel(
          # Add a textarea input
          textAreaInput("text", "Enter text", rows = 7),
          numericInput("num", "Maximum number of words", 25),
          colourInput("col", "Background color", value = "white")
        ),
        mainPanel(
          wordcloud2Output("cloud")
        )
      )
    )
    
    server <- function(input, output) {
      df_data <- reactive({
        input$text %>% 
          term_stats(., drop_punct = TRUE, drop = stopwords_en) %>% 
          arrange(desc(count))
      })
      
      output$cloud <- renderWordcloud2({
        # Use the textarea's value as the word cloud data source
        wordcloud2(data = df_data()[1:input$num, ],
                         backgroundColor = input$col)
      })
    }
    
    shinyApp(ui = ui, server = server)