Plotly axis as exponential format

匿名 (未验证) 提交于 2019-12-03 01:40:02

问题:

This is a follow up on this question: SO Q

The answer in the above question uses JavaScript code that turns an axis ticks into exponential. It works for a single axis on a single plot.

When running it in a subplot() structure, it only works on the first plot when I apply it to the final plot.

The modification I am looking for are these:

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

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

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)

4: If possible print as 1.23E+1 rather than 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) 

UPDATE with the answer suggestions given here I am now running the following code that checks for numerical and then works for each axis. I haven't been able to get the last suggestion to deal with negative values to work yet, which was using tick[0].innerHTML. I ended up using the suggestion under the answer under point 1, using, as I could not get the tick[0].innerHTML approach to work inside the forEach loop. However, the suggestion in response to earlier comment is going back to the tick[0] approach, and I can't get one approach to work completely.

This is the code I ended up using before running into negative -> NaN problems

  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:

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.

The ticks of the 2nd/3rd/etc. subplot have class names like y2tick/y3tick/etc. We could make our d3 selector less specific and then use each to change all ticks.

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)

Just change the selectAll statement to 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)

You could change your fixTicks functions to check if the input value is numeric, e.g. by using typeof or a regex. With values like 1999, 2000, etc. it might be tricky and you would need to manually address it.

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

toExponential takes one parameter which is the "number of digits in the notation after the decimal point", i.e. num.toExponential(3) would do the trick in your case.

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

Plotly uses an Unicode minus sign instead of a regular dash. You can replace it with the following JavaScript line:

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

Note: the double backslash \\ is required in R, pure JavaScript would require only a single \.



回答2:

I have finally managed to solve all issues thanks to the other answer posted here.

javascript:
replaces symbols so that it works too for negative values

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

skips if value is 0 as it doesnt have to be changed to exp format

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

skips if values are text (after changing the symbol for negatives, otherwise they are seen as text

 if(isNaN(num1)){ 

Also added the layout command to the plot code, and put the labels on the x axis at a 45 degrees angle, otherwise they will overlap each other. Some changes to the mtcars df are made so that we get negative and large numbers in the data.

For plotting text values on the xaxis it works with this:

x = rownames(data),   

instead of a numerical x parameter

x = ~disp,  

Here is the working code:

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) 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!