Plotly 时间线可以在 Jupyter Notebook Widget 中使用/复制吗?

Posted

技术标签:

【中文标题】Plotly 时间线可以在 Jupyter Notebook Widget 中使用/复制吗?【英文标题】:Can Plotly timeline be used / reproduced in Jupyter Notebook Widget? 【发布时间】:2021-05-18 22:56:50 【问题描述】:

plotly plotly.express.timeline 很棒,但它创造了自己的形象。似乎我需要将此视觉效果嵌入到 FigureWidget 中,以使其与 Jupyter Notebook 中的布局搭配得很好。所以我正在尝试使用 px.timeline() 所基于的 plotly.graph_objects.Bar() 重新创建绘图。

不幸的是,我不知道如何实现这一点。似乎条形的值被添加到不用作绝对位置的“基本”向量(作为相对值)中。 Plotly 似乎不理解 datetime.timedelta() 对象。打印时间线()图形版本将值显示为 一个浮点值数组,不清楚它们是如何计算的。我试过简单地复制它们,但最终会认为 x 轴不是日期时间轴。

欢迎提供任何线索。如何使用 Box() 绘制适当的图形,或者如何在笔记本中嵌入/动画/布局 px.timeline() 图形。

import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
from datetime import datetime

# the data:
df = pd.DataFrame([
    dict(Task="one", Start=datetime(2009,1,1), Finish=datetime(2009,4,28)),
    dict(Task="two", Start=datetime(2009,5,5), Finish=datetime(2009,7,15)),
    dict(Task="three", Start=datetime(2009,7,20), Finish=datetime(2009,9,30))
])

# working plotly express figure:
pxfig = px.timeline(df, x_start="Start", x_end="Finish", y="Task")
pxfig.show() # looks great

# Broken bar figure:
plainfig = go.Figure()
plainfig.add_bar(base=df['Start'],
#                 x=pxfig.data[0].x,  # this breaks the axis as they are not of type datetime.
#                 x=df['Finish']-df['Start'], # this doesn't produce the right plot
                 x=df['Finish'], # these appear to be relative to base, not absolute
                 y=df['Task'], orientation='h')

plainfig.show()

# looking at the two shows interesting differences in the way the x data is stored
print(pxfig)
print(plainfig)

Figure(
    'data': ['alignmentgroup': 'True',
          'base': array([datetime.datetime(2009, 1, 1, 0, 0),
                         datetime.datetime(2009, 5, 5, 0, 0),
                         datetime.datetime(2009, 7, 20, 0, 0)], dtype=object),
          'x': array([1.01088e+10, 6.13440e+09, 6.22080e+09]),
          'xaxis': 'x',
          'y': array(['one', 'two', 'three'], dtype=object),
          'yaxis': 'y'],
    'layout': 'barmode': 'overlay',
           'legend': 'tracegroupgap': 0,
           'margin': 't': 60,
           'template': '...',
           'xaxis': 'anchor': 'y', 'domain': [0.0, 1.0], 'type': 'date',
           'yaxis': 'anchor': 'x', 'domain': [0.0, 1.0], 'title': 'text': 'Task'
)
Figure(
    'data': ['base': array([datetime.datetime(2009, 1, 1, 0, 0),
                         datetime.datetime(2009, 5, 5, 0, 0),
                         datetime.datetime(2009, 7, 20, 0, 0)], dtype=object),
          'orientation': 'h',
          'type': 'bar',
          'x': array([datetime.datetime(2009, 4, 28, 0, 0),
                      datetime.datetime(2009, 7, 15, 0, 0),
                      datetime.datetime(2009, 9, 30, 0, 0)], dtype=object),
          'y': array(['one', 'two', 'three'], dtype=object)],
    'layout': 'template': '...'
)

【问题讨论】:

【参考方案1】:

我无法回答如何在 FigureWidget 中嵌入时间线,但我想我可以解决您最初的问题,即让时间线与 jupyter 笔记本布局很好地配合。我猜您希望能够以交互方式更新时间线?

我通过将px.timeline 生成的图形嵌入到输出小部件中解决了这个问题。然后,每当我需要更新图形时(例如,从按钮回调),我只需清除输出小部件中的输出,创建一个新的时间线图形并显示该新图形。这不是最优雅的做事方式,但可以完成工作。

import ipywidgets as widgets
from IPython.display import display, clear_output
import pandas as pd
import plotly.express as px
from datetime import datetime

output = widgets.Output()

df = pd.DataFrame([
    dict(Task="one", Start=datetime(2009,1,1), Finish=datetime(2009,4,28)),
    dict(Task="two", Start=datetime(2009,5,5), Finish=datetime(2009,7,15)),
    dict(Task="three", Start=datetime(2009,7,20), Finish=datetime(2009,9,30))
])

updated_df = pd.DataFrame([
    dict(Task="one", Start=datetime(2009,1,1), Finish=datetime(2009,4,28)),
    dict(Task="two", Start=datetime(2009,5,5), Finish=datetime(2009,7,15)),
    dict(Task="three", Start=datetime(2009,7,20), Finish=datetime(2009,9,30)),
    dict(Task="four", Start=datetime(2009,10,5), Finish=datetime(2009,10,10))
])

# display the original timeline figure
pxfig = px.timeline(df, x_start="Start", x_end="Finish", y="Task")
with output:
    display(pxfig)

# create a button which when pressed will update the timeline figure
button = widgets.Button(description='update figure')
def on_click(button):
    with output:
        clear_output()
        new_pxfig = px.timeline(updated_df, x_start="Start", x_end="Finish", y="Task")
        display(new_pxfig)
button.on_click(on_click)

display(button)

【讨论】:

以上是关于Plotly 时间线可以在 Jupyter Notebook Widget 中使用/复制吗?的主要内容,如果未能解决你的问题,请参考以下文章

在 jupyter 中 [每隔几秒] 更新 plotly 图形

甘特图上的显示问题(Jupyter、Plotly、Python)

Plotly 在 jupyter lab 中给出一个空字段作为输出

Plotly express 不在 jupyter lab 中渲染

jupyter笔记本中的Plotly数据占用了大量内存

Plotly Table 在 Python 中的 Jupyter Lab 中不显示?