Interactive drag-and-drop line chart in R/Shiny

I have developed a unique R/Shiny application that utilizes linear regression to forecast various metrics.

To enhance the interactivity of this app, I aim to incorporate a dynamic line chart, enabling users to manipulate the data points on the chart, record these changes, and predict values accordingly.

In essence, I am seeking a solution similar to the one showcased in this example within the realm of RShiny. Can anyone provide guidance on how to accomplish this task?

Answer №1

If you want to create an interactive visualization using R/Shiny and d3.js, check out the example below for a step-by-step guide along with code snippets.

Note from December 2018: Take into account MrGrumble's comment about changes in events when using d3 v5.

How to Reproduce:

To replicate this project, simply clone the repository from here.

Preview:

Apologies for the low quality of the gif, but you can still get an idea from it: https://i.sstatic.net/lUgeT.gif

Explanation:

The implementation involves combining d3.js, Shiny, and R. A custom shiny function called renderDragableChart() is utilized to allow customization of circle colors and sizes. Check out the details in DragableFunctions.R.

R-to-d3.js Interaction:

Initially, the data points' locations are set in R as demonstrated in server.R:

df <- data.frame(x = seq(20,150, length.out = 10) + rnorm(10)*8,
                 y = seq(20,150, length.out = 10) + rnorm(10)*8)
df$y[1] = df$y[1] + 80

The graphics rendering is managed through d3.js, including features like draggable points and updating changes back to R via

Shiny.onInputChange("JsData", coord);
.

The Code Structure:

ui.R:

This contains a customized shiny function called DragableChartOutput(), defined in DragableFunctions.R. Here's how it looks:

library(shiny)
shinyUI( bootstrapPage( 
  fluidRow(
    column(width = 3,
           DragableChartOutput("mychart")
    ),
    column(width = 9,
           verbatimTextOutput("regression")
    )
  )
))

server.R:

In addition to basic shiny functionality, there's a special function named renderDragableChart(). Here's a snippet from the server script:

library(shiny)
options(digits=2)
df <- data.frame(x = seq(20,150, length.out = 10) + rnorm(10)*8,
                 y = seq(20,150, length.out = 10) + rnorm(10)*8)
df$y[1] = df$y[1] + 80
...

The specific functions are stored in DragableFunctions.R for modularity. While using library(htmlwidgets) could simplify the process, opting for the detailed approach helps in understanding the nuances better.

library(shiny)
...remaining code...

To delve deeper into the dynamic behavior powered by d3.js, refer to ChartRendering.js. It showcases the circle generation and drag functionalities, ensuring real-time updates to R upon dragging completion.

var col = "orange";
...d3.js code snippet...

Answer №2

This interactive feature can also be achieved using dynamic shapes in plotly:

library(plotly)
library(purrr)
library(shiny)

ui <- fluidPage(
  fluidRow(
    column(5, verbatimTextOutput("summary")),
    column(7, plotlyOutput("p"))
  )
)

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

  rv <- reactiveValues(
    x = mtcars$mpg,
    y = mtcars$wt
  )
  grid <- reactive({
    data.frame(x = seq(min(rv$x), max(rv$x), length = 10))
  })
  model <- reactive({
    d <- data.frame(x = rv$x, y = rv$y)
    lm(y ~ x, d)
  })

  output$p <- renderPlotly({
    # creates a list of circle shapes from x/y data
    circles <- map2(rv$x, rv$y, 
      ~list(
        type = "circle",
        # anchor circles at (mpg, wt)
        xanchor = .x,
        yanchor = .y,
        # give each circle a 2 pixel diameter
        x0 = -4, x1 = 4,
        y0 = -4, y1 = 4,
        xsizemode = "pixel", 
        ysizemode = "pixel",
        # other visual properties
        fillcolor = "blue",
        line = list(color = "transparent")
      )
    )

    # plot the shapes and fitted line
    plot_ly() %>%
      add_lines(x = grid()$x, y = predict(model(), grid()), color = I("red")) %>%
      layout(shapes = circles) %>%
      config(edits = list(shapePosition = TRUE))
  })

  output$summary <- renderPrint({a
    summary(model())
  })

  # update x/y reactive values in response to changes in shape anchors
  observe({
    ed <- event_data("plotly_relayout")
    shape_anchors <- ed[grepl("^shapes.*anchor$", names(ed))]
    if (length(shape_anchors) != 2) return()
    row_index <- unique(readr::parse_number(names(shape_anchors)) + 1)
    pts <- as.numeric(shape_anchors)
    rv$x[row_index] <- pts[1]
    rv$y[row_index] <- pts[2]
  })

}

shinyApp(ui, server)

https://i.sstatic.net/lbBj4.gif

Answer №3

If you want to achieve this, the rAmCharts4 package (with or without Shiny) can help. In this example, I create a cubic regression line for two sets of values.

Asym = 5; R0 = 1; lrc = -3/4
x <- seq(-.3, 5, len = 21)
y0 <- Asym + (R0-Asym) * exp(-exp(lrc)* x)

dat <- data.frame(
  x = x,
  y1 = y0 + rnorm(21, sd = 0.33),
  y2 = y0 + rnorm(21, sd = 0.33) + 2
)

amScatterChart(
  data = dat,
  width = "800px",
  height = "600px",
  xValue = "x",
  yValues = c("y1", "y2"),
  trend = list("_all" = list(
    method = "lm.js", 
    order = 3,
    style = amLine()
  )),
  draggable = TRUE,
  pointsStyle = list(
    y1 = amTriangle(
      width = 12,
      height = 12,
      strokeColor = "yellow",
      strokeWidth = 1
    ),
    y2 = amTriangle(
      width = 12,
      height = 12,
      strokeColor = "chartreuse",
      strokeWidth = 1,
      rotation = 180
    )
  ),
  chartTitle = amText(text = "Regression model"),
  xAxis = "x",
  yAxis = "y",
  Xformatter = "#.###",
  Yformatter = "#.",
  theme = "kelly",
  zoomButtons = TRUE)

https://i.sstatic.net/U7jlF.gif

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Store and Persist Data for a Model in MongoDB

Currently working through the MongoDB and Mongoose section on FreeCodeCamp. The challenge involves creating a document instance using the Person constructor previously built. The object passed to the constructor should have fields for name, age, and favor ...

Executing sendkeys in Python with Selenium WebDriver to interact with AngularJS applications

I'm encountering an issue when trying to use sendkeys on a specific element, as it consistently displays the error message: "selenium.common.exceptions.ElementNotInteractableException: Message: Element is not visible". This is the related HTML code: ...

Incorporate Data into Table Rows inside the Material-UI Accordion

I am tasked with creating a table filled with two mock data accordions and one accordion containing database data using ReactJS in Material-UI. I have successfully implemented an accordion with the table data, but I am struggling to display only the table ...

Using the ui-router to repeatedly call an AngualrJS directive

I've encountered an issue with my HTML audio player in AngularJS. When the page is refreshed, everything works perfectly - I can set the source and play audio without any problems. However, if I navigate to another state in the app and then try to loa ...

Combining logistic lasso regression with aggregation techniques in glmnet

When using the glm() function, you can model bernoulli [0,1] outcomes with logistic regression by specifying the family as "binomial" in the syntax below: glm(bin ~ x, df, family = "binomial") Additionally, you can conduct aggregated binomial re ...

Disabling dates in the second datetimepicker depending on following days selected in the first one

I have implemented the bootstrap date picker and I am using two textboxes for searching by date range. I want the second textbox to display the days after the date selected in the first textbox. Any suggestions would be appreciated. Here is the HTML code: ...

Maintaining Scene Integrity in THREE.JS: Tips for Handling Window Resizing

My layout includes a div with a scene that looks great initially; however, as soon as I start moving or resizing the window, the scene overflows the boundaries of the div and goes haywire, almost filling the entire window. Are there any techniques or solu ...

Unable to append HTML table row to designated table

I am trying to populate an HTML table with the JSON string data I receive. The object data seems correct, but for some reason, it is not getting appended to the table. Can you help me identify what's wrong with my jQuery append statement? functio ...

Tips for organizing variables on the x-axis in a bar plot using R

I am having trouble finding an example on how to plot this specific data frame. Despite my search efforts, I have yet to find a perfect match. x <- data.frame(a=c(1:4), b=c(5:8), c=c(9:12), d=c(13:16)) r ...

Introducing navigation enhancements for browsing through thumbnail and larger image galleries with the inclusion of Next and Previous

I need assistance with adding next and previous buttons to both the thumbnail and larger image gallery. These buttons should also support keyboard event listeners. Here is the link I have attempted: http://jsfiddle.net/L7yKp/36/ Any help would be greatl ...

The issue arises when a continuous use of angularjs directives linked with an external template fails to display correctly upon the addition of new

In the code snippet below, you'll find a fiddle that displays 3 columns of numbers that increase in value. These columns represent directives with different templates: one inline, one preloaded, and one from an external template. When you click on the ...

A type guard for generics in TypeScript

I'm dealing with a variable that can be either of type C1[] or C2<C1>[]. How can I create a type guard for this variable? interface C<T>{ key: string; secret: T; } private isC(d: Foo[] | C<Foo>): d is C<Foo>[] { ret ...

Error: Execution of 'texImage2D' on 'WebGLRenderingContext' failed due to overload resolution issues

I am currently working on developing a 3D customizer by using fabricjs as the canvas texture for my three js model. The method I use to load the obj file is as follows: function loadObj() { canvasTexture.anisotropy = renderer.capabilities.getMaxAnisotr ...

When transferring files to the src/pages directory in Next.js, the custom _app and _document components are not recognized

The documentation for Next.js mentions that the src/pages directory can be used as an alternative to /pages. However, I encountered a problem when moving my custom _app.tsx and _document.tsx files into the src folder, as they were being ignored. If you cr ...

Error Encountered with WebKitSubtleCrypto while utilizing dispatchMessage within a Safari Extension

I'm currently working on developing a Safari extension that allows users to share screenshots of webpages. However, I've encountered an issue when trying to pass the image back to Swift - it triggers an error that causes Safari to become unstable ...

Convert the button element to an image

Can someone please explain how to dynamically change a button element into an image using javascript when it is clicked? For instance, changing a "Submit" button into an image of a check mark. ...

Create a custom countdown using Jquery and Javascript that integrates with MySQL and PHP to display the datetime in the format Y-m-d

Hey there, I'm looking to create a countdown script using dates in the format (Y-m-d H:m:s). The goal is to retrieve the current_datetime and expire_datetime in this format and incorporate them into some JavaScript or jQuery plugin to display a countd ...

Tips for troubleshooting an Angular error when no specific information is provided

I'm encountering an error `ERROR Error: "[object Object]" in my console and my app is displaying a white screen. Everything was working perfectly fine before, and I can't pinpoint any changes that may have caused this issue. The error appears to ...

Load website content in real-time

My website requires dynamic content to be loaded on user interaction. When a user clicks certain elements on the page, information should be retrieved from a file located in the same directory as the webpage and displayed in a designated <div>. My u ...

Notification of Leaf Name in d3.js

I am trying to display the leaf name when it is clicked, but I am unsure how to do it. I am new to D3 and would appreciate any guidance on how to achieve this. Source: http://bl.ocks.org/mbostock/7607535 var circle = svg.selectAll("circle") .data(nod ...