分组甘特图视图,其图例与所使用的颜色不同(python)

问题描述 投票:0回答:1

这里需要社区的灯光来实现如下所示的甘特图视图。

数据框看起来像这样,其中子项目是一个唯一的代码:

project     start       end      phase       decision    subproject
    1      02-2017    03-2018   Phase_1       09-2023        a1
    1      08-2017    07-2019   Phase_1,2     09-2023        a2
    1      02-2018    11-2021   Phase_2       09-2023        a3
    1      04-2021    02-2023   Phase_3       06-2022        a4
    2      01-2019    02-2022   Phase_1       06-2022        b1
    2      06-2019    07-2022   Phase_2       06-2022        b2
    2      01-2021    03-2023   Phase_2,3     06-2022        b3
    2      03-2022    02-2021   Phase_3       06-2022        b4
    3      11-2017    02-2019   Phase_1       06-2022        c1
    3      01-2018    06-2019   Phase_2       06-2022        c2
    3      02-2018    07-2020   Phase_2       06-2022        c3
    3      02-2019    06-2021   Phase_2,3     03-2023        c4
    4      10-2019    10-2019   Phase_2       03-2023        d1
    4      06-2019    08-2020   Phase_3       03-2023        d2
    4      02-2020    02-2021   Phase_3       03-2023        d3

我尝试通过迭代每个条形来创建着色。 我的主要目标是让每个项目的所有条形都分组而不重叠,因此我使用子项目代码,但我想要基于阶段列的不同图例和颜色。

enter image description here

到目前为止我的(不成功的)代码是:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
import plotly.express as px

n_colors = 20  # Number of colors in each gradient
blue_orange_cmap = mcolors.LinearSegmentedColormap.from_list("blue_orange", ["blue", "orange"])
orange_green_cmap = mcolors.LinearSegmentedColormap.from_list("orange_green", ["orange", "green"])

blue_orange_colors = [blue_orange_cmap(i/n_colors) for i in range(n_colors)]
orange_green_colors = [orange_green_cmap(i/n_colors) for i in range(n_colors)]

colors = {'Phase_1' : 'blue',
          'Phase_2' : 'orange',
          'Phase_3' : 'green',
          'Phase_1,2' : blue_orange_colors,
          'Phase_2,3' : orange_green_colors}

for dat in fig.data:
    try:
        ix = example_df.index[example_df['subproject']==dat.name]
        phase = example_df.loc[ix, 'phase'].values[0]
        color = colors[phase]
        dat.marker.color = color
        dat.name = phase
    except:
        pass

fig = px.timeline(example_df, x_start='start', x_end='end', y='project', color = 'subproject')

fig.update_layout(xaxis_title='timeline', yaxis_title='projects', showlegend=True, barmode='group', title = "Example", width=1000, height=500)

fig.update_layout(legend=dict(title_text='', traceorder='reversed', itemsizing='constant'))

fig.show()
python pandas plotly gantt-chart
1个回答
0
投票

基于这篇文章:https://community.plotly.com/t/bar-chart-gradient-color-in-data-bars/43552/2 和你的代码,我能够得到一些可能让你更接近你想要的东西:

import matplotlib.colors as mcolors
import plotly.express as px
import pandas as pd
import numpy as np

example_df = pd.DataFrame({
    'start': ['2021-01-01', '2021-02-01', '2021-03-01', '2021-01-05', '2021-02-10'],
    'end': ['2021-01-15', '2021-02-15', '2021-03-15', '2021-01-10', '2021-02-20'],
    'project': ['Project 1',  'Project 1', 'Project 2', 'Project 2', 'Project 3'],
    'subproject': ['Subproject 1', 'Subproject 4', 'Subproject 5', 'Subproject 2', 'Subproject 3'],
    'phase': ['Phase_1,2', 'Phase_2', 'Phase_3', 'Phase_1,2', 'Phase_2,3']
})

n_colors = 20
blue_orange_cmap = mcolors.LinearSegmentedColormap.from_list("blue_orange", ["blue", "orange"])
orange_green_cmap = mcolors.LinearSegmentedColormap.from_list("orange_green", ["orange", "green"])

blue_orange_colors = [mcolors.rgb2hex(blue_orange_cmap(i/n_colors)) for i in range(n_colors)]
orange_green_colors = [mcolors.rgb2hex(orange_green_cmap(i/n_colors)) for i in range(n_colors)]

colors = {'Phase_1' : 'blue',
          'Phase_2' : 'orange',
          'Phase_3' : 'green',
          'Phase_1,2' : blue_orange_colors,
          'Phase_2,3' : orange_green_colors}

split_rows = []

for _, row in example_df.iterrows():
    phase = row['phase']
    if phase in colors and isinstance(colors[phase], list):
        start = pd.to_datetime(row['start'])
        end = pd.to_datetime(row['end'])
        duration = (end - start) / n_colors
        for i in range(n_colors):
            new_row = row.copy()
            new_row['start'] = start + i * duration
            new_row['end'] = start + (i + 1) * duration
            new_row['color_value'] = i
            split_rows.append(new_row)
    else:
        row['color_value'] = 0
        split_rows.append(row)

split_df = pd.DataFrame(split_rows)
split_df['project_subproject'] = split_df['project'] + ' - ' + split_df['subproject']

fig = px.timeline(split_df, x_start='start', x_end='end', y='project_subproject', color='color_value',
                  color_continuous_scale=blue_orange_colors)

for dat in fig.data:
    try:
        ix = example_df.index[example_df['subproject'] == dat.name.split(' - ')[1]]
        phase = example_df.loc[ix, 'phase'].values[0]
        color = colors[phase]
        
        if isinstance(color, list):
            num_segments = len(dat.x)
            gradient_colors = [color[int(i * (len(color) - 1) / (num_segments - 1))] for i in range(num_segments)]
            dat.marker.color = gradient_colors
        else:
            dat.marker.color = color
        
        dat.name = phase
    except:
        pass

fig.update_traces(marker_line_width=0)
fig.update_layout(hovermode="x unified")

fig.show()

结果如下图: enter image description here

我希望通过这个你可以更接近你想要的。

© www.soinside.com 2019 - 2024. All rights reserved.