将JavaScript(d3.js)与Shiny绑定

41
首先,我对JavaScript及其库d3.js不太熟悉,但我熟悉R。使用Shiny创建仪表板非常有趣和容易(感谢stackoverflow)。现在我想通过将d3元素连接到它来扩展它。
我正在寻找关于如何实际绑定JavaScript到Shiny(R仪表板)的信息来源,并解释实际发生了什么。
背景: 我完成了w3schools上的js和jquery教程,并学习了一些有关使用Scott Murray的书(交互式数据可视化)的d3的知识。我希望这足以让我理解有关如何在Shiny网站上构建自定义输入/输出绑定的示例和说明。

http://shiny.rstudio.com/articles/building-inputs.html

不幸的是,我没有找到任何最小工作代码示例。许多在GitHub上的示例对我来说过于复杂,可能是因为我对JavaScript的经验太少了。以下是一个使用JavaScript进行自定义输入绑定的示例:

https://github.com/jcheng5/shiny-js-examples/tree/master/input

这是一个我尝试展示的输入和输出绑定的示例:

<script src="http://d3js.org/d3.v3.js"></script>
<script type="text/javascript">
(function(){
  // Probably not idiomatic javascript.

  this.countValue=0;

  // BEGIN: FUNCTION
  updateView = function(message) {

    var svg = d3.select(".d3io").select("svg")

    svg.append("text")
      .transition()
      .attr("x",message[0])
      .attr("y",message[1])
      .text(countValue)
      .each("end",function(){
        if(countValue<100) {
          countValue+=1;
          $(".d3io").trigger("change");
        }
      })
  }
  // END: FUNCTION

  //BEGIN: OUTPUT BINDING
  var d3OutputBinding = new Shiny.OutputBinding();
  $.extend(d3OutputBinding, {
    find: function(scope) {
      return $(scope).find(".d3io");
    },
    renderError: function(el,error) {
      console.log("Foe");
    },
    renderValue: function(el,data) {
      updateView(data);
      console.log("Friend");
    }
  });
  Shiny.outputBindings.register(d3OutputBinding);
  //END: OUTPUT BINDING

  //BEGIN: INPUT BINDING
  var d3InputBinding = new Shiny.InputBinding();
  $.extend(d3InputBinding, {
    find: function(scope) {
      return $(scope).find(".d3io");
    },
    getValue: function(el) {
      return countValue;
    },
    subscribe: function(el, callback) {
      $(el).on("change.d3InputBinding", function(e) {
        callback();
      });
    }
  });
  Shiny.inputBindings.register(d3InputBinding);
 //END: OUTPUT BINDING

})()
</script>

在ui中,"d3io"是一个div元素,updateView()是一个函数。以下是ui:

#UI
library(shiny)

d3IO <- function(inputoutputID) {
  div(id=inputoutputID,class=inputoutputID,tag("svg","")) #; eerst zat ; erbij, maar werkt blijkbaar ook zonder
}

# Define UI for shiny d3 chatter application
shinyUI(pageWithSidebar(

  # Application title
  headerPanel("D3 Javascript chatter",
              "Demo of how to create D3 I/O and cumulative data transfer"),

  sidebarPanel(
    tags$p("This widget is a demonstration of how to wire shiny direct to javascript, without any input elements."),
    tags$p("Each time a transition ends, the client asks the server for another packet of information, and adds it
            to the existing set"),
    tags$p("I can't claim this is likely to be idiomatic javascript, because I'm a novice, but it allows d3 apps
            to do progressive rendering.  In real use, a more complex request/response protocol will probably be
            required.  -AlexBBrown")
  ),

  mainPanel(
    includeHTML("d3widget.js"),
    d3IO("d3io") #Creates div element that d3 selects
    )
))

这里是服务器文件:

# SERVER
library(shiny)
# Define server logic required to respond to d3 requests
shinyServer(function(input, output) {

  # Generate a plot of the requested variable against mpg and only 
  # include outliers if requested
  output$d3io <- reactive(function() {
    if (is.null(input$d3io)) {
      0;
    } else {
      list(rnorm(1)*400+200,rnorm(1)*400+200);
    }
  })
})

具体问题:

1)server.r 似乎接收名为 "d3io"(input$d3io)的输入,因为在 ui.r 中没有定义它,我推断它必须来自 javascript 文件。它实际上指的是哪个元素?

2)我对自定义绑定部分感到困惑:

var d3OutputBinding = new Shiny.OutputBinding();
  $.extend(d3OutputBinding, {
    find: function(scope) {
      return $(scope).find(".d3io");
    },
    renderError: function(el,error) {
      console.log("Foe");
    },
    renderValue: function(el,data) {
      updateView(data);
      console.log("Friend");
    }
  });
  Shiny.outputBindings.register(d3OutputBinding);

