Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Underlying plot data dissapears when animation frames are added #2423

Closed
piotr-rarus opened this issue Apr 30, 2020 · 7 comments
Closed

Underlying plot data dissapears when animation frames are added #2423

piotr-rarus opened this issue Apr 30, 2020 · 7 comments

Comments

@piotr-rarus
Copy link

Hello folks,
I'm posting this issue as I couldn't find answer on stackoverflow and in the docs.
I'm trying to create animated plot with some data underneath, and animate points in consecutive frames. When I add the frames to the plot my underlying spline dissapears. I'm attaching exemplary script which you can paste into jupyter notebook.

Python 3.7.5

jupyter==1.0.0
numpy==1.18.1
plotly==4.5.4
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 = np.linspace(
            start=0,
            stop=20,
            num=100,
            endpoint=True
        )

        y = np.array([math.sin(x_i) for x_i in x])

        x_min = np.min(x)
        x_max = np.max(x)

        y_min = np.min(y)
        y_max = np.max(y)

        fig = go.Figure(
            data=[
                go.Scatter(
                    y=y,
                    x=x,
                    mode="lines",
                    line_shape='spline'
                )
            ]
        )

        frames = []

        for i in range(50):
            x = np.random.random_sample(size=5)
            x *= 20

            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])

            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()

Without frames

obraz

With frames

Consecutive frames are missing underlying spline.

obraz

@nicolaskruchten
Copy link
Contributor

With animations, each frame replaces the last, so here it looks like you're adding a spline to the first frame but not the subsequent ones, and hence you're not seeing the spline throughout the animation...

@piotr-rarus
Copy link
Author

piotr-rarus commented Apr 30, 2020

Thanks you @nicolaskruchten for the respond.

I've tried that as well, but to no prevail. Docs present creating frames only for data that changes.
According to them you can still pass data to master figure. It would be also little optimal performance-wise to create underlying plot/surface each time. docs

I've changed the script, but now it shows nothing. Neither the data nor the frames.

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'):
        fig = go.Figure()
        
        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.add_trace(spline)

        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, spline])

            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()

@empet
Copy link

empet commented Apr 30, 2020

When your fig.data contains only one trace then it is supposed that each frame updates that trace
and the trace is no more displayed during the animation.

That's why you must define:

fig = go.Figure(
            data=[
                go.Scatter(
                    y=y,
                    x=x,
                    mode="lines",
                    line_shape='spline'
                )]*2)

i.e. include the same trace twice in fig.data.

At the same time modify the frame definition as follows:

 frame = go.Frame(data=[scatter],
                  traces=[1])

traces = [1] informs plotly.js that each frame updates the trace fig.data[1], while fig.data[0] is unchanged during the animation.

@nicolaskruchten
Copy link
Contributor

Wow, I didn't know that was possible :) Adding to the #1965

@piotr-rarus
Copy link
Author

@empet it worked, I love you <3

Pasting final version of the code:

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()

@dtoniolo
Copy link

dtoniolo commented Mar 11, 2021

Hi, can you give us additional clarifications on how go.Traces work? To be more specific, my current understanding is the following;

  • the frames argument in the go.Figure constructor is just a container for the frames. To define an animations, controls need to be added that tell the figure which frames to pick and in which order.
  • Each frame modifies certain traces. Which ones will be affected is specified by the traces argument. If it is not given, the traces are chosen as the first ones in the data argument of the figure.
  • The data argument of the figure is used to determine the traces that exist before the animation is started.

However, I have the following questions:

  • Instead of using the traces argument of the go.Frame constructor, can we pass named traces to the data argument (of the same constructor)?
  • Can the frames be used to define new traces (i.e. traces that don't exist in the data argument of the figure) (e.g. if the figure only has one trace, but each frame as two)?
  • How can (if it is possible) data be shared across traces? I guess that the baseframe argument has something to do with this, but I'm not sure about its meaning.

I'll post it here because in the end this issue seems to be about the current lack of good documentation about the inner workings of go.Frame, but I can open a new issue if needed.

@gvwilson
Copy link
Contributor

Hi - we are trying to tidy up the stale issues and PRs in Plotly's public repositories so that we can focus on things that are still important to our community. Since this one has been sitting for several years, I'm going to close it; if it is still a concern, please add a comment letting us know what recent version of our software you've checked it with so that I can reopen it and add it to our backlog. Thanks for your help - @gvwilson

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants