以指数格式绘制轴
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)
这是对这个问题的跟进:
上述问题的答案使用 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)