这里需要社区的灯光来实现如下所示的甘特图视图。
数据框看起来像这样,其中子项目是一个唯一的代码:
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
我尝试通过迭代每个条形来创建着色。 我的主要目标是让每个项目的所有条形都分组而不重叠,因此我使用子项目代码,但我想要基于阶段列的不同图例和颜色。
到目前为止我的(不成功的)代码是:
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()
基于这篇文章: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()
我希望通过这个你可以更接近你想要的。