我有一个简单的数据集,想要创建一个带有两个y轴的条形图.我使用以下代码:

data = {'col 1': [-6, 18.6, 106.35, 111],
        'col 2': [-787.5, 976.5, 11246, 25682]}
df_overview = pd.DataFrame(data)
df_overview.index = ['A', 'B', 'C', 'D']
colors = ['#FF0000', '#0000FF']  # red, blue
columns = ['col 1', 'col 2']
fig = make_subplots(specs=[[{"secondary_y": True}]])
for i, col in enumerate(columns):
    fig.add_trace(
        go.Bar(x=df_overview.index, y=df_overview[col], name=col, marker_color=colors[i], offsetgroup=i,),
        secondary_y=(i == 0)
    )
fig.update_layout(
    barmode='group',
    font_size=14,
    hovermode="x unified",
)
fig.show()

enter image description here

但不知何故,两个y轴并不是以零对齐.用plotly-python可以做到这一点吗?有人能帮帮忙吗?

推荐答案

这有点棘手,因为Ploly使用下面的公式计算默认的yAxis范围:[y_min-padding, y_max+padding] where padding=(y_max-y_min)/16.

通常,这将意味着yaxis1yaxis2的零不一定对齐(而且很可能不对齐).但是,我们可以通过计算0在yaxis1上的相对位置来求解yaxis2的填充.例如:

y1_min, y1_max = df_overview['col 2'].min(), df_overview['col 2'].max()
y1_padding = (y1_max - y1_min)/16
y1_range = [y1_min - y1_padding, y1_max + y1_padding]
y1_relative_zero = (0 - y1_range[0]) / (y1_range[1] - y1_range[0])

y1_relative_zero = 0.08200108720519005意味着y1轴上的0大约是最小值和最大值之间的8.2%.现在我们可以计算第二个yAxis上的必要填充,以确保0也是y2数据的最小和最大之间的相同百分比.

我们需要以下方程式的解:

(0 - y2_range_min) / (y2_range_max - y2_range_min) = y1_relative_zero

y2_range_min = y2_min - y2_paddingy2_range_max = y2_max + y2_padding之间.

我不会深入讨论所有细节,因为它正在重新安排变量,但以下是解决方案:

y2_padding = (y1_relative_zero * (y2_max - y2_min) + y2_min) / (1 - 2*y1_relative_zero)

把所有这些放在一起:

import pandas as pd
from plotly.subplots import make_subplots
import plotly.graph_objects as go

data = {'col 1': [-6, 18.6, 106.35, 111],
        'col 2': [-787.5, 976.5, 11246, 25682]}
df_overview = pd.DataFrame(data)
df_overview.index = ['A', 'B', 'C', 'D']
colors = ['#FF0000', '#0000FF']  # red, blue
columns = ['col 1', 'col 2']
fig = make_subplots(specs=[[{"secondary_y": True}]])
for i, col in enumerate(columns):
    fig.add_trace(
        go.Bar(x=df_overview.index, y=df_overview[col], name=col, marker_color=colors[i], offsetgroup=i,),
        secondary_y=(i == 0)
    )
fig.update_layout(
    barmode='group',
    font_size=14,
    hovermode="x unified",
)

y1_min, y1_max = df_overview['col 2'].min(), df_overview['col 2'].max()
y1_padding = (y1_max - y1_min)/16
y1_range = [y1_min - y1_padding, y1_max + y1_padding]
y1_relative_zero = (0 - y1_range[0]) / (y1_range[1] - y1_range[0])

y2_min, y2_max = df_overview['col 1'].min(), df_overview['col 1'].max()

## we solve the following equation:
# (0 - y2_range_min) / (y2_range_max - y2_range_min) = y1_relative_zero
# (0 - (y2_min - y2_padding)) / ((y2_max - y2_min) + 2*y2_padding) = y1_relative_zero
# y1_relative_zero * ((y2_max - y2_min) + 2*y2_padding) = (y2_padding - y2_min)
# y1_relative_zero * (y2_max - y2_min) + (y1_relative_zero*2*y2_padding) = (y2_padding - y2_min)
# (y2_padding - y2_min) - (y1_relative_zero*2*y2_padding) = y1_relative_zero * (y2_max - y2_min)
# y2_padding(1 - 2*y1_relative_zero) - y2_min = y1_relative_zero * (y2_max - y2_min)
y2_padding = (y1_relative_zero * (y2_max - y2_min) + y2_min) / (1 - 2*y1_relative_zero)
y2_range = [y2_min - y2_padding, y2_max + y2_padding]

fig.update_yaxes(range=y1_range, secondary_y=False)
fig.update_yaxes(range=y2_range, secondary_y=True)

fig.show()

enter image description here

Python-3.x相关问答推荐

根据收件箱内部的值以行降序的特定顺序重新排序列

Pandas groupby基于索引的连续列值相等

在Python中从mySQL获取多行

Numpy将3D数组的每个切片相乘以进行转置并对其求和

被多个\n拆分并保留

以编程方式关闭jupyterlab内核

泛型类型的参数的静态类型

如何将参数/值从测试方法传递给pytest的fixture函数?

逐行比较2个Pandas数据帧,并对每一行执行计算

使用Python抓取sofascore以获取有关球队阵容和投票的信息

我无法直接在 VSCode 中运行该程序,但可以使用 VScode 中的终端运行它

Django在POST到外部URL时如何进行CSRF保护? 更新

在 python f-string 中使用 \u

使用正则表达式提取字符串之间的文本

使用 pandas 进行多类分类的总体准确度

Python 3.10 模式匹配 (PEP 634) - 字符串中的通配符

python 3的蓝牙库

为什么 Python 不能识别我的 utf-8 编码源文件?

如何强制 Sphinx 使用 Python 3.x 解释器

无法解码 Python Web 请求