简体   繁体   English

Plotly 带有 integer x 轴的甘特图的快速时间表?

[英]Plotly Express timeline for Gantt Chart with integer xaxis?

I'm using plotly express timeline to produce a Gantt chart following this example: https://medium.com/dev-genius/gantt-charts-in-python-with-plotly-e7213f932f1e我正在使用 plotly 快速时间表生成甘特图,遵循此示例: https://medium.com/dev-genius/gantt-charts-in-python-with-plotly-e7213f932f1e

It automatically sets the x-axis to use dates but I'd actually like to just use integers (ie Project Kick-Off +1, Project Kick-Off +6, etc).它会自动将 x 轴设置为使用日期,但实际上我只想使用整数(即 Project Kick-Off +1、Project Kick-Off +6 等)。

Is there a way to make a timeline plot NOT use dates for the xaxis?有没有办法让时间线 plot 不使用 x 轴的日期?

If I try using integers it'll treat them like milliseconds.如果我尝试使用整数,它会将它们视为毫秒。

The answer:答案:

Yes, it's possible, Just give integers as start and end "dates", calculate the difference between them ( delta ), and make these changes to your fig :是的,有可能,只需将整数作为开始和结束“日期”,计算它们之间的差异( delta ),然后对您的fig进行这些更改:

fig.layout.xaxis.type = 'linear'
fig.data[0].x = df.delta.tolist()

Plot Plot

在此处输入图像描述

The details:细节:

There actually is a way to achieve this, although the docs state that:实际上有一种方法可以实现这一点,尽管文档state 说:

The px.timeline function by default sets the X-axis to be of type=date, so it can be configured like any time-series chart. px.timeline function 默认将 X 轴设置为 type=date,因此可以像任何时间序列图一样进行配置。

And therefore every other functionality in px.timeline() seems to revolve around that fact.因此px.timeline()中的所有其他功能似乎都围绕着这一事实。 But if you just ignore that and use integers as values for Start and Finish , then you can tweak a few attributes to get what you want.但是,如果您只是忽略这一点并使用整数作为StartFinish的值,那么您可以调整一些属性以获得您想要的。 You just need to calculate the differences between each Start and Stop .您只需要计算每个StartStop之间的差异。 For example like this:例如像这样:

df = pd.DataFrame([
    dict(Task="Job A", Start=1, Finish=4),
    dict(Task="Job B", Start=2, Finish=6),
    dict(Task="Job C", Start=3, Finish=10)
])
df['delta'] = df['Finish'] - df['Start']

And then tweaks a little further:然后进一步调整:

fig.layout.xaxis.type = 'linear'
fig.data[0].x = df.delta.tolist()

Complete code:完整代码:

import plotly.express as px
import pandas as pd

df = pd.DataFrame([
    dict(Task="Job A", Start=1, Finish=4),
    dict(Task="Job B", Start=2, Finish=6),
    dict(Task="Job C", Start=3, Finish=10)
])
df['delta'] = df['Finish'] - df['Start']

fig = px.timeline(df, x_start="Start", x_end="Finish", y="Task")
fig.update_yaxes(autorange="reversed") 

fig.layout.xaxis.type = 'linear'
fig.data[0].x = df.delta.tolist()
f = fig.full_figure_for_development(warn=False)
fig.show()

I tried the other answer listed here, but that doesn't work if I specify a color .我尝试了此处列出的其他答案,但如果我指定color则不起作用。 If I try, the data fig.data has multiple Bar objects, and I don't think it contains the data necessary to assign all the deltas.如果我尝试,数据fig.data有多个Bar对象,我认为它不包含分配所有增量所需的数据。 However, I did find that I could monkeypatch plotly code to not convert it to time objects and I get the correct result:但是,我确实发现我可以猴子补丁plotly 代码不将其转换为时间对象,我得到了正确的结果:

import plotly.express as px
import pandas as pd

def my_process_dataframe_timeline(args):
    """
    Massage input for bar traces for px.timeline()
    """
    print("my method")
    args["is_timeline"] = True
    if args["x_start"] is None or args["x_end"] is None:
        raise ValueError("Both x_start and x_end are required")

    x_start = args["data_frame"][args["x_start"]]
    x_end = args["data_frame"][args["x_end"]]

    # note that we are not adding any columns to the data frame here, so no risk of overwrite
    args["data_frame"][args["x_end"]] = (x_end - x_start)
    args["x"] = args["x_end"]
    del args["x_end"]
    args["base"] = args["x_start"]
    del args["x_start"]
    return args
