问题描述
我有两个不同的数据框,包含不同的列:开始和结束日期、持续时间...
我想使用 plotly express 将数据绘制到时间线(甘特图)中。我知道如何使用子图,但我希望它们在同一个图形上而不是图上。
这是我的尝试:
'''
import plotly.graph_objs as go
import plotly.express as px
fig = px.timeline(chosen_data3,x_start="Start Date",x_end="End Date",y="new" )
fig2 = px.timeline(chosen_data4,y="new",ax = fig )
fig2.show()
#Using graph_objs :
fig = go.figure()
for (start,end,value,value2) in zip(chosen_data3["Start Date"],chosen_data3["End Date"],chosen_data3["Duration [Hours]"],chosen_data3["Hs [m]"] ):
name = f"Towing : {start} to {end}"
fig.add_trace(go.Scatter(x=[start,end],y=[value,value],mode='lines',name = name,marker =
dict(color = 'rgb(0,0)'),hoverinfo = 'all'))
for (start,value2) in zip(chosen_data4["Start Date"],chosen_data4["End Date"],chosen_data4["Duration [Hours]"],chosen_data4["Hs [m]"]):
name = f"Hook-Up :{start} to {end}"
fig.add_trace(go.Scatter(x=[start,marker = dict(color = 'rgb(65,105,225)'),hoverinfo = 'all'))
fig.show()
'''
graph_objs 的解决方案是正确的,但使用 express 的解决方案没有
编辑: 我的两个数据帧示例:
使用图形对象的解决方案:
我尝试的解决方案是连接两个数据框并创建一个新列来区分每个数据,当您将鼠标悬停在每个条上时,我会使用 plotly express 时间轴得到下图:
解决方法
您的想法是正确的:要使用 plotly express 重现您使用 plotly graph_objects 制作的图形,我们需要为您的两个 DataFrame 创建新列以保存您需要传递给 px.timeline
方法的信息.然后我们可以连接两个DataFrame,并将合并后的DataFrame传递给px.timeline。
我们可以创建一个名为 legend_entry
的列来保存要在图例中显示的字符串,并将其传递给 px.timeline 的 color
参数。但是,由于 color 参数也用于为条形分配颜色,并且您拥有的每个 legend_entry 都是唯一的,因此每个条形都是不同的颜色——我们可以通过将要分配给每个条形的显式颜色数组传递给每个条形来覆盖它参数 color_discrete_sequence
。为此,我们可以创建一个名为 color
的附加列,我们将根据牵引或连接任务使用 'black'
或 'blue'
填充该列。
由于最顶部和最底部的条形将正好位于图的边缘,我们可以通过将 y 轴的范围扩展到您喜欢的时间来添加一些填充。我选择了 10。
import numpy as np
import pandas as pd
import plotly.express as px
## recreate your data
chosen_data3 = pd.DataFrame({
"Start Date":['1999-03-06 05:00:00','1999-03-08 22:00:00','1999-03-12 22:00:00','1999-03-22 19:00:00'],"End Date":['1999-03-08 00:00:00','1999-03-12 19:00:00','1999-03-21 20:00:00','1999-03-26 03:00:00'],"Hs [m]":[1.804182,1.461362,1.825023,1.717531]
})
chosen_data4 = pd.DataFrame({
"Start Date":['1999-03-09 06:00:00','1999-03-20 05:00:00'],"End Date":['1999-03-11 18:00:00','1999-03-21 10:00:00'],"Hs [m]":[1.209672,1.121267]
})
chosen_data3[['Start Date','End Date']] = chosen_data3[['Start Date','End Date']].apply(pd.to_datetime)
chosen_data4[['Start Date','End Date']] = chosen_data4[['Start Date','End Date']].apply(pd.to_datetime)
chosen_data3['Duration [Hours]'] = (chosen_data3['End Date'] - chosen_data3['Start Date']) / np.timedelta64(1,'h')
chosen_data4['Duration [Hours]'] = (chosen_data4['End Date'] - chosen_data4['Start Date']) / np.timedelta64(1,'h')
## add a new column with a string for the legend
chosen_data3['legend_entry'] = "Towing : " + chosen_data3['Start Date'].apply(str) + " to " + chosen_data3['End Date'].apply(str)
chosen_data4['legend_entry'] = "Hook up : " + chosen_data4['Start Date'].apply(str) + " to " + chosen_data4['End Date'].apply(str)
## add a new column to specify the color of each bar
chosen_data3['color'] = 'black'
chosen_data4['color'] = 'blue'
## we can concatenate the data into one DataFrame so that we only need to use px.timeline once
df = pd.concat([chosen_data3,chosen_data4])
## pass an explicit array to the color_discrete_sequence
fig = px.timeline(df,x_start="Start Date",x_end="End Date",y="Duration [Hours]",color="legend_entry",color_discrete_sequence=df.color.values)
## add some padding so that the bottom and topmost bars aren't flush against the edge of the figure
y_axis_range_padding = 10
fig.update_layout(
title="Towing & Hook up tasks in function of duration",title_x=0.5,legend_title_text='',yaxis_range=[df['Duration [Hours]'].min() - y_axis_range_padding,df['Duration [Hours]'].max() + y_axis_range_padding]
)
fig.show()