How record a chart animation function

Hi, I’m a new user in Julia (but, I have large experience with R and Python).
Well, I’m create a function that create a figure (a grid of charts) bellow. The idea is the parameter i is the number of points or simulations. My target is create a movie (mp4 or gif) that shows the figure for each i (1:n). Example:

i = 70

I can create one-by-one. But, I want to create a animation with range 1.000.000 or more simulations (it is impossible to do manually). So, I search and found the function record() and I had tried for many forms without success.

The idea that I want:

indexes = 1:10
figRec = geraPlot(1)
record(figRec, "test_plot_movie.mp4",indexes) do i   
    figRec = geraPlot(i)
end

But, when I run this, the result is a short static movie stopped as figRec = geraPlot(1), the first figure only.

How I do it?
I not found a example in the Internet of record that use the figure as a function.


To get around this problem, I’ve generating many PNGs files, and after, I use other software to create a gif or movie with ones with the code:

#save figures
path = "D:\\Dev\\julia\\areaFunctionMC\\img\\"
for i in 1:nSimul
    save( path*"pl"*string(i)*".png", geraPlot(i))
end

But, I’d like to do it in the code with julia’s packages.

My code:

using LaTeXStrings
using GLMakie
using CairoMakie
using DataFrames
using LaTeXStrings
using Plots


# Simulation Data
    nSimul = 100
    x = 0:10    
    y = x .^ 2
    df = DataFrame(x=rand(nSimul)*10, y=rand(nSimul)*100, Fx=0.00, Δy=0.00, up_x=-1.00, up_y=-1.00, dw_x=-1.00, dw_y=-1.00)
    df.x .- df.y
    
    #Up or Down of the function y?
    df.Fx = df.x .^2
    df.Δy = df.y .- df.Fx
    for i in 1:nSimul
        if   df.Δy[i] .> 0
            df.up_x[i] = df.x[i]
            df.up_y[i] = df.y[i]
        else
            df.dw_x[i] = df.x[i]
            df.dw_y[i] = df.y[i]
        end
    end
    contXYdw = nrow(df[df.Δy .<= 0,:])

    #Calc integral
    AreaSquare = 10 * (10^2)
    integral = (contXYdw / nSimul) * AreaSquare
    integralCorreta = (10^3)/3

    #loop for i to generate the steps in a dataframe
    dfIntegral = DataFrame(n=1:nSimul, cont=0, valor=0.00, erro=0.00, Δ=0.00 )
    cont = 0
    area = 0
    for i in 1:nSimul
        if df[i,"Δy"] <= 0            
            cont = cont +1
        end        
        area = (cont / i) * AreaSquare
        dfIntegral.cont[i] = cont
        dfIntegral.valor[i] = area
        dfIntegral.erro[i] =  area - integralCorreta
        if i>1
            dfIntegral.Δ[i] = dfIntegral.valor[i] - dfIntegral.valor[i-1]
        end        
    end
    A_yMax = AreaSquare
    A_yMin = 0
    er_yMax = maximum(dfIntegral.erro)
    er_yMin = minimum(dfIntegral.erro)
    Δ_yMax = maximum(dfIntegral.Δ)
    Δ_yMin = minimum(dfIntegral.Δ)
   
# Now, plotting the dataframe per i

function geraPlot(i) #function that update the chart to each i points
    #i=10
    fig = Figure()
        # Graf 1

        xx1 = df.up_x[1:i]
        yy1 = df.up_y[1:i]
        xx2 = df.dw_x[1:i]
        yy2 = df.dw_y[1:i]

        ax1 = fig[1:4, 1] = Axis(fig, xlabel = L"x", ylabel = L"f (x)",
                            xgridstyle = :dash, ygridstyle = :dash)                            

        lines!(x, y; label = L"x^{2}")
        band!(x, fill(0, length(x)), y; color = (:blue, 0.4), label = L"A (x) = \int_0^{10} x^{2}dx")
        scatter!(xx1,yy1, rasterize = true, markersize = 4.0, color = (:gray, 1.), label = "∉A")
        scatter!(xx2,yy2, rasterize = true, markersize = 4.0, color = (:red, 1.), label = "∈A")

        xlims!(0, 10)
        ylims!(0, 100)
        #axislegend("Legend"; position = :rt, bgcolor = (:grey90, 0.25))
        
        fig[1, 2] = Legend(fig, ax1, "Legend", bgcolor = (:grey90, 0.25), framevisible= false)
        #display(fig)
   

        # Graf 3 - Integral Result x nSimulation
        xx1 = dfIntegral.n[1:i]
        yy1 = dfIntegral.valor[1:i]
        ax3 = fig[2, 2:3] = Axis(fig, title = "Integral value (A)")
            lines!(xx1,yy1)        
            xlims!(0, nSimul + 10)
            ylims!(A_yMin, A_yMax)

        # Graf 4 - Erro x nSimulation
        yy1 = dfIntegral.erro[1:i]        
        ax4 = Axis(fig[3, 2:3], title = "Erro")
            lines!(xx1,yy1)
            xlims!(0, nSimul + 10)
            ylims!(er_yMin, er_yMax)

        # Graf - Δ Resultado x nSimulation
        y1 =dfIntegral.Δ[1:i] 
        ax5 = Axis(fig[4, 2:3], title = "Δ = A(i) - A(i-1)", xlabel = "n points",)
            lines!(xx1,yy1)
            xlims!(0, nSimul + 10)
            ylims!(Δ_yMin, Δ_yMax)
    #display(fig)
    return fig
end

1 Like

Based on this, how about this approach:

using LaTeXStrings
using GLMakie
using DataFrames

function simulation_data(nSimul, AreaSquare)
    df = DataFrame(
        x = rand(nSimul) * 10,
        y = rand(nSimul) * 100,
        Fx = 0.00,
        Δy = 0.00,
        up_x = -1.00,
        up_y = -1.00,
        dw_x = -1.00,
        dw_y = -1.00,
    )
    # df.x .- df.y

    #Up or Down of the function y?
    df.Fx = df.x .^ 2
    df.Δy = df.y .- df.Fx
    for i = 1:nSimul
        if df.Δy[i] .> 0
            df.up_x[i] = df.x[i]
            df.up_y[i] = df.y[i]
        else
            df.dw_x[i] = df.x[i]
            df.dw_y[i] = df.y[i]
        end
    end
    contXYdw = nrow(df[df.Δy .<= 0, :])

    #Calc integral
    integral = (contXYdw / nSimul) * AreaSquare
    integralCorreta = (10^3) / 3

    #loop for i to generate the steps in a dataframe
    dfIntegral = DataFrame(n = 1:nSimul, cont = 0, valor = 0.00, erro = 0.00, Δ = 0.00)
    cont = 0
    area = 0
    for i = 1:nSimul
        if df[i, "Δy"] <= 0
            cont = cont + 1
        end
        area = (cont / i) * AreaSquare
        dfIntegral.cont[i] = cont
        dfIntegral.valor[i] = area
        dfIntegral.erro[i] = area - integralCorreta
        if i > 1
            dfIntegral.Δ[i] = dfIntegral.valor[i] - dfIntegral.valor[i - 1]
        end
    end
    return df, dfIntegral
end

# Now, plotting the dataframe per i

function geraPlot(df, dfIntegral, AreaSquare) #function that update the chart to each i points

    nSimul = size(df, 1)
    #i=10
    fig = Figure()
    # Graf 1

    i = Observable(1)

    up_pts = @lift Point2f.(df.up_x[1:($i)], df.up_y[1:($i)])
    dw_pts = @lift Point2f.(df.dw_x[1:($i)], df.dw_y[1:($i)])

    ax1 =
        fig[1:4, 1] = Axis(
            fig,
            xlabel = L"x",
            ylabel = L"f (x)",
            xgridstyle = :dash,
            ygridstyle = :dash,
        )

    x = 0:10
    y = x .^ 2
    lines!(x, y; label = L"x^{2}")
    band!(
        x,
        fill(0, length(x)),
        y;
        color = (:blue, 0.4),
        label = L"A (x) = \int_0^{10} x^{2}dx",
    )
    scatter!(up_pts, rasterize = true, markersize = 4.0, color = (:gray, 1.0), label = "∉A")
    scatter!(dw_pts, rasterize = true, markersize = 4.0, color = (:red, 1.0), label = "∈A")

    xlims!(0, 10)
    ylims!(0, 100)
    #axislegend("Legend"; position = :rt, bgcolor = (:grey90, 0.25))

    fig[1, 2] = Legend(fig, ax1, "Legend", bgcolor = (:grey90, 0.25), framevisible = false)
    #display(fig)

    # Graf 3 - Integral Result x nSimulation
    valor_pts = @lift Point2f.(dfIntegral.n[1:($i)], dfIntegral.valor[1:($i)])
    ax3 = fig[2, 2:3] = Axis(fig, title = "Integral value (A)")
    lines!(valor_pts)
    xlims!(0, nSimul + 10)
    A_yMax = AreaSquare
    A_yMin = 0
    ylims!(A_yMin, A_yMax)

    # Graf 4 - Erro x nSimulation
    erro_pts = @lift Point2f.(dfIntegral.n[1:($i)], dfIntegral.erro[1:($i)])
    ax4 = Axis(fig[3, 2:3], title = "Erro")
    lines!(erro_pts)
    xlims!(0, nSimul + 10)
    er_yMax = maximum(dfIntegral.erro)
    er_yMin = minimum(dfIntegral.erro)
    ylims!(er_yMin, er_yMax)

    # Graf - Δ Resultado x nSimulation
    Δ_pts = @lift Point2f.(dfIntegral.n[1:($i)], dfIntegral.Δ[1:($i)])
    ax5 = Axis(fig[4, 2:3], title = "Δ = A(i) - A(i-1)", xlabel = "n points")
    lines!(Δ_pts)
    xlims!(0, nSimul + 10)
    Δ_yMax = maximum(dfIntegral.Δ)
    Δ_yMin = minimum(dfIntegral.Δ)
    ylims!(Δ_yMin, Δ_yMax)
    #display(fig)
    return fig, i
end

function make_animation(output_filename, nSimul = 100, AreaSquare = 10 * 10^2)
    df, dfIntegral = simulation_data(nSimul, AreaSquare)
    figure, index = geraPlot(df, dfIntegral, AreaSquare)

    record(figure, output_filename, 1:nSimul; framerate = 30) do i
        index[] = i
    end
end

make_animation("test.gif")

test

5 Likes

Thank you so much!

So, here, the run result in error:

ERROR: UndefVarError: Point2f not defined

The problem is in the rows:

    up_pts = @lift Point2f.(df.up_x[1:($i)], df.up_y[1:($i)])
    dw_pts = @lift Point2f.(df.dw_x[1:($i)], df.dw_y[1:($i)])

How I can fix it?

Is your makie version so old that it doesn’t export Point2f?

I’m using the lastest version. Well, I tried to update all packages, and ok. But, only the package API has a error (I do not know why). Is it the problem?

I’m sorry but I don’t understand what you’re trying to say.

Sorry, @jules . I’m investigate and I think that the problem is the installation of package Meshes. Could you verify with your package Meshes is ok? And if the directory was created in the %AppData% Julia’s path?

https://discourse.julialang.org/t/error-i-cannot-install-package-meshes/84051