对数刻度上的闪亮滑块

Posted

技术标签:

【中文标题】对数刻度上的闪亮滑块【英文标题】:Shiny slider on logarithmic scale 【发布时间】:2015-08-10 17:50:45 【问题描述】:

我正在使用 Shiny 构建一个简单的 Web 应用程序,该应用程序带有 slider,用于控制应在输出中显示哪些 p 值。

如何使滑块作用于对数而不是线性比例?

目前我有:

sliderInput("pvalue",
            "PValue:",
            min = 0,
            max = 1e-2,
            value = c(0, 1e-2)
),

谢谢!

【问题讨论】:

input_slider from ggvis 有一个 map 参数。可以直接使用ggivs,或者查看map参数的实现。 我想我可以得到一个非常简单的 javascript 来解决这个问题,不应该超过几行,你能告诉我们你想要什么吗? 【参考方案1】:

这个问题没有得到更多关注的原因是它很难回答。要满足提问者的要求,您必须编写 javascript 并将其注入网页。下面是我为将shiny 滑块正确格式化为日期而调整的代码。我没有尝试将其修改为对数,因为我只学习了足够的 javascript 来修补这个脚本,然后立即将其从内存中删除,以便为更重要的事情腾出空间,比如街区下酒吧的季节性啤酒菜单。