px._core.process_dataframe_timeline = my_process_dataframe_timeline

df = pd.DataFrame([
    dict(Task="Job A", Start=1, Finish=4, color="1"),
    dict(Task="Job B", Start=2, Finish=6, color="2"),
    dict(Task="Job C", Start=3, Finish=10, color="1")
])
df['delta'] = df['Finish'] - df['Start']

fig = px.timeline(df, x_start="Start", x_end="Finish", y="Task", color="color")
fig.update_yaxes(autorange="reversed") 

fig.layout.xaxis.type = 'linear'
fig.show()

Obviously not desirable to do this... it would be nice to get formal support.显然不希望这样做......获得正式支持会很好。

I think this is much simpler solution than above when we have to specify color我认为当我们必须指定color时,这比上面的解决方案要简单得多

for d in fig.data:
  filt = df['color'] == d.name
  d.x = df[filt]['Delta'].tolist()

To illustrate how the answer from JounghooLee works, I want to add this example为了说明 JounghooLee 的答案是如何工作的,我想添加这个例子

import plotly.express as px
import pandas as pd

df = pd.DataFrame([
    dict(Task="Job A", Start=1, Finish=10),
    dict(Task="Job B", Start=3, Finish=6),
    dict(Task="Job C", Start=3, Finish=10)
])

df['delta'] = df['Finish'] - df['Start']

fig = px.timeline(df, x_start="Start", x_end="Finish", y="Task", color="Task")


print(fig.data[0].x)
print(fig.data[1].x)
print(fig.data[2].x)

a = [True, False, False]
b = [False, True, False]
c = [False, False, True]

aSeries = pd.Series(a)
bSeries = pd.Series(b)
cSeries = pd.Series(c)

fig.layout.xaxis.type = 'linear'

# the dataFrame df is filtered by the pandas series, to add the specific delta to each of the bars
fig.data[0].x = df[aSeries]['delta'].tolist()
fig.data[1].x = df[bSeries]['delta'].tolist()
fig.data[2].x = df[cSeries]['delta'].tolist()

print(fig.data[0].x)
print(fig.data[1].x)
print(fig.data[2].x)


fig.show()

The prints give the following output: prints给出以下 output:

[0.]
[0.]
[0.]
(9,)
(3,)
(7,)

and the fig.data contains three bars并且fig.data包含三个条

在此处输入图像描述

If there is no color assigned, it does not work, because then fig.data contains just 1 bar如果没有分配color ,它就不起作用,因为fig.data只包含 1 条

在此处输入图像描述

The print(fig.data[0].x) returns print(fig.data[0].x)返回

[0. 0. 0.]

in this case.在这种情况下。

See also this https://stackoverflow.com/a/71141827/7447940另见https://stackoverflow.com/a/71141827/7447940

Another option is to use a plotly barplot and use the base argument to indicate where to put the bar start and the x value would be the duration of the task:另一种选择是使用 plotly 条形图并使用base参数指示条形开始的位置, x值将是任务的持续时间:

df = pd.DataFrame([
    dict(task="Job A", start=1, end=4),
    dict(task="Job B", start=2, end=6),
    dict(task="Job C", start=3, end=10)
])
df['delta'] = df['end'] - df['start']

fig = px.bar(df, 
base = "start",
x = "delta",
y = "task",
orientation = 'h'
)

fig.update_yaxes(autorange="reversed")
fig.show()

在此处输入图像描述

The barplot also accepts the color argument and correctly groups the tasks by the column you indicate, for example:条形图还接受color参数并按您指定的列正确分组任务,例如:

df = pd.DataFrame([
    dict(task="Job A", start=1, end=4, color = "A"),
    dict(task="Job B", start=2, end=6, color = "B"),
    dict(task="Job C", start=3, end=10, color = "A")
])
df['delta'] = df['end'] - df['start']

fig = px.bar(df, 
base = "start",
x = "delta",
y = "task",
color = "color",
orientation = 'h'
)

fig.update_yaxes(autorange="reversed")
fig.show()

在此处输入图像描述

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM