Shiny 反应式

概述

之前我们描述了如何使用 ojs_define() 函数将 Python 和 R 中的数据提供给 OJS 单元格使用。在这种情况下,数据预处理在渲染时只进行一次,随后的所有交互都在客户端处理。

但是如果你想根据用户输入动态地进行数据转换呢?这也是可能的,因为 ojs_define() 不仅可以传递静态值,还可以传递 Shiny 反应式(假设它在 Shiny 交互文档 中运行)。

你好,Shiny

以下是 Shiny Gallery 中的 K-Means 聚类 示例,通过 OJS 客户端和 Shiny 服务器实现:

你可以在 https://jjallaire.shinyapps.io/kmeans-shiny-ojs/ 查看部署的文档。

源代码

我们来看一下源代码。在客户端,我们有看起来熟悉的 OJS 输入和一个使用 panel: sidebarpanel: fill 布局的图表:

```{ojs}
//| panel: sidebar
vars = ["Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width"]
viewof xcol = Inputs.select(vars, {label: "X 变量"})
viewof ycol = Inputs.select(vars, {label: "Y 变量", value: vars[1]})
viewof count = Inputs.range([1, 9], {label: "聚类数量", step: 1, value: 3})
```

```{ojs}
//| panel: fill
Plot.plot({
  color: {
    type: "ordinal",
    scheme: "category10"
  },
  marks: [
    Plot.dot(transpose(selectedData), {
      x: xcol,
      y: ycol,
      fill: (d, i) => clusters.cluster[i],
    }),
    Plot.dot(clusters.centers, { 
      x: d => d[0],
      y: d => d[1],
      r: 10,
      stroke: "black",
      fill: (d, i) => i + 1
    }),
  ]
})
```

注意绘图代码引用了变量 selectedDataclusters。这些将由 Shiny 服务器代码中的反应式表达式提供。还要注意我们使用 transpose() 函数将数据重塑为 Plot 库预期的行格式。

以下是服务器代码:

```{r}
#| context: server

selectedData <- reactive({
  iris[, c(input$xcol, input$ycol)]
})

clusters <- reactive({
  kmeans(selectedData(), input$count)
})

ojs_define(selectedData, clusters)
```

我们通过 context: server 选项指定这段代码在服务器上运行。

注意我们引用了客户端通过 viewof 表达式定义的几个输入(例如 input$xcol)。当这些输入改变时,它们将导致服务器端相应的反应式重新执行。

我们创建了两个反应式值(selectedDataclusters),并使用 ojs_define() 将它们提供给客户端。当这些值改变时,图表将自动在客户端重新绘制。

示例

以下是一些展示了使用 OJS 与 Shiny 的各种方式的示例:

示例 源代码 描述
K-Means 代码 简单示例,展示如何将 OJS 输入绑定到 Shiny 输入,并将 Shiny 反应式绑定到 OJS 图表。
Binning 代码 演示在服务器上对中等大小数据集(32mb)进行快速分箱。
数据绑定 代码 演示如何从 https://observablehq.com 导入笔记本,并将其数据字段绑定到 Shiny 反应式。

绑定

OJS 到 Shiny

在上面的示例中,我们利用了默认情况下 OJS viewof 表达式会自动传播到 Shiny 输入(例如 input$xcol)的事实。这提供了合理的关注点分离,并防止在具有大型 OJS 变量的情况下产生过多的网络流量。

然而,如果你想使用其他 OJS 变量作为 Shiny 输入,这也是可能的,使用 ojs-export 选项即可。默认行为映射到以下配置:

---
server:
  type: shiny
  ojs-export: viewof
---

你也可以指定 ojs-export: all 以使所有 OJS 反应式绑定到 Shiny 输入:

---
server:
  type: shiny
  ojs-export: all
---

或者,你可以通过名称指定一个OJS反应列表(包括使用~来过滤出反应),并可选择将其与viewof和/或all选项结合使用。例如:

---
server:
  type: shiny
  ojs-export: [all, ~large_dataset]
---

Shiny到OJS

不太常见但偶尔有用的是将Shiny输入绑定到OJS的能力。默认情况下不会发生此类绑定,但你可以使用ojs-import选项选择特定的Shiny输入。例如:

---
server: 
  type: shiny
  ojs-import: [minimum, maximum]
---