在 Jupyter 中使用 plotly 在离线模式下创建动画图表
Posted
技术标签:
【中文标题】在 Jupyter 中使用 plotly 在离线模式下创建动画图表【英文标题】:Using plotly in Jupyter to create animated chart in off-line mode 【发布时间】:2017-08-20 21:32:37 【问题描述】:我一直在尝试让“Python 中的填充区域动画”示例在 Jupyter 笔记本中以离线模式使用 plotly 工作。该示例可以在这里找到:https://plot.ly/python/filled-area-animation/
由于我处于离线模式,我创建了一个本地 csv 文件,其中包含用作数据源的虚拟数据,然后使用 pandas 数据帧读取 csv:
# Add the following line
from plotly.offline import init_notebook_mode, iplot
.....
# Read csv instead of using get_data_yahoo
#appl = web.get_data_yahoo('AAPL', '2016-01-01', '2016-11-30')
appl = pd.read_csv("C:\\test.csv")
apple_data_matrix = appl.head(10)
.....
# Use offline version of iplot
#py.iplot(table, filename='apple_data_table')
iplot(table, filename='apple_data_table')
到目前为止一切顺利。 “制作网格”的代码保持不变 - 除了注释掉仅在线的最后一行:
def to_unix_time(dt):
epoch = datetime.utcfromtimestamp(0)
return (dt - epoch).total_seconds() * 1000
appl_price = list(appl['Adj Close'])
my_columns = []
for k in range(len(appl.index) - 1):
my_columns.append(Column(appl.index[:k + 1], 'x'.format(k + 1)))
my_columns.append(Column(appl_price[:k + 1], 'y'.format(k + 1)))
grid = Grid(my_columns)
#py.grid_ops.upload(grid, 'AAPL-daily-stock-price' + str(time.time()), auto_open=False)
代码的最后一部分(“制作图形”)是我苦苦挣扎的地方。这是绘制图表并对其进行动画处理的代码:
data=[dict(type='scatter',
xsrc=grid.get_column_reference('x1'),
ysrc= grid.get_column_reference('y1'),
name='AAPL',
mode='lines',
line=dict(color= 'rgb(114, 186, 59)'),
fill='tozeroy',
fillcolor='rgba(114, 186, 59, 0.5)')]
axis=dict(ticklen=4,
mirror=True,
zeroline=False,
showline=True,
autorange=False,
showgrid=False)
layout = dict(title='AAPL Daily Stock Price',
font=dict(family='Balto'),
showlegend=False,
autosize=False,
width=800,
height=400,
xaxis=dict(axis, **'nticks':12, 'tickangle':-45,
'range': [to_unix_time(datetime(2016, 1, 4)),
to_unix_time(datetime(2016, 11, 30))]),
yaxis=dict(axis, **'title': '$', 'range':[0,120]),
updatemenus=[dict(type='buttons',
showactive=False,
y=1,
x=1.1,
xanchor='right',
yanchor='top',
pad=dict(t=0, r=10),
buttons=[dict(label='Play',
method='animate',
args=[None, dict(frame=dict(duration=50, redraw=False),
transition=dict(duration=0),
fromcurrent=True,
mode='immediate')])])])
frames=['data':['xsrc': grid.get_column_reference('x'.format(k + 1)),
'ysrc': grid.get_column_reference('y'.format(k + 1))],
'traces': [0]
for k in range(len(appl.index) - 1)]
fig=dict(data=data, layout=layout, frames=frames)
py.icreate_animations(fig, 'AAPL-stockprice' + str(time.time()))
py.icreate_animations(最后一行)在离线模式下不可用。我尝试用 plotly.offline.iplot(fig) 替换它,但得到以下堆栈跟踪:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-17-c3918e48e33a> in <module>()
46 fig=dict(data=data, layout=layout, frames=frames)
47 #fig = go.Figure(data=data, layout=layout, frames=frames)
---> 48 plotly.offline.iplot(fig)
49 #iplot.create_animations(fig, 'AAPL-stockprice' + str(time.time()))
50 #py.icreate_animations(fig, 'AAPL-stockprice' + str(time.time()))
C:\Users\IBM_ADMIN\Anaconda3\lib\site-packages\plotly\offline\offline.py in iplot(figure_or_data, show_link, link_text, validate, image, filename, image_width, image_height)
340 )
341
--> 342 figure = tools.return_figure_from_figure_or_data(figure_or_data, validate)
343
344 # Though it can add quite a bit to the display-bundle size, we include
C:\Users\IBM_ADMIN\Anaconda3\lib\site-packages\plotly\tools.py in return_figure_from_figure_or_data(figure_or_data, validate_figure)
1378
1379 try:
-> 1380 graph_objs.Figure(figure)
1381 except exceptions.PlotlyError as err:
1382 raise exceptions.PlotlyError("Invalid 'figure_or_data' argument. "
C:\Users\IBM_ADMIN\Anaconda3\lib\site-packages\plotly\graph_objs\graph_objs.py in __init__(self, *args, **kwargs)
1110
1111 def __init__(self, *args, **kwargs):
-> 1112 super(Figure, self).__init__(*args, **kwargs)
1113 if 'data' not in self:
1114 self.data = Data(_parent=self, _parent_key='data')
C:\Users\IBM_ADMIN\Anaconda3\lib\site-packages\plotly\graph_objs\graph_objs.py in __init__(self, *args, **kwargs)
375 d = key: val for key, val in dict(*args, **kwargs).items()
376 for key, val in d.items():
--> 377 self.__setitem__(key, val, _raise=_raise)
378
379 def __dir__(self):
C:\Users\IBM_ADMIN\Anaconda3\lib\site-packages\plotly\graph_objs\graph_objs.py in __setitem__(self, key, value, _raise)
430
431 if self._get_attribute_role(key) == 'object':
--> 432 value = self._value_to_graph_object(key, value, _raise=_raise)
433 if not isinstance(value, (PlotlyDict, PlotlyList)):
434 return
C:\Users\IBM_ADMIN\Anaconda3\lib\site-packages\plotly\graph_objs\graph_objs.py in _value_to_graph_object(self, key, value, _raise)
541 # this can be `None` when `_raise == False`
542 return GraphObjectFactory.create(key, value, _raise=_raise,
--> 543 _parent=self, _parent_key=key)
544
545 def help(self, attribute=None, return_help=False):
C:\Users\IBM_ADMIN\Anaconda3\lib\site-packages\plotly\graph_objs\graph_objs.py in create(object_name, *args, **kwargs)
791 class_name = graph_reference.OBJECT_NAME_TO_CLASS_NAME.get(object_name)
792 if class_name in ['Figure', 'Data', 'Frames']:
--> 793 return globals()[class_name](*args, **kwargs)
794 else:
795 kwargs['_name'] = object_name
C:\Users\IBM_ADMIN\Anaconda3\lib\site-packages\plotly\graph_objs\graph_objs.py in __init__(self, *args, **kwargs)
156
157 for index, value in enumerate(list(*args)):
--> 158 value = self._value_to_graph_object(index, value, _raise=_raise)
159
160 if isinstance(value, PlotlyBase):
C:\Users\IBM_ADMIN\Anaconda3\lib\site-packages\plotly\graph_objs\graph_objs.py in _value_to_graph_object(self, index, value, _raise)
1010 return GraphObjectFactory.create(item, _raise=_raise,
1011 _parent=self,
-> 1012 _parent_key=index, **value)
1013
1014 def get_data(self, flatten=False):
C:\Users\IBM_ADMIN\Anaconda3\lib\site-packages\plotly\graph_objs\graph_objs.py in create(object_name, *args, **kwargs)
797 return PlotlyList(*args, **kwargs)
798 else:
--> 799 return PlotlyDict(*args, **kwargs)
800
801
C:\Users\IBM_ADMIN\Anaconda3\lib\site-packages\plotly\graph_objs\graph_objs.py in __init__(self, *args, **kwargs)
375 d = key: val for key, val in dict(*args, **kwargs).items()
376 for key, val in d.items():
--> 377 self.__setitem__(key, val, _raise=_raise)
378
379 def __dir__(self):
C:\Users\IBM_ADMIN\Anaconda3\lib\site-packages\plotly\graph_objs\graph_objs.py in __setitem__(self, key, value, _raise)
400 if key.endswith('src'):
401 if key in self._get_valid_attributes():
--> 402 value = graph_objs_tools.assign_id_to_src(key, value)
403 return super(PlotlyDict, self).__setitem__(key, value)
404
C:\Users\IBM_ADMIN\Anaconda3\lib\site-packages\plotly\graph_objs\graph_objs_tools.py in assign_id_to_src(src_name, src_value)
254 if src_id == '':
255 err = exceptions.COLUMN_NOT_YET_UPLOADED_MESSAGE
--> 256 err.format(column_name=src_value.name, reference=src_name)
257 raise exceptions.InputError(err)
258 return src_id
AttributeError: 'str' object has no attribute 'name'
任何人都可以帮助锻炼如何让动画在离线模式下工作。
提前致谢 -- 西蒙
【问题讨论】:
【参考方案1】:这个问题在 Plotly icreate_animations offline on Jupyter Notebook
根据那个答案,plotly.grid_objs Grid 函数不支持离线。他将代码转换为使用 DataFrame,从而避免了该问题。
【讨论】:
【参考方案2】:这是旧的,但如果有人想弄清楚,唯一的区别是使用from plotly.grid_objs import Grid, Column
作为数据持有者,可以很容易地被pandas.DataFrame
或dict
替换。
通过使用dict
,替换以下代码块:
my_columns = []
for k in range(len(appl.index) - 1):
my_columns.append(Column(appl.index[:k + 1], 'x'.format(k + 1)))
my_columns.append(Column(appl_price[:k + 1], 'y'.format(k + 1)))
grid = Grid(my_columns)
与:
my_columns =
for k in range(len(appl.Date) - 1):
my_columns['x'.format(k + 1)] = list(appl.Date)[:k + 1]
my_columns['y'.format(k + 1)] = appl_price[:k + 1]
那么发生了什么? Plotly 的 Grid
使用 get_column_reference
方法,其工作原理与 dict
完全一样,它存储用于绘图开始以及 frames
的键值对。
从那里,将所有对Grid
的引用替换为您创建的dict
,该dict
从我们上面所做的更改中存储为my_columns
。最后使用离线 Python API 中的iplot
而不是icreate_animations
。
第二段代码:
data=[dict(type='scatter',
x= my_columns['x1'],
y= my_columns['y1'],
name='AAPL',
mode='lines',
line=dict(color= 'rgb(114, 186, 59)'),
fill='tozeroy',
fillcolor='rgba(114, 186, 59, 0.5)')]
axis=dict(ticklen=4,
mirror=True,
zeroline=False,
showline=True,
autorange=False,
showgrid=False)
layout = dict(title='AAPL Daily Stock Price',
font=dict(family='Balto'),
showlegend=False,
autosize=False,
xaxis=dict(axis, **'nticks':12, 'tickangle':-45,
'range': [to_unix_time(datetime(2015, 2, 17)),
to_unix_time(datetime(2016, 11, 30))]),
yaxis=dict(axis, **'title': '$', 'range':[0,170]),
updatemenus=[dict(type='buttons',
showactive=False,
y=1,
x=1.1,
xanchor='right',
yanchor='top',
pad=dict(t=0, r=10),
buttons=[dict(label='Play',
method='animate',
args=[None, dict(frame=dict(duration=50, redraw=True),
transition=dict(duration=0),
fromcurrent=True,
mode='immediate')])])])
frames=['data':['x': my_columns['x'.format(k + 1)],
'y': my_columns['y'.format(k + 1)]],
'traces': [0]
for k in range(len(appl.Date) - 1)]
fig=dict(data=data, layout=layout, frames=frames)
iplot(fig,
show_link=False, config=dict(displaylogo=False, modeBarButtonsToRemove=['sendDataToCloud']))
【讨论】:
【参考方案3】:您不得不担心在offline mode 中使用 Plotly 的日子已经一去不复返了。为了实现与您的源生成的图表相同的图表:
...您现在可以在此 excellent example from the Plotly community forum 上构建,添加以下内容:
fig.data[1].fill = 'tozeroy'
...然后得到这个:
完整代码:
import numpy as np
import pandas as pd
import plotly.graph_objects as go #plotly 4.0.0rc1
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')
low = df['AAPL.Low'].tolist()
high = np.array(df['AAPL.High'])+20 # artificially added 20 to get the second graph above the first one
trace1 = go.Scatter(x=df.Date[:2],
y=low[:2],
mode='lines',
line=dict(width=1.5))
trace2 = go.Scatter(x = df.Date[:2],
y = high[:2],
mode='lines',
line=dict(width=1.5))
frames = [dict(data= [dict(type='scatter',
x=df.Date[:k+1],
y=low[:k+1]),
dict(type='scatter',
x=df.Date[:k+1],
y=high[:k+1])],
traces= [0, 1], #this means that frames[k]['data'][0] updates trace1, and frames[k]['data'][1], trace2
)for k in range(1, len(low)-1)]
layout = go.Layout(width=650,
height=400,
showlegend=False,
hovermode='closest',
updatemenus=[dict(type='buttons', showactive=False,
y=1.05,
x=1.15,
xanchor='right',
yanchor='top',
pad=dict(t=0, r=10),
buttons=[dict(label='Play',
method='animate',
args=[None,
dict(frame=dict(duration=3,
redraw=False),
transition=dict(duration=0),
fromcurrent=True,
mode='immediate')])])])
layout.update(xaxis =dict(range=[df.Date[0], df.Date[len(df)-1]], autorange=False),
yaxis =dict(range=[min(low)-0.5, high.max()+0.5], autorange=False));
fig = go.Figure(data=[trace1, trace2], frames=frames, layout=layout)
f = fig.full_figure_for_development(warn=False)
fig.data[1].fill = 'tozeroy'
fig.show()
【讨论】:
以上是关于在 Jupyter 中使用 plotly 在离线模式下创建动画图表的主要内容,如果未能解决你的问题,请参考以下文章