随便:

    output$selectUI <-
        renderUI(
            list(sliderInput(inputId = "target", label = "Date",
                                             min = 0, max = diff_months(targetEnd(),targetStart()) - 1,
                                             value = diff_months(targetEnd(),targetStart()) - 1,
                                             animate = animationOptions( loop = T)),
                     singleton(html(
                '
            <script type="text/javascript">
            $(document).ready(function() 
            var monthNames = [ "Jan.", "Feb.", "Mar.", "Apr.", "May", "June",
            "July", "Aug.", "Sept.", "Oct.", "Nov.", "Dec." ];
            var endDate = new Date(',
                year(Sys.Date()),',', month(Sys.Date())
                ,', 1);
            var slider = $("#dates").slider();
            var labels = slider.domNode.find(".jslider-label span");
            labels.eq(0).text("Jan., 1962");
            labels.eq(1).text([monthNames[endDate.getUTCMonth() +1], endDate.getUTCFullYear()].join(", "));
            // override the default "nice" function.
            slider.nice = function(value) 
alert("hi")
                var ref_date = new Date(1962, 1, 1);
                var slider_date = new Date(ref_date.setMonth(ref_date.getMonth() + value));
                return [monthNames[slider_date.getUTCMonth()], slider_date.getUTCFullYear()].join(", ");
            

            $(slider).trigger("slidechange");
            )

    $(document).ready(function() 
                var monthNames = [ "Jan.", "Feb.", "Mar.", "Apr.", "May", "June",
                    "July", "Aug.", "Sept.", "Oct.", "Nov.", "Dec." ];
      var slider = $("#target").slider();
            var labels = slider.domNode.find(".jslider-label span");
          labels.eq(0).text([monthNames[', month(targetStart()) +1, '],', year(targetStart()), '].join(", "));
            labels.eq(1).text([monthNames[', month(targetEnd()) + 1, '], ', year(targetEnd()), '].join(", "));


      // override the default "nice" function.
      slider.nice = function(value) 
alert("hi")
        var ref_date = new Date(', year(targetStart()), ', ', month(targetStart()),',1 );
        // each slider step is 4 weeks, translating to 24 * 3600 * 1000 milliseconds
        var slider_date = new Date(ref_date.setMonth(ref_date.getMonth() + value - 1));

        return [monthNames[slider_date.getUTCMonth()],
                slider_date.getUTCFullYear()].join(", ");
      

            $(slider).trigger("slidechange");
    )
  </script>
            ')
            )
        )
)

【讨论】:

好吧,我想说它是问题的答案,它确实需要解决对数方面的问题! 我不这么认为。定义映射很简单(如果您知道 javascript)。困难的部分是弄清楚如何变得闪亮来做到这一点。上面的代码花了大约 4 天的时间才开始工作,其中大约 10 分钟用于创建日期格式的代码。如果那样的话。 太棒了!因此,进行这些更改以回答问题应该很容易......! 哇,我想我应该更加小心地选择我想帮助的人。祝你好运! 非常感谢您的帮助,但是如果它实际上是针对不同的问题(即使您相信差异是微妙的)。这可能是让某人兴奋的必要条件,但这不是问题的答案。如果您对其进行编辑以便可以接受/授予它,那就太棒了【参考方案2】:

我不确定你想要什么作为输出,但我所做的是让可能的 p 值为 [0, 0.00001, 0.0001, 0.001, 0.01]。如果您想要一些不同的东西,希望这个答案是一个足够好的起点。

基本上,我首先创建了一个包含数字值(0、0.0000.1、...)的数组,然后我只使用滑块 API 中的特殊更新函数来使这些值粘在滑块上.一旦您弄清楚如何使用滑块的 API,这将非常简单。此外,由于某种原因,在 RStudio 中运行它看起来很奇怪,但在浏览器中它很好。另外,请注意我有 5 毫秒的延迟,因为我想确保它在滑块初始化后运行。不是最干净的解决方案,可能有更好的方法来做到这一点,但它确实有效。

library(shiny)

JScode <-
  "$(function() 
    setTimeout(function()
      var vals = [0];
      var powStart = 5;
      var powStop = 2;
      for (i = powStart; i >= powStop; i--) 
        var val = Math.pow(10, -i);
        val = parseFloat(val.toFixed(8));
        vals.push(val);
      
      $('#pvalue').data('ionRangeSlider').update('values':vals)
    , 5))"

runApp(shinyApp(
  ui = fluidPage(
    tags$head(tags$script(HTML(JScode))),

    sliderInput("pvalue",
                "PValue:",
                min = 0,
                max = 1e-2,
                value = c(0, 1e-2)
    )
  ),
  server = function(input, output, session) 
  
))

【讨论】:

OP/赏金提问者:这是你想要的吗? 这看起来很不错,但是标签发生了一些奇怪的事情,就像它们都被连接在一起 - 有什么想法吗? 嗯,在我的浏览器上它们看起来不错(在 RStudio 中它们都聚集在一起)。在实际浏览器中对您来说看起来很奇怪?我认为这可能是因为浮点问题(如果您查看数字,它们就像“.00010000000000002”),因此解决方案是将它们四舍五入到 5 位数字或将数字构造为字符串为 10^-n 我更新了我的答案,我只是使用parseFloat(...toFixed()) 来确保它只显示前 8 个小数点。请注意,ionrangeslider 似乎确实有一个奇怪的错误,它甚至在小数点右侧插入逗号作为千位分隔符(10000 正确显示为 10,000,但 0.0001 错误显示为 0.0,001)。这是一个单独的问题,我认为这不是这个答案的责任 你是对的,但无论如何,这种变化已经对其进行了排序。这正是我所追求的,很高兴得到这个答案 - 如果现在满意,由 OP 接受【参考方案3】:

我现在没有闪亮的,我已经扩大了一点范围,如果你尝试这样会发生什么:

sliderInput("pvalue",
            "PValue:",
            min = 1e-02,
            max = 1e+02,
            value = -10^seq(-2, 2)
),

你的帖子提到1e-2,我用过1e-02,我检查如下

> 1e-2==1e-02
[1] TRUE

【讨论】:

这不起作用。 value 参数只接受一个或两个值(请参阅shiny.rstudio.com/reference/shiny/latest/sliderInput.html)。这样做的唯一方法是在比例尺上显示 log(pValue) 或其他人发布的相当简洁的 JavaScript 解决方案。【参考方案4】:

不确定该线程是否仍然处于活动状态,但以防万一想要使用 ionRangeSlider 的美化函数属性添加更通用的“记录” inputSlider 方法,而不是覆盖值,其优点是您可以定义最小值, max, step 和 inputSlider 像往常一样的默认值,然后在 Javascript 端发生的所有事情都是显示值的变化(提供了两个选项,一个用于数字输出,一个用于科学计数法):

library(shiny)

# logifySlider javascript function
JS.logify <-
"
// function to logify a sliderInput
function logifySlider (sliderId, sci = false) 
  if (sci) 
    // scientific style
    $('#'+sliderId).data('ionRangeSlider').update(
      'prettify': function (num)  return ('10<sup>'+num+'</sup>'); 
    )
   else 
    // regular number style
    $('#'+sliderId).data('ionRangeSlider').update(
      'prettify': function (num)  return (Math.pow(10, num)); 
    )
  
"

# call logifySlider for each relevant sliderInput
JS.onload <-
"
// execute upon document loading
$(document).ready(function() 
  // wait a few ms to allow other scripts to execute
  setTimeout(function() 
    // include call for each slider
    logifySlider('log_slider', sci = false)
    logifySlider('log_slider2', sci = true)
  , 5))
"

ui <- fluidPage(
  tags$head(tags$script(HTML(JS.logify))),
  tags$head(tags$script(HTML(JS.onload))),

  sliderInput("log_slider", "Log Slider (numbers):",
              min = -5, max = 3, value = -4, step = 1),

  sliderInput("log_slider2", "Log Slider (sci. notation):",
              min = -5, max = 3, value = 1, step = 0.5),

  br(),

  textOutput("readout1"),
  textOutput("readout2")
)

server <- function(input, output, session) 
  output$readout1 <- reactive(
    paste0("Selected value (numbers): ", input$log_slider, " = ", 10^input$log_slider)
  )

  output$readout2 <- reactive(
    paste0("Selected value (sci. notation): ", input$log_slider2, " = ", 10^input$log_slider2)
  )


shinyApp(ui, server)

【讨论】:

这不适用于 RStudio 浏览器,但在 chrome 和 FF 中可以 这很好用,但在 renderUI 中的服务器端创建滑块输入时效果不佳。问题是脚本在创建对象之前触发(如果我将等待时间设置得足够长,它就可以工作)。有什么好的解决办法吗? 我找到了方法。将对 JS.onload 的调用插入到渲染 UI 中就可以了。 session$sendCustomMessage(type='jsCode', list(value = JS.onload))。你在 ui 中也需要这个:tags$head(tags$script(HTML('Shiny.addCustomMessageHandler("jsCode", function(message) eval(message.value); );'))) 我还必须在 .update( ) 调用中设置 'prettify_enabled' : true【参考方案5】:

更新(2018 年 5 月):

现在可以通过shinyWidgets::sliderTextInput() 控件实现。您可以指定自定义步骤(例如,对数间隔),然后滑块逐步完成这些步骤。缺点是您需要指定每一步,而不是指定最大值和最小值,并让滑块计算步数,但它适用于此类应用程序。

小例子:

library(shiny)
ui <- fluidPage(
  shinyWidgets::sliderTextInput("pvalue2","PValue:",
                            choices=c(0, 0.0001, 0.001, 0.01),
                            selected=0, grid = T)
)
server <- function(input, output) 
shinyApp(ui, server)

【讨论】:

一个小例子会很棒! @agenis 已编辑以包含示例。感谢更正函数名称!

以上是关于对数刻度上的闪亮滑块的主要内容,如果未能解决你的问题,请参考以下文章

如何修改 KendoUI 滑块刻度上的标签?

在 iOS 滑块上,我可以单击刻度上的任意位置还是必须拖动滑块旋钮?

将线拟合到 R 中对数刻度上的数据

Teechart + 对数刻度

Matplotlib 半对数图:当范围很大时,次要刻度线消失了

移动范围拇指时动画范围刻度