首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Plotly Python:将分组条形图中的X轴与多个Y轴对齐

Plotly Python:将分组条形图中的X轴与多个Y轴对齐
EN

Stack Overflow用户
提问于 2018-06-16 02:29:37
回答 1查看 2.5K关注 0票数 1

我有一个分组条形图,每个条形图都有两个不同比例的y轴。我正在尝试对齐两组的x轴(y=0)。我发现在其中设置rangemode='zero'应该起作用的链接link1link2很少,但是我的数据由负值组成,我猜这是因为将rangemode设置为0不起作用。

下面是我的代码:

代码语言:javascript
复制
import plotly.offline as plt
import plotly.graph_objs as go
traces = [go.Bar(x=[1,2,3,4], y=[-1,2,-3,4], name='y actual'), 
          go.Bar(x=[1], y=[0], name='y dummy', hoverinfo='none', showlegend=False), 
          go.Bar(x=[1],y=[0],yaxis='y2', name='y2 dummy', hoverinfo='none', showlegend=False),
          go.Bar(x=[1,2,3,4], y=[22, 2, 13, 25], yaxis='y2', name='y2 actual')]
layout = go.Layout(barmode='group',
                   yaxis=dict(title='y actual', rangemode="tozero", anchor='x', overlaying='y2'),
                   yaxis2=dict(title='y2 actual', side='right', rangemode = "tozero", anchor='x'))
fig = go.Figure(data=traces, layout=layout)
plt.iplot(fig)

上面的代码生成的图:

我该如何解决这个问题?

注意:您可以在代码中看到两个虚拟跟踪。我引入了它们,这样'y actual‘和'y2 actual’这两个轨迹就不会相互重叠。有关我为什么这么做的更多信息,请查看此link

EN

回答 1

Stack Overflow用户

发布于 2018-06-16 03:02:04

一种可能的解决方法:

将两个图的range元素设置为彼此成比例,然后轴将对齐。基本上,你的问题是一个轴必须显示负数,而另一个轴不显示。通过告诉y2显示负数,我们实现了我们的目标。

代码语言:javascript
复制
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
import plotly.graph_objs as go

init_notebook_mode(connected=True)

traces = [
    go.Bar(
        x=[1, 2, 3, 4], 
        y=[-1, 2, -3, 4], 
        name='y actual'
    ), 
    go.Bar(
        x=[1], 
        y=[0], 
        name='y dummy', 
        hoverinfo='none', 
        showlegend=False
    ), 
    go.Bar(
        x=[1],
        y=[0],
        yaxis='y2', 
        name='y2 dummy', 
        hoverinfo='none', 
        showlegend=False
    ),
   go.Bar(
       x=[1, 2, 3, 4], 
       y=[22, 2, 13, 25], 
       yaxis='y2', 
       name='y2 actual'
   )
]

# layout
layout = go.Layout(
    barmode='group',
    yaxis=dict(
        title='y actual', 
        rangemode="tozero", 
        #anchor='x', 
        overlaying='y2',
        side="left",
        range = [-4, 10]
    ),
    yaxis2=dict(
        title='y2 actual', 
        side='right', 
        rangemode = "tozero",
        #anchor='x',
        range = [-12, 30]
    )
)

# make fig
fig = go.Figure(data=traces, layout=layout)
iplot(fig)

这可能是恼人的,因为必须保持它们的比例,但这将确保它们的对齐。

为了帮助自动化这个过程,您可以使用以下函数来生成两个彼此成比例的范围。

