5

I'm a Shiny novice, but I'm trying to use it in a project that I'm working on. I'd like to be able to do two things from clicking on a point on a ggplot graph: add a plot character at the specified point (conditioned on info from the sidebar), and add the coordinates (with info from the sidebar) to a data frame. Here's what I've got in terms of code so far:

library(shiny)
library(ggplot2)

df = data.frame()


ui = pageWithSidebar(
  headerPanel("Test"),

  sidebarPanel(
    radioButtons("orientation", "Pick", c("L", "P", "H")),

    selectInput(
      "select1",
      "Select Here:",
      c("Option 1", "Option 2")
    ),

    selectInput(
      "select2",
      "Select Here:",
      c("Option 3", "Option 4"),
    ),

    radioButtons("type", "Type:", c("P", "S")),

    radioButtons("creator", "Creator?", c("H", "A"))
  ),

  mainPanel(
    plotOutput("plot1", click = "plot_click"),
    verbatimTextOutput("info"),
    actionButton("update", "Add Event")
  )
)

server = function(input, output){

  output$plot1 = renderPlot({
    ggplot(df) + geom_rect(xmin = 0, xmax = 100, ymin = 0, ymax = 50, fill = "red")
  })

  output$info = renderText({
    paste0("x = ", input$plot_click$x, "\ny = ", input$plot_click$y)
  })
}

shinyApp(ui, server)

I'm confused on how to add the clicked x and y points from plot_click to df so that I can add the data to a bigger database. Any help would be appreciated, and I'd be happy to give more info about the project if needed!

1 Answer 1

8

Here's a general framework that you could use:

  1. Use reactiveValues() to set up a reactive data.frame with columns for x, y, inputs
  2. Create a plot using the reactive data.frame with plotting characteristics based on input
  3. Upon clicking plot, add a new row to the reactive data.frame using observeEvent
  4. (Optional) Add an actionButton to remove the last added point

A simplified example based on your code is below. The table is based on this answer.

enter image description here

library(shiny)
library(ggplot2)

ui <- pageWithSidebar(
    headerPanel("Example"),
    sidebarPanel(
        radioButtons("color", "Pick Color", c("Pink", "Green", "Blue")),
        selectInput("shape", "Select Shape:", c("Circle", "Triangle"))
    ),
    mainPanel(
        fluidRow(column(width = 6,
                        h4("Click plot to add points"),
                        actionButton("rem_point", "Remove Last Point"),
                        plotOutput("plot1", click = "plot_click")),
                 column(width = 6,
                        h4("Table of points on plot"),
                        tableOutput("table")))
    )
)

server = function(input, output){

    ## 1. set up reactive dataframe ##
    values <- reactiveValues()
    values$DT <- data.frame(x = numeric(),
                            y = numeric(),
                            color = factor(),
                            shape = factor())

    ## 2. Create a plot ##
    output$plot1 = renderPlot({
       ggplot(values$DT, aes(x = x, y = y)) +
            geom_point(aes(color = color,
                           shape = shape), size = 5) +
            lims(x = c(0, 100), y = c(0, 100)) +
            theme(legend.position = "bottom") +
            # include so that colors don't change as more color/shape chosen
            scale_color_discrete(drop = FALSE) +
            scale_shape_discrete(drop = FALSE)
    })

    ## 3. add new row to reactive dataframe upon clicking plot ##
    observeEvent(input$plot_click, {
        # each input is a factor so levels are consistent for plotting characteristics
        add_row <- data.frame(x = input$plot_click$x,
                              y = input$plot_click$y,
                              color = factor(input$color, levels = c("Pink", "Green", "Blue")),
                              shape = factor(input$shape, levels = c("Circle", "Triangle")))
        # add row to the data.frame
        values$DT <- rbind(values$DT, add_row)
    })

    ## 4. remove row on actionButton click ##
    observeEvent(input$rem_point, {
        rem_row <- values$DT[-nrow(values$DT), ]
        values$DT <- rem_row
    })

    ## 5. render a table of the growing dataframe ##
    output$table <- renderTable({
        values$DT
    })
}

shinyApp(ui, server)
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks so much! This was a huge step in the right direction. One question I still have is if it's possible to have values$DT plot over points from another data frame. Is it as simple as just adding another layer to the other ggplot and specifying the aesthetics to use?
You're welcome! It's definitely possible -- you have the right idea. Try something like this: ggplot(cars, aes(x = speed, y = dist)) + geom_point() + geom_point(data = values$DT, size = 5, aes(x = x, y = y, color = color, shape = shape)) ...
There seems to be an issue when I do that. The first part of the plot is based on a data frame (the cars in the example you just gave), and it relies on input from selectInput(). When I don't add the extra geom_point() everything works well. However, adding the geom_point() results in an error of Aesthetics must be either length 1 or the same as the data (50): x, y, shape, colour, group appearing in the app. Any ideas why that happens? Edit: spelling
@rossdrucker9 it's hard for me to tell without seeing your code, as there are many ways to get this error. It might be easiest if you open a new question
@HallieSwan thanks so much for this, it is very helpful! Is there a way to reset (ie clear) the plot and dataframe after the user has plotted a given number of points? like if I wanted for the plot and dataframe to reset after the user has clicked 10 times, how would I do that?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.