Plotly:在值和单位之间添加 space(荣誉 SI)

Plotly: add space between value and units (honor SI)

根据The International System of Units

The value of a quantity is written as a number followed by a space (representing a multiplication sign) and a unit symbol; e.g., 21 kg, 22 K.

我正在尝试创建一个纪念 space 的情节,但我似乎做不到。 Plotly 将在数量和单位符号之间不添加 space(在本例中为 MN,添加 mega 前缀后):

const data = [
  {
    x: ['A', 'B', 'C'],
    y: [20000000, 14000000, 23000000],
    type: 'bar',
  }
];

const layout = {
  yaxis: {
    title: 'Force',
    ticksuffix: 'N',
  }
}

Plotly.newPlot('plotDiv', data, layout);
<head>
  <script src='https://cdn.plot.ly/plotly-2.6.3.min.js'></script>
</head>
<body>
  <div id='plotDiv'></div>
</body>

如何在数量和单位符号之间添加 space?

理想情况下:

这有点复杂。我尝试通过 plotly API 查找,但找不到解决方案。然而,下面的代码只是生成了它自己的工具提示。

注意添加的 css 到 head 标签。

<html>
<head>
  <script src='https://cdn.plot.ly/plotly-2.6.3.min.js'></script>

  <!-- Add the css to hide the plotly tooltip and create our own -->
  <style>
        /* hide plotly tooltip - we'll still use it for position and data */
        .main-svg g.hoverlayer g.hovertext {
            opacity: 0;
        }

        /* Style our own tooltip - you can play with the values as you wish */
        #tooltip {
            display: flex;
            justify-content: center;
            align-items: center;
            min-width: 80px; 
            height: 20px;
            background: #006eb6;
            position: absolute;
            z-index: 10;
            top: 0;
            margin-left: 10px;
            font-size: 10px;
            color: white;
            border: 1px solid white;
            border-left: 0 solid white;
        }

        /* create the triangle */
        #tooltip:before {
            content: "";
            width: 0;
            height: 0;
            left: -10px;
            top: 0;
            position: absolute;
            border-top: 10px solid transparent;
            border-bottom: 10px solid transparent;
            border-right: 10px solid #006eb6;
        }
  </style>

</head>
<body>
  <div id='plotDiv'></div>
</body>

<script>
  const data = [
  {
    x: ['A', 'B', 'C'],
    y: [20000000, 14000000, 23000000],
    type: 'bar',
  }
];

const layout = {
  yaxis: {
    title: 'Force',
    ticksuffix: 'N'
    //ticksuffix: ' N'
  }
}

Plotly.newPlot('plotDiv', data, layout);

// add the spacing to the axes
const yaxes = document.querySelectorAll('.ytick text')
for(let yaxis of yaxes) {
  yaxis.innerHTML = yaxis.innerHTML.replace(/(\d+)/g, (_, num) => num + ' ') 
}

// tooltip starts here
// create a new tool tip - add it to body and hide it on display
const tooltip = document.createElement('div')
tooltip.setAttribute('id', 'tooltip')
tooltip.style.display = "none"
document.body.appendChild(tooltip)


// add mousemove event to body to override plotly hover
// get the bounding box of each bar in the chart
const underbars = document.getElementsByClassName('point')
const positions = []
for(let underbar of underbars) {
    positions.push(underbar.getBoundingClientRect())
}

// add event to display tooltip when mouse cursor is inside bar
document.body.onmousemove = evt => {
    const x = evt.clientX 
    const y = evt.clientY 
   
    // check if cursor falls inside bar
    let position = null;
    positions.forEach(pi => {
        pi.left = pi.left + window.scrollX 
        pi.top = pi.top + window.scrollY
        if(x >= pi.left && x <= pi.left + pi.width && y >= pi.top && y <= pi.top + pi.height) {
            position = pi
        }
    })

    // if it does, get the data from plotly tool tip and add it to our own
    if(position) {
        // get position of tooltip
        const hover_element = document.querySelector('.main-svg g.hoverlayer g.hovertext')
        if(!hover_element) {
            return
        }
        const transform = hover_element.getAttribute('transform').replaceAll(' ', '')
        
        
        // get the text from plotly tooltip and set it to our tooltip adding a space
        const text = document.querySelector('.main-svg g.hoverlayer g.hovertext text').innerHTML

        tooltip.innerHTML = text.replace(/(\d+)/g, (_, num) => num + ' ') 

        // get position of our plotly tooltip - add px so its compatible with css
        tooltip.style.transform = transform.replace(/(\d+\.\d+)/g, (_, num) => num + 'px')

        // have to add scroll and position container offset - this allows the chart to 
        // be rendered anywhere on screen. Can +/- px's for finer control of tooltip 
        const cp = document.querySelector('.main-svg').getBoundingClientRect()
        tooltip.style.marginTop = window.scrollY + cp.top
        tooltip.style.marginLeft = window.scrollX + cp.left

        // display tooltip
        tooltip.style.display = 'flex'
    }
    else {

        // hide tooltip on exit
        tooltip.style.display = "none"
    }
}

</script>
<\html>

也许更熟悉 Plotly 的人会插话。否则,请告诉我是否可以添加任何说明。

我将 &nbsp; 添加到 ticksuffix 和 hoverformat:".1f",结果可能就是您想要的。注意:&nbsp; 是 space 的转义码。 我使用 hoverformat 来避免默认格式(即 12000000 在悬停文本上变为 12M)。如果需要,它打开了进一步操纵悬停文本的可能性。

编辑

我修改了代码,得到的结果更符合OP的要求。

const data = [
  {
    x: ['A', 'B', 'C'],
    y: [20000000, 14000000, 12300000],
    type: 'bar',
  }
];

const average = (arr) => arr.reduce((a, b) => a + b, 0) / arr.length;
const divby1e6 = (arr) => arr.map((a) => a / 1000000);
let meany = average(data[0].y);
let suffix = "N";
if (meany > 1e6) {
    // compute new y, prep suffix
    suffix = " M" + suffix;
    data[0].y = divby1e6(data[0].y);
  } 
else {
    // just in case
  }
const layout = {
  yaxis: {
    title: 'Force',
    hoverformat: ".3f",  //specify decimal digits
    ticksuffix: suffix,
    tickformat: '.3f'
  }
}

Plotly.newPlot('plotDiv', data, layout);
<head>
  <script src='https://cdn.plot.ly/plotly-2.6.3.min.js'></script>
</head>
<body>
  <div id='plotDiv'></div>
</body>