代码语言:javascript
复制
def make_proportional_intervals(a, b):
    """
    Given two list like objects, compute two proprotionally sized ranges.
    This function assumes the max value in both lists is positive and non-zero
    """
    min_a, min_b = min(a), min(b)
    max_a, max_b = max(a), max(b)
    if (min_a >=0) & (min_b >= 0):
        # provide a 20% cushion to the scale
        return [0, round(1.2*max_a)], [0, round(1.2*max_b)]
    else:
        if (min_a < min_b) & (max_a < max_b):
            n = -(-max_b // max_a)
            # n = math.ceil(max_b / max_a), if you cannot assume ints.
            return [min_a, max_a], [n*min_a, n*max_a]

        elif (min_b < min_a) & (max_b < max_a):
            n = -(-max_a // max_b)
            # n = math.ceil(max_b / max_a), if you cannot assume ints.
            return [n*min_b, n*max_b], [min_b, max_b]

        elif (min_b < min_a) & (max_a < max_b):
            n = max( -(-max_b // max_a), -(min_b // min_a) )
            return [min_b / n, max_b / n], [min_b, max_b]

        elif (min_a < min_b) & (max_b < max_a):
            n = max( -(-max_a // max_b), -(min_a // min_b) )
            return [min_a, max_a], [min_a / n, max_a / n]
        elif (min_a == min_b):
            m = max(max_a, max_b)
            return [min_a, m], [min_b,  m]
        elif max_a == max_b:
            m = min(min_a, min_b)
            return [m, max_a], [m, max_b]

此函数假设您的值将是整数,但如果不是,您可以使用import math并使用math.ceil()来代替我的整数除法。我在避免添加更多的导入。如果您希望看到这段代码的实际效果,我在jupyter notebook中创建了一个示例,您可以多次运行该示例,以了解它如何处理不同的数组。

代码语言:javascript
复制
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
import plotly.graph_objs as go
import numpy as np

def make_proportional_intervals(a, b):
    """
    Given two list like objects, compute two proprotionally sized ranges.
    This function assumes the max value in both lists is positive and non-zero
    """
    min_a, min_b = min(a), min(b)
    max_a, max_b = max(a), max(b)
    if (min_a >=0) & (min_b >= 0):
        # provide a 20% cushion to the scale
        return [0, round(1.2*max_a)], [0, round(1.2*max_b)]
    else:
        if (min_a < min_b) & (max_a < max_b):
            n = -(-max_b // max_a)
            # n = math.ceil(max_b / max_a), if you cannot assume ints.
            return [min_a, max_a], [n*min_a, n*max_a]

        elif (min_b < min_a) & (max_b < max_a):
            n = -(-max_a // max_b)
            # n = math.ceil(max_b / max_a), if you cannot assume ints.
            return [n*min_b, n*max_b], [min_b, max_b]

        elif (min_b < min_a) & (max_a < max_b):
            n = max( -(-max_b // max_a), -(min_b // min_a) )
            return [min_b / n, max_b / n], [min_b, max_b]

        elif (min_a < min_b) & (max_b < max_a):
            n = max( -(-max_a // max_b), -(min_a // min_b) )
            return [min_a, max_a], [min_a / n, max_a / n]
        elif (min_a == min_b):
            m = max(max_a, max_b)
            return [min_a, m], [min_b,  m]
        elif max_a == max_b:
            m = min(min_a, min_b)
            return [m, max_a], [m, max_b]

init_notebook_mode(connected=True)

y0 = np.random.randint(-5, 35, 6)
y1 = np.random.randint(-7, 28, 6)

print(y0, y1)
range0, range1 = make_proportional_intervals(y0, y1)

traces = [
    go.Bar(
        x=[1, 2, 3, 4, 5, 6], 
        y=y0, 
        name='y actual'
    ), 
    go.Bar(
        x=[1], 
        y=[0], 
        name='y dummy', 
        hoverinfo='none', 
        showlegend=False
    ), 
    go.Bar(
        x=[1],
        y=[0],
        yaxis='y2', 
        name='y2 dummy', 
        hoverinfo='none', 
        showlegend=False
    ),
   go.Bar(
       x=[1, 2, 3, 4, 5, 6], 
       y=y1, 
       yaxis='y2', 
       name='y2 actual'
   )
]

# layout
layout = go.Layout(
    barmode='group',
    yaxis=dict(
        title='y actual', 
        rangemode="tozero", 
        #anchor='x', 
        overlaying='y2',
        side="left",
        range = range0
    ),
    yaxis2=dict(
        title='y2 actual', 
        side='right', 
        rangemode = "tozero",
        #anchor='x',
        range = range1
    )
)

fig = go.Figure(data=traces, layout=layout)
iplot(fig)

同样,这只是一种变通办法,因为您具有负数,不能将rangemode = "tozero"用作场景here。也许开发人员将来会在rangemode中添加一些东西来纠正这一点。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50880980

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档