为什么我不能像这个代码笔示例那样在我的 plotly.js 散点图中对齐我的 y 轴?

Why can't I align my y axes in my plotly.js scatter chart like in this code pen example?

提问人:gib65 提问时间:10/24/2023 更新时间:10/25/2023 访问量:43

问:

我们正在开发一个带有 plotly.js 图表的 AngularJS 应用程序。我们有一个散点图,可以有多个 y 轴。我们希望将 y 轴水平堆叠到图表的右侧(理想情况下),或者将每个奇数 y 轴堆叠在左侧,将每个偶数 y 轴堆叠在右侧。在 Plotly.js 引用文档中有一个示例,其中包含指向代码笔示例的链接。请注意,图表的左侧堆叠了 2 个 y 轴,右侧堆叠了 2 个 y 轴:

enter image description here

但是当我尝试模仿这种行为时,我得到了这个:

enter image description here

请注意,kWh 轴(蓝色)位于图表内(从左侧开始约 85%)。为什么它这样做,而代码笔示例不这样做?

这是我的跟踪数据:

[
   {
      "type":"scatter",
      "x":[...],
      "y":[...],
      "hovertemplate":"%{x}<br>%{y:.2f} %{text}<extra></extra>",
      "text":[...],
      "name":"°C",
      "marker":{
         "color":"#89d300",
         "size":1,
         "opacity":1
      },
      "line":{
         "width":1
      },
      "yaxis":"y",
      "connectgaps":true,
      "measurementGroupId":18
   },
   {
      "type":"scatter",
      "x":[...],
      "y":[...],
      "hovertemplate":"%{x}<br>%{y:.2f} %{text}<extra></extra>",
      "text":[...],
      "name":"m3",
      "marker":{
         "color":"#3c3c3c",
         "size":1,
         "opacity":1
      },
      "line":{
         "width":1
      },
      "yaxis":"y2",
      "connectgaps":true,
      "measurementGroupId":8
   },
   {
      "type":"scatter",
      "x":[...],
      "y":[...],
      "hovertemplate":"%{x}<br>%{y:.2f} %{text}<extra></extra>",
      "text":[...],
      "name":"kWh",
      "marker":{
         "color":"#2fb5ea",
         "size":1,
         "opacity":1
      },
      "line":{
         "width":1
      },
      "yaxis":"y3",
      "connectgaps":true,
      "measurementGroupId":2
   }
]

这是我的布局:

{
   "title":{
      "text":"Time Series",
      "font":{
         "color":"#3dcd58",
         "family":"Nunito-Regular",
         "size":18
      },
      "xanchor":"left",
      "x":0
   },
   "font":{
      "color":"#333",
      "family":"Nunito-Regular"
   },
   "dragmode":"select",
   "autosize":true,
   "margin":{
      "l":20,
      "r":15,
      "b":30,
      "t":30
   },
   "plot_bgcolor":"#ffffff",
   "showlegend":true,
   "legend":{
      "x":0,
      "bgcolor":"rgba(255,255,255,0.3)"
   },
   "xaxis":{
      "range":[
         1664845980000,
         1699440540000
      ]
   },
   "hovermode":"closest",
   "yaxis":{
      "showticklabels":true,
      "side":"left",
      "tickfont":{
         "color":"#89d300"
      },
      "range":[
         2.133101851851852,
         31.59375
      ],
      "name":"°C"
   },
   "yaxis2":{
      "showticklabels":true,
      "side":"right",
      "overlaying":"y",
      "tickfont":{
         "color":"#3c3c3c"
      },
      "range":[
         -1328.6000000000001,
         27900.6
      ],
      "name":"m3"
   },
   "yaxis3":{
      "showticklabels":true,
      "side":"left",
      "position":0.85,
      "overlaying":"y",
      "tickfont":{
         "color":"#2fb5ea"
      },
      "range":[
         -948.1500000000001,
         41163.15
      ],
      "name":"kWh"
   }
}

用于构建布局的代码如下所示:

var layout = {
    title: {
        text: 'Time Series',
        font: {
            color: "#3dcd58",
            family: "Nunito-Regular",
            size: 18
        },
        xanchor: "left",
        x: 0
    },
    font: {
        color: "#333",
        family: "Nunito-Regular"
    },
    dragmode: 'select',
    autosize: true,
    margin: {
        l: 20,
        r: 15,
        b: 30,
        t: 30
    },
    plot_bgcolor: '#ffffff',
    showlegend: true,
    legend: {
        x: 0,
        bgcolor: 'rgba(255,255,255,0.3)'
    },
    xaxis: {
        range: [minMaxDateValues[0], minMaxDateValues[1]]
    },
    hovermode: 'closest'
};
                        
...

for (var i = 0; i < visibleDrivers.length; i++) {
    var driver = visibleDrivers[i];
    var yAxisName = 'yaxis' + (i > 0 ? (i + 1) : '');
    var minMax = _.find(minMaxDriverValues, function(values) {
        return values.id === driver.nodeId + '.' + driver.measurementGroupId; 
    });
    if (!minMax) {
        minMaxDriverValues.push({
            id: driver.nodeId + '.' + driver.measurementGroupId,
            yAxis: yAxisName,
            minValue: driver.minValue,
            maxValue: driver.maxValue
        });
    }
    layout[yAxisName] = {
        showticklabels: true,
        side: i % 2 > 0 ? 'right' : 'left',
        position: i < 2 ? undefined : 0.85,
        overlaying: i > 0 ? 'y' : undefined,
        tickfont: { color: driver.color },
        range: driver.measurementGroupId === 2
            ? energyRange
            : [
                minMax ? minMax.minValue : driver.minValue,
                minMax ? minMax.maxValue : driver.maxValue
            ],
        name: driver.unit
    }
}

请注意,我正在切换 y 轴显示的一侧,如下所示:.我像这样设置 y 轴的位置:.side: i % 2 > 0 ? 'right' : 'left'position: i < 2 ? undefined : 0.85

这适用于前两个 y 轴。第一个被放置在左边,没有位置,第二个被放置在右边,没有位置。然后,当涉及到第三个 y 轴时,它被分配到右侧并获得 .85 的位置。这显然(与代码笔示例不同)将其从左侧定位到图表的 85% 中(即使它被分配到右侧)。

我可以尝试将其位置设置为 1.0 的技巧,将其放置在图表的最右侧(但仍在图表内):

enter image description here

但这并不理想,因为 1) 我们希望轴位于图表之外,以及 2) 当我们有第四个、第五个或第六个 y 轴时会发生什么?超出 1.0 位置的任何内容似乎都被忽略了,并将轴定位回开头(图表左侧):

enter image description here

查看我的代码和生成的布局,任何人都可以看到我做错了什么,而代码笔示例做对了吗?谢谢!

JavaScript 图表 plotly.js codepen yaxis

评论


答:

0赞 opike 10/24/2023 #1

如果显式宽度如下,会发生什么情况:

   "width":800,
   "title":{
      "text":"Time Series",
      "font":{
         "color":"#3dcd58",
         "family":"Nunito-Regular",
         "size":18
      },
      "xanchor":"left",
      "x":0
   },
   "font":{
      "color":"#333",
      "family":"Nunito-Regular"
   },
   Rest snipped...

评论

0赞 gib65 10/24/2023
设置宽度正是这样做的......设置图表的宽度。但轴的定位问题仍然存在。
0赞 opike 10/24/2023
我想,也许确定垂直轴定位的逻辑被动态宽度所抛弃。他们有效的例子是使用静态宽度。在查看了他们的文档后,看起来您需要在 yaxis 属性中设置的是 “anchor”: “free”。否则,将忽略“position”属性。请参阅此处:plotly.com/python/reference/layout/yaxis/#layout-yaxis-anchor
1赞 EricLavault 10/25/2023 #2

您需要约束 xaxis ,以便为 y 轴腾出一些空间,例如。

const layout = {
  // ...
  xaxis: { domain: [0.0, 0.9] }
}

这使得绘图的部分可用,现在设置的 y 轴也默认位于,因此您可以调整第三个 y 轴,例如。[0.9, 1.0]side: 'right'0.9position

position: i < 2 ? undefined : 0.95,

评论

0赞 gib65 10/29/2023
谢谢埃里克!这正是我所需要的!