我理解的是:
创建一个新的闪亮输出绑定,首先找到类.d3io(div元素),如果出错则写入控制台“Foe”(这是特殊代码吗?),如果没有错误则使用updateView函数使用数据渲染值(它从哪里接收该值?),并写入控制台“Friend”。最后注册输出。
希望你们能帮忙!我正在创建一份文档,介绍“当你不知道任何JavaScript时,学习如何将JavaScript实现到闪亮中所需的步骤”,我会很高兴的:)
谢谢, Long

在不了解JavaScript的情况下学习如何将JavaScript实现到Shiny的必要步骤中+1。 - Vincent
你还对如何编写包括d3.js在内的自定义绑定指南感兴趣吗? - Tonio Liebrand
4个回答

12

嗨,Sweetbabyjesus(说起来真有趣)。您有两个问题:

1)server.r似乎会接收名为“d3io”的输入(input $ d3io),因为它在ui.r中未定义,我推断它必须来自javascript文件。 它实际上指的是哪个元素?

该短语 input $ d3io 由以下组件组成:

  • input是传递到函数中的参数-它是存储应用程序中所有小部件当前值的列表。
  • $是成员选择器。
  • d3io是指在UI的mainPanel中具有该id(“d3IO(“ d3io”)”)的div元素的内容。

2)我有困难理解自定义绑定部分:

var d3OutputBinding = new Shiny.OutputBinding();

没错,这将创建一个Shiny.OutputBinding的实例并将其分配给变量d3OutputBinding。

$.extend(d3OutputBinding, {
  find: function(scope) {
    return $(scope).find(".d3io");
  },
  renderError: function(el,error) {
    console.log("Foe");
  },
  renderValue: function(el,data) {
    updateView(data);
    console.log("Friend");
  }
});

这段代码通过三个函数findrenderErrorrenderValue扩展了d3OutputBinding的行为。这三个函数是Shiny.OutputBinding所需的。

find是关键,因为它返回一个元素列表,应该通过el参数传递给两个渲染函数。请注意,它返回的元素的css类是"d3io" - 这是之前提到的相同div。

请注意,extend()是jQuery javascript库的一个函数,而此上下文中的$是jQuery对象的别名。

Shiny.outputBindings.register(d3OutputBinding);
让Shiny知道这个新配置的对象现在应该被使用。祝好,Nick。

8

我会退一步并假设您想要D3能够实现的惊人结果,但并不一定与D3绑定。本质上,我将回答这个问题:

当您不了解JavaScript时,学习如何将JavaScript实现到Shiny的必要步骤是什么?

虽然D3非常强大,但它也以难以掌握而闻名 - 即使对于许多非常熟悉JavaScript的人来说也是如此。虽然我喜欢D3并几乎每天都在使用它,但在这种情况下,我建议不要使用它。相反,有一个名为Plotly的库,它在后台使用D3,但专门为科学界和数据科学家构建,因此非常适合R社区。

他们有一个深入的教程,可以连接到Shiny,甚至有一个ggplot2转换器,如果您已经熟悉该语法,那么在R世界中的许多人都是这样。除非您的需求非常特殊,否则Plotly很可能会像直接编写D3一样满足您的需求,并具有更友好的学习曲线。


1
Plotly看起来非常用户友好,是个不错的建议。与其创建D3图像,我正在寻找如何将D3.js绑定到shiny,以便我可以利用现有的D3图像池(而无需实际创建它们),这比plotly提供的更多。我更喜欢利用直接的资源(这是雄心勃勃的)。 - Sweetbabyjesus
@warship 看起来这个问题已经不存在了。:-/ - Chris Fritz
嗨Chris,这是链接:http://stackoverflow.com/questions/34426202/downloadbutton-for-html-output-in-shiny - warship
1
Plotly的优点在于其实现非常容易,但渲染所需的时间有些慢。相比之下,D3.js要比它快得多。如今(因为这个问题已经五年了),有一个叫做R2D3的软件包,可以轻松地将R连接到D3。 - Yannik Suhre

3

你是否熟悉rCharts包?它可以与Shiny很好地配合使用,大多数输出选项都基于D3变体。这里有两个例子。


3
非常忙于工作,我没有机会发布它。请注意,这是使用customMessageHandler的解决方法(我没有使用自定义输入/输出绑定)。以下是步骤:
目标:使用customMessageHandler从数据帧发送数据以创建D3JS树状图。
路径:我已经成功地将数据框格式的数据发送到了d3js树状图中。在单击actionbutton后,它将数据框中的数据更改为JSON格式,然后将其发送到创建树状图的js文件中。树的数据在“server.r”上硬编码。
代码在哪里? 我的github上!链接如下: https://github.com/SweetBabyJesus/shiny-d3js-simple-binding 原始内容:我基于CHAID创建了一种树形算法,用于从大型数据集中创建洞察力。人们可以将其csv上传到仪表板,然后输出d3js树:) 代码有些冗长,因此我为您削减了代码并创建了一个最小的代码示例。
希望你喜欢。
谢谢, Long

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接