【问题标题】:Plotly Gantt Chart: Remove Duplicate Y-Axis Labels and Stack Parallel TasksPlotly 甘特图:删除重复的 Y 轴标签并堆叠并行任务
【发布时间】:2021-05-28 13:56:13
【问题描述】:

我打算使用 python 库“Plotly”来构建甘特图。具体来说:https://plotly.com/python/gantt/#group-tasks-together

但是,每个“作业”可以有多个任务,并且这些任务可以并行运行。根据我的观察,Plotly 不会将并行运行的任务堆叠在一起,这使得阅读图表变得非常困难。这是一个示例,其中“作业 A”有两个并行运行的任务,但只有一个可见:

data = [dict(Task="Job A", Start='2009-01-01', Finish='2009-02-28'),
      dict(Task="Job A", Start='2009-01-01', Finish='2009-02-28'),
      dict(Task="Job B", Start='2009-03-05', Finish='2009-04-15'),
      dict(Task="Job C", Start='2009-02-20', Finish='2009-05-30')]

# Without group_tasks=True, There would be two separate "Job A" labels
fig = ff.create_gantt(data, group_tasks=True)
fig.show()

我想要的是两个“作业 A”任务都可见,但垂直堆叠,“作业 A”位于其任务占用的垂直空间的中心。像这样,但没有两个“工作 A”标签:

如果有人对我的甘特图项目有任何图书馆建议,请随时分享!谢谢!

【问题讨论】:

  • 我在Altair's 库中尝试了您的数据,但作业 A 没有聚合成两个任务。这取决于您对任务的看法,但是如果在相同的时间内有相同的任务,那么工作名称不会是 Job A-1 和 Job A-2 吗?
  • 感谢您花时间研究这些数据!让我把数据放到一个有意义的角度。想象一下,每个“工作”都是一家餐厅,每个任务(酒吧)都是一个订单,其中包含烹饪和上菜所需的时间。餐厅以异步方式移动,因此它们有多个并行运行的任务,并且经常相互重叠。这些任务总是独一无二的,永远不应该被隐藏。这有帮助吗?
  • 除了垂直堆叠之外,您还可以用另一种方法来完成这项工作吗?这个问题的解决方案:stackoverflow.com/questions/67129085/… 使用时间线(我认为可以在您的情况下使用),甚至应该使用甘特图。
  • 我找到了这个例子:community.plotly.com/t/group-by-color-in-px-timeline/51049 这正是我想要的。我怎样才能做到这一点,因为它必须可以使用 Plotly?
  • 我的猜测是您将需要一些解决方法,例如使用注释或形状手动将Job A 的条形放置在图中其余部分的正确配置中。如果没有人回答,我稍后会回过头来!

标签: python plotly gantt-chart


【解决方案1】:

首先是使用fig.add_shape 在原始Task 下方添加一个相同的Task 作为矩形。

为此,我们需要每个矩形的 y 坐标,但方便的是,第一个条在 y=0 处,第二个条在 y=1 处,依此类推。因此,按顺序列出的唯一任务的索引也是 y 坐标(唯一任务是 [Job A, Job B, Job C],因此 Job C 条将集中在 y=3)。每个条形的默认宽度为 0.8,因此如果 y0 是条形的起始 y 坐标,则 y1 应以 y0-0.4 结束。

请注意,带注释的形状不会有任何悬停模板,并且每个条的颜色与当前编写的方式相同。

import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go

## added additional duplicate Task to demonstrate generalizability
data = [dict(Task="Job A", Start='2009-01-01', Finish='2009-02-28'),
      dict(Task="Job A", Start='2009-01-01', Finish='2009-02-28'),
      dict(Task="Job B", Start='2009-03-05', Finish='2009-04-15'),
      dict(Task="Job C", Start='2009-02-20', Finish='2009-05-30'),
      dict(Task="Job C", Start='2009-02-20', Finish='2009-05-30')]

df = pd.DataFrame(data)

# Without group_tasks=True, There would be two separate "Job A" labels
# fig = ff.create_gantt(data, group_tasks=True)

## plot the non-duplicate rows
fig = px.timeline(df.loc[~df['Task'].duplicated()], x_start="Start", x_end="Finish", y="Task")

## plot the duplicate rows using rectangular shapes
for row in df.loc[df['Task'].duplicated()].itertuples():
      y_val = np.where(df.Task.unique()==row[1])[0][0]
      # print(f"found {row[1]} at index {y_val}")
      fig.add_shape(type="rect",
            xref="x", yref="y",
            x0=row[2], x1=row[3], 
            y0=y_val, y1=y_val-0.4,
            line_width=0,
            fillcolor="salmon",
)
fig.show()

【讨论】:

    【解决方案2】:
    import plotly.express as px
    import pandas as pd
    
    data = [dict(Task="Job A", Start='2009-01-01', Finish='2009-02-28'),
          dict(Task="Job A", Start='2009-01-01', Finish='2009-02-28'),
          dict(Task="Job B", Start='2009-03-05', Finish='2009-04-15'),
          dict(Task="Job C", Start='2009-02-20', Finish='2009-05-30')]
    
    df = pd.DataFrame(data)
    
    df['JobNum'] = ""
    df.loc[0,'JobNum'] = 1
    for idx in range(1,df.shape[0]):
        if df.loc[idx-1,'Task'] == df.loc[idx,'Task']:
            df.loc[idx,'JobNum'] = df.loc[idx-1,'JobNum'] + 1
        else:
            df.loc[idx,'JobNum'] = 1
    
    df['hoverName'] = df.apply(lambda x: x['Task'] + "|" + str(x['JobNum']), axis=1)
    

    方法 1:使用 Facet 行

    fig = px.timeline(df
                      , x_start="Start"
                      , x_end="Finish"
                      , y="Task"
                      , hover_name= "Task"
                      , color_discrete_sequence=px.colors.qualitative.Prism
                      , opacity=.7
                      , template='plotly_white'
                      , color='Task'
                      , facet_row= 'JobNum'
                      , hover_data = ['Start','Finish']
                     )
    fig.show()
    

    Appraoch 1 Output

    方法 2:调整宽度和偏移量。当有两个以上的并行任务时,这需要概括。

    fig = px.timeline(df
                      , x_start="Start"
                      , x_end="Finish"
                      , y="Task"
                      , hover_name= "hoverName"
                      , color_discrete_sequence=px.colors.qualitative.Prism
                      , opacity=.7
                      , template='plotly_white'
                      , color='JobNum'
                      , hover_data = ['Start','Finish']
                     )
    
    for obj in fig.data:
        Task, JobNum = obj.hovertext[0].split("|")
        if (int(JobNum) == 1):
            obj.width = 0.1
            obj.offset = 0.05
        elif (int(JobNum) == 2):
            obj.width = 0.1
            obj.offset = -0.05
    
    fig.show()
    

    Approach 2 output

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-30
      • 1970-01-01
      • 1970-01-01
      • 2021-01-22
      • 2021-10-13
      • 2013-10-17
      相关资源
      最近更新 更多