R ggplot 或plotly 的用户输入没有闪亮

发布于 2025-01-11 18:10:14 字数 866 浏览 0 评论 0原文

我有一个带有简单散点图(例如地图)的 Rmarkdown,我希望用户能够通过输入提供一些任意 xy 坐标将它们绘制在图表上(在下面的示例中以红色显示)。问题是,我没有闪亮的服务器,所以我不能依赖这个选项。是否有一个实现,例如,通过 javascript 或其他东西?

这就是我所拥有的:

---
title: "Untitled"
output: html_document
---

```{r setup, include=FALSE}
library(ggplot2)
library(plotly)
```

```{r fig.height=4, fig.width=4}
X <- data.frame(x = 1:10, y = 1:10)
gg <- ggplot(X, aes(x, y)) + geom_point()
ggplotly(gg)
```

这就是我正在寻找的:

在此处输入图像描述


编辑
上面的例子是一个简化。实际上,网格是360x240,坐标只能是整数。

编辑2 @JohanRosa 已经通过完全在plotly.js 上重建绘图提供了一个很好的答案。然而,我的 ggplot 实际上相当复杂,而且我有很多。因此,将它们重建到plotly.js 对我来说是相当复杂的。这就是我正在寻找一种可以直接在我拥有的 ggplot(ly) 上工作的解决方案的原因。

I have an Rmarkdown with a simple scatter plot (a map for instance), and I would like users to be able to provide some arbitrary x and y coordinates via an input and have those plotted on the graph (in red in the example below). The problem is, I don't have a shiny server so I cannot rely on that option. Is there a implement this, for instance, via javascript or something?

This is what I have:

---
title: "Untitled"
output: html_document
---

```{r setup, include=FALSE}
library(ggplot2)
library(plotly)
```

```{r fig.height=4, fig.width=4}
X <- data.frame(x = 1:10, y = 1:10)
gg <- ggplot(X, aes(x, y)) + geom_point()
ggplotly(gg)
```

This is what I am looking for:

enter image description here


Edit
The example above is a simplification. In reality, the grid is 360x240 and the coordinates can only be integers.

Edit 2
@JohanRosa already provided a nice answer by rebuilding the plot entirely on plotly.js. However, my ggplot is in fact quite complexe and I have many of them. It would therefore be quite complicated for me to rebuild each of them into plotly.js. This is the reason I am looking for an solution that can work directly on the ggplot(ly) that I have.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

⊕婉儿 2025-01-18 18:10:14

我们可以使用 htmlwidgets::onRender 将自定义 JS 代码注入到您的 ggplotly 对象中。

我重用了 @JohanRosa 的输入(谢谢!+1),并向容器 div 提供了一个 id 以监听输入。此外,我使用 Plotly.restyle 来避免重绘绘图。

请检查以下内容:

---
title: "ggplotly user inputs"
output: html_document
---
    
:::{#inputcontainerid .input-container}

:::{.xs}
### X coordinate
<input type='number' value=5 id='x1' class='x'>
<input type='number' value=2.5 id='x2' class='x'>
<input type='number' value=7.5 id='x3' class='x'>
:::

:::{.ys}
### Y coordinate
<input type='number' value=10 id='y1'>
<input type='number' value=5 id='y2'>
<input type='number' value=2.5 id='y3'>
:::

:::

<!-- css configuration to arrange the inputs -->
```{css, echo = FALSE}
input {
  display: block;
}

.xs, .ys {
  display: inline-block;
}
```

```{r setup, include=FALSE}
library(ggplot2)
library(plotly)
library(htmlwidgets)
```

```{r out.width='100%', echo=FALSE}
X <- data.frame(x = 1:10, y = 1:10)

JS <- "
function(el, x){
  var id = el.getAttribute('id');
  var gd = document.getElementById(id);
  
  let defaultInputs = {
    x: [$('#x1').val(), $('#x2').val(), $('#x3').val()],
    y: [$('#y1').val(), $('#y2').val(), $('#y3').val()],
    mode: 'markers',
    type: 'scatter',
    name: 'user'
  };
  
  Plotly.addTraces(gd, defaultInputs);
  
  document.getElementById('inputcontainerid').addEventListener('input', function(event){
    let userInputs = {
      x: [[$('#x1').val(), $('#x2').val(), $('#x3').val()]],
      y: [[$('#y1').val(), $('#y2').val(), $('#y3').val()]]
    };
    Plotly.restyle(gd, userInputs, 1);
  });
}
"

gg <- ggplot(X, aes(x, y)) + geom_point()
ggplotly(gg) %>% 
  layout(xaxis = list(autorange = TRUE), yaxis = list(autorange = TRUE)) %>%
  onRender(jsCode = JS)
```

result

有关其他信息,请参阅第 5 章“JavaScript 中的事件处理” ”来自卡森Sievert 的书使用 R、plotly 和闪亮的基于网络的交互式数据可视化

We can use htmlwidgets::onRender to inject custom JS code into your ggplotly object.

I reused @JohanRosa's inputs (thanks! +1) and provided an id to the container div to listen on the inputs. Furthermore I'm using Plotly.restyle to avoid redrawing the plot.

Please check the following:

---
title: "ggplotly user inputs"
output: html_document
---
    
:::{#inputcontainerid .input-container}

:::{.xs}
### X coordinate
<input type='number' value=5 id='x1' class='x'>
<input type='number' value=2.5 id='x2' class='x'>
<input type='number' value=7.5 id='x3' class='x'>
:::

:::{.ys}
### Y coordinate
<input type='number' value=10 id='y1'>
<input type='number' value=5 id='y2'>
<input type='number' value=2.5 id='y3'>
:::

:::

<!-- css configuration to arrange the inputs -->
```{css, echo = FALSE}
input {
  display: block;
}

.xs, .ys {
  display: inline-block;
}
```

```{r setup, include=FALSE}
library(ggplot2)
library(plotly)
library(htmlwidgets)
```

```{r out.width='100%', echo=FALSE}
X <- data.frame(x = 1:10, y = 1:10)

JS <- "
function(el, x){
  var id = el.getAttribute('id');
  var gd = document.getElementById(id);
  
  let defaultInputs = {
    x: [$('#x1').val(), $('#x2').val(), $('#x3').val()],
    y: [$('#y1').val(), $('#y2').val(), $('#y3').val()],
    mode: 'markers',
    type: 'scatter',
    name: 'user'
  };
  
  Plotly.addTraces(gd, defaultInputs);
  
  document.getElementById('inputcontainerid').addEventListener('input', function(event){
    let userInputs = {
      x: [[$('#x1').val(), $('#x2').val(), $('#x3').val()]],
      y: [[$('#y1').val(), $('#y2').val(), $('#y3').val()]]
    };
    Plotly.restyle(gd, userInputs, 1);
  });
}
"

gg <- ggplot(X, aes(x, y)) + geom_point()
ggplotly(gg) %>% 
  layout(xaxis = list(autorange = TRUE), yaxis = list(autorange = TRUE)) %>%
  onRender(jsCode = JS)
```

result

For additional infos please see chapter 5 "Event handling in JavaScript" from Carson Sievert's book Interactive web-based data visualization with R, plotly, and shiny.

靖瑶 2025-01-18 18:10:14

我直接在 JavaScript 中使用plotlty.js 完成了它。我认为有了这个你就可以进步。

---
output: html_document
---

<!-- This is a container for your inputs -->

:::{.input-container}

:::{.xs}
### X coordinate
<input type='number' value=5 id='x1' class='x'>
<input type='number' value=2.5 id='x2' class='x'>
<input type='number' value=7.5 id='x3' class='x'>
:::

:::{.ys}
### Y coordinate
<input type='number' value=10 id='y1'>
<input type='number' value=5 id='y2'>
<input type='number' value=2.5 id='y3'>
:::

:::

<!-- 
I did it using a submit button, I have to read more to make it totally reactive
-->
<input type='button' id='plot' value='Update points' class='btn btn-primary'>


<!-- The next div is a placeholder for the plot -->
<div id="tester" style="width:600px;height:250px;"></div>

<!-- You have to include the plolty.js script -->
<script src="https://cdn.plot.ly/plotly-2.9.0.min.js"></script>


<!-- css configuration to arrange the inputs -->
```{css, echo = FALSE}
input {
  display: block;
}

.xs, .ys {
  display: inline-block;
}
```

<!-- This is the magic, the Js code -->
<!-- language: lang-js -->
```{js, echo=FALSE}

// Get the html element that should contain the plot
plot = document.getElementById('tester');

// Create an object with the default data
let var1 = {
  x: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
  x: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
  mode: 'markers',
  type: 'scatter',
  name: 'deafault'
}


let layout = {
  xaxis: {
    range: [ 0, 10.5 ]
  },
  yaxis: {
    range: [0, 10.5]
  },
  title:'Testing'
};

let data = [var1]

// Default plot
Plotly.newPlot(plot, data, layout);


// Using jQuery add an event listener on click to de button
// that way when the user click on it the plot will be updated
$('#plot').click(function(){
  let userInputs = {
    x: [$('#x1').val(), $('#x2').val(), $('#x3').val()],
    y: [$('#y1').val(), $('#y2').val(), $('#y3').val()],
    mode: 'markers',
    type: 'scatter',
    name: 'user'
  }
  
  data = [var1, userInputs] 
  
  Plotly.newPlot(plot, data, layout);
})
``` 

输入图片此处描述

I did it using plotlty.js directly in JavaScript. I think with this you can advance.

---
output: html_document
---

<!-- This is a container for your inputs -->

:::{.input-container}

:::{.xs}
### X coordinate
<input type='number' value=5 id='x1' class='x'>
<input type='number' value=2.5 id='x2' class='x'>
<input type='number' value=7.5 id='x3' class='x'>
:::

:::{.ys}
### Y coordinate
<input type='number' value=10 id='y1'>
<input type='number' value=5 id='y2'>
<input type='number' value=2.5 id='y3'>
:::

:::

<!-- 
I did it using a submit button, I have to read more to make it totally reactive
-->
<input type='button' id='plot' value='Update points' class='btn btn-primary'>


<!-- The next div is a placeholder for the plot -->
<div id="tester" style="width:600px;height:250px;"></div>

<!-- You have to include the plolty.js script -->
<script src="https://cdn.plot.ly/plotly-2.9.0.min.js"></script>


<!-- css configuration to arrange the inputs -->
```{css, echo = FALSE}
input {
  display: block;
}

.xs, .ys {
  display: inline-block;
}
```

<!-- This is the magic, the Js code -->
<!-- language: lang-js -->
```{js, echo=FALSE}

// Get the html element that should contain the plot
plot = document.getElementById('tester');

// Create an object with the default data
let var1 = {
  x: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
  x: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
  mode: 'markers',
  type: 'scatter',
  name: 'deafault'
}


let layout = {
  xaxis: {
    range: [ 0, 10.5 ]
  },
  yaxis: {
    range: [0, 10.5]
  },
  title:'Testing'
};

let data = [var1]

// Default plot
Plotly.newPlot(plot, data, layout);


// Using jQuery add an event listener on click to de button
// that way when the user click on it the plot will be updated
$('#plot').click(function(){
  let userInputs = {
    x: [$('#x1').val(), $('#x2').val(), $('#x3').val()],
    y: [$('#y1').val(), $('#y2').val(), $('#y3').val()],
    mode: 'markers',
    type: 'scatter',
    name: 'user'
  }
  
  data = [var1, userInputs] 
  
  Plotly.newPlot(plot, data, layout);
})
``` 

enter image description here

泪意 2025-01-18 18:10:14

这可能不是您想要的,但您可以通过在 yaml 中添加闪亮的运行时来做到这一点

---
title: "Untitled"
output: html_document
runtime: shiny
---

```{r setup, include=FALSE}
library(ggplot2)
library(plotly)
library(shiny)
```

```{r shinyInputs}

shiny::numericInput('someInput', "Some Number", value = 5)
shiny::numericInput('someInput2', "Some Number2", value = 2)

plotlyOutput('gg')

```

```{r fig.height=4, fig.width=4}
X <- data.frame(x = 1:10, y = 1:10)

output$gg <- renderPlotly({
  temp <- tibble::tibble(x = input$someInput, y = input$someInput2)
  ggplotly(ggplot(X, aes(x, y)) + geom_point() + geom_point(data = temp, aes(x =         
x, y = y), color = 'red'))
  })
```

This may not be what you want but you can do this by adding a runtime of shiny in your yaml

---
title: "Untitled"
output: html_document
runtime: shiny
---

```{r setup, include=FALSE}
library(ggplot2)
library(plotly)
library(shiny)
```

```{r shinyInputs}

shiny::numericInput('someInput', "Some Number", value = 5)
shiny::numericInput('someInput2', "Some Number2", value = 2)

plotlyOutput('gg')

```

```{r fig.height=4, fig.width=4}
X <- data.frame(x = 1:10, y = 1:10)

output$gg <- renderPlotly({
  temp <- tibble::tibble(x = input$someInput, y = input$someInput2)
  ggplotly(ggplot(X, aes(x, y)) + geom_point() + geom_point(data = temp, aes(x =         
x, y = y), color = 'red'))
  })
```
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文