以指数格式绘制轴

Plotly axis as exponential format

这是对这个问题的跟进:

上述问题的答案使用 JavaScript 代码将轴刻度变为指数。它适用于单个图上的单个轴。

当 运行 它在 subplot() 结构中时,当我将它应用于最终情节时,它仅适用于第一个情节。

我正在寻找的修改是这些:

1:如何让它在子图中工作。我尝试在每个子图的构建中调用 JavaScript,但是所有的图都没有指数。

2:让它在 x 轴和 y 轴上都起作用(我试过了,失败了,哭了一点)

3: 当坐标轴不是数值时,让它不破坏宇宙(我的应用程序也可以绘制日期列,所以它需要检查它是否真的是一个数值输入)

4:如果可能打印为 1.23E+1 而不是 1E+1

 library(shiny)
library(plotly)
library(htmlwidgets)


ui <- fluidPage(
    mainPanel(
     plotlyOutput('myplotly')
     )
  )

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

  javascript <- "
function(el, x) 
{
  function fix_ticks()
  {
    ticks = Plotly.d3.selectAll('g.ytick').selectAll('text');
    ticks.forEach(function(tick) 
    {
      var num = parseInt(tick[0].innerHTML); 
      tick[0].innerHTML = num.toExponential();
    })
  }
  el.on('plotly_afterplot', fix_ticks);
}"


  output$myplotly <- renderPlotly({

    myplots <- lapply(unique(mtcars$cyl), function(v) {

    data <- mtcars[which(mtcars$cyl == v), ] 
      subp <-   plot_ly(data = data,
                x = rownames(data), 
                y = ~mpg,
                type = "bar")
      subp
     })

    p <- subplot(myplots, margin = 0.08)

    p$elementId <- NULL   ## to surpress warning of widgetid
    p <- onRender(p, javascript)

    p <- p %>% layout(margin =list(l = 60, r = 20, b = 160, t = 70) )

    p
  })
 }

shinyApp(ui = ui, server = server)

更新 有了这里给出的答案建议,我现在 运行 以下代码检查数值,然后适用于每个轴。我还没有得到最后一个处理负值的建议,它使用的是 tick[0].innerHTML。我最终使用了第 1 点答案下的建议,因为我无法使用 tick[0].innerHTML 方法在 forEach 循环内工作。但是,针对早期评论的建议是回到 tick[0] 方法,我无法让一种方法完全起作用。

这是我在 运行 进入否定 -> NaN 问题之前最终使用的代码

  javascriptMPP <- "
  function(el, x) 
  {
    function isNumber(n) {
      return (Object.prototype.toString.call(n) === '[object Number]' || Object.prototype.toString.call(n) === '[object String]') &&!isNaN(parseFloat(n)) && isFinite(n.toString().replace(/^-/, ''));
    }
    function fixTicks()
    {
      ticks = Plotly.d3.selectAll('g.yaxislayer-above,g.xaxislayer-above').selectAll('text');
      ticks.each(function(d) 
      {
        if(parseInt(d.text) !== 0 )
        {
          var num = parseInt(d.text).toExponential(2);
          Plotly.d3.select(this).text(num);
        }
      })
    }
    el.on('plotly_afterplot', fixTicks);
  }"

1: How to make it work on subplots. I tried calling java in the build of each subplot, but all plots came out without exponential then.

第二个/第三个/等等的刻度。子图有 class 个名称,例如 y2tick/y3tick/等。我们可以让我们的 d3 选择器不那么具体,然后使用 each 来改变所有的刻度。

ticks = Plotly.d3.selectAll('g.yaxislayer-above').selectAll('text');
ticks.each(function(d, i) 
{
  var num = parseInt(d.text).toExponential();
  Plotly.d3.select(this).text(num);
})

2: make it work for both x an y axis (I tried, failed and cried a little)

只要把selectAll语句改成Plotly.d3.selectAll('g.xaxislayer-above').selectAll('text')

3: Make it not destroy the universe when the axis turns out to not be a numerical (My app can plot date columns too, so it needs to check whether it actually is a numerical input)

您可以更改 fixTicks 函数来检查输入值是否为数字,例如通过使用 typeof 或正则表达式。使用 1999、2000 等值可能会很棘手,您需要手动解决它。

4: If possible print as 1.23E+1 rather than 1E+1

toExponential 需要一个 parameter,即 "number of digits in the notation after the decimal point",即 num.toExponential(3) 可以解决您的问题。

From the comment: I seem to get NaN when the values on the ticks are negative values

Plotly 使用 Unicode minus sign 而不是常规破折号。您可以将其替换为以下 JavaScript 行:

var num = parseInt(tick[0].innerHTML.replace(/\u2013|\u2014|\u2212/g, '-'));

注意:R 中需要双反斜杠 \,纯 JavaScript 只需要一个 \.

感谢此处发布的其他答案,我终于设法解决了所有问题。

javascript:
替换符号,因此它也适用于负值

  var num1 = parseInt(d.text.replace(/\u2013|\u2014|\u2212/g, '-'));

如果值为 0 则跳过,因为它不必更改为 exp 格式

   if(parseInt(d.text) !== 0 )

如果值是文本则跳过(在更改负数符号后,否则它们将被视为文本

 if(isNaN(num1)){

另外在plot代码中加入layout命令,将标签在x轴上以45度角放置,否则会相互重叠。 对 mtcars df 进行了一些更改,以便我们在数据中得到负数和大数。

为了在 x 轴上绘制文本值,它适用于:

x = rownames(data),  

而不是数字 x 参数

x = ~disp, 

这是工作代码:

library(shiny)
library(plotly)
library(htmlwidgets)


ui <- fluidPage(
  mainPanel(
    plotlyOutput('myplotly')
  )
)

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

  javascriptMPP <- "
  function(el, x) 
  {
  function isNumber(n) {
  return (Object.prototype.toString.call(n) === '[object Number]' || Object.prototype.toString.call(n) === '[object String]') &&!isNaN(parseFloat(n)) && isFinite(n.toString().replace(/^-/, ''));
  }
  function fixTicks()
  {
  ticks = Plotly.d3.selectAll('g.yaxislayer-above,g.xaxislayer-above').selectAll('text');
  ticks.each(function(d) 
  {
   if(parseInt(d.text) !== 0 )
  {
  var num1 = parseInt(d.text.replace(/\u2013|\u2014|\u2212/g, '-'));
  if(isNaN(num1)){
  } else {
    var num = parseInt(num1).toExponential(2);
  Plotly.d3.select(this).text(num);
  }
}
  })
  }
  el.on('plotly_afterplot', fixTicks);
  }"

  output$myplotly <- renderPlotly({
    mtcars2 <- mtcars
    mtcars2$mpg <- mtcars2$mpg*-1
    mtcars <- rbind(mtcars, mtcars2)
    mtcars3 <- mtcars
    mtcars3$mpg <- mtcars3$mpg*1000000

    mtcars <- rbind(mtcars, mtcars3)

    myplots <- lapply(unique(mtcars$cyl), function(v) {

      data <- mtcars[which(mtcars$cyl == v), ] 
      subp <-   plot_ly(data = data,
                        x = ~disp, 
                        y = ~mpg,
                        type = "bar")
      subp <- layout(subp, xaxis = list(tickangle = 45))
      subp
    })

    p <- subplot(myplots, margin = 0.08)

    p$elementId <- NULL   ## to surpress warning of widgetid
    p <- onRender(p, javascriptMPP)

    p <- p %>% layout(margin =list(l = 60, r = 20, b = 160, t = 70) )

    p
  })
}

shinyApp(ui = ui, server = server)