我正在尝试使用plotly在固定表面上制作动画散点图。
这是我用来绘制表面的代码:
import plotly.graph_objects as go
def surface(x, y, z, opacity: float = 1.0) -> go.Figure:
fig = go.Figure()
fig.add_trace(
go.Surface(
x=x,
y=y,
z=z,
contours_z=dict(
show=True,
usecolormap=True,
project_z=True,
),
opacity=opacity
)
)
return fig
然后我试图在其上覆盖散点图。
def population(
self,
benchmark: CEC2013,
batch_stats: BatchStats,
filename: str = 'population'
):
# Here I'm creating the surface figure using previous method
surface = figure.surface.surface(*benchmark.surface, opacity=0.8)
frames = []
# Time to add some frames using Scatter 3D
for population in batch_stats.population_history:
x = [solution.genome[0] for solution in population.solutions]
y = [solution.genome[1] for solution in population.solutions]
fitness = population.fitness
frame = go.Frame(
data=[
go.Scatter3d(
x=x,
y=y,
z=fitness,
mode='markers',
marker=dict(
size=6,
color='#52CA34',
)
)
]
)
frames.append(frame)
# Update frames to root figure
surface.frames = frames
# just a fancy display, to make it work in offline mode in html render from notebook
pyo.iplot(
surface,
filename=filename,
image_width=self.IMAGE_WIDTH,
image_height=self.IMAGE_HEIGHT
)
表面仅在第一帧中显示。散点图显示在后续帧中,但没有底面。
代码位于dev分支上的here。根处有一个名为test_vis.ipynb
的调试笔记本。感谢您的帮助<3
我已将这个问题作为问题发布在plotly的repository。
这是我收到的答案。
@ empet-谢谢<3
当您的fig.data仅包含一条迹线时,则假定每一帧都更新该迹线并且在动画过程中不再显示轨迹。这就是为什么您必须定义:
fig = go.Figure(
data=[
go.Scatter(
y=y,
x=x,
mode="lines",
ine_shape='spline'
)
]*2
)
即在图数据中包含两次相同的迹线。同时修改框架定义,如下所示:
frame = go.Frame(data=[scatter], traces=[1])
[traces = [1]
通知plotly.js,每帧更新轨迹fig.data[1]
,而fig.data[0]
在动画过程中保持不变。
这是一个如何在某些基础图上制作动画的完整示例。
import math
import numpy as np
import plotly.graph_objects as go
import plotly.io as pio
import plotly.offline as pyo
class Plot:
def __init__(
self,
image_width: int = 1200,
image_height: int = 900
) -> None:
self.IMAGE_WIDTH = image_width
self.IMAGE_HEIGHT = image_height
pyo.init_notebook_mode(connected=False)
pio.renderers.default = 'notebook'
def population(self, filename: str = 'population'):
x_spline = np.linspace(
start=0,
stop=20,
num=100,
endpoint=True
)
y_spline = np.array([math.sin(x_i) for x_i in x_spline])
x_min = np.min(x_spline)
x_max = np.max(x_spline)
y_min = np.min(y_spline)
y_max = np.max(y_spline)
spline = go.Scatter(
y=y_spline,
x=x_spline,
mode="lines",
line_shape='spline'
)
fig = go.Figure(
data=[spline] * 2
)
frames = []
for i in range(50):
x = np.random.random_sample(size=5)
x *= x_max
y = np.array([math.sin(x_i) for x_i in x])
scatter = go.Scatter(
x=x,
y=y,
mode='markers',
marker=dict(
color='Green',
size=12,
line=dict(
color='Red',
width=2
)
),
)
frame = go.Frame(data=[scatter], traces=[1])
frames.append(frame)
fig.frames = frames
fig.layout = go.Layout(
xaxis=dict(
range=[x_min, x_max],
autorange=False
),
yaxis=dict(
range=[y_min, y_max],
autorange=False
),
title="Start Title",
updatemenus=[
dict(
type="buttons",
buttons=[
dict(
label="Play",
method="animate",
args=[None]
)
]
)
]
)
fig.update_layout(
xaxis_title='x',
yaxis_title='y',
title='Fitness landscape',
# autosize=True
)
pyo.iplot(
fig,
filename=filename,
image_width=self.IMAGE_WIDTH,
image_height=self.IMAGE_HEIGHT
)
plot = Plot()
plot.population()