I would like to turn information in a dataframe into a scatterplot which I can filter with help of sliders. What I came up with is this:
using Makie, GLMakie
fig = Figure() # initialize canvas
plot = Axis(fig[1, 1]) # initialize plot
sg = SliderGrid( # create slider grid
fig[2, 1],
(label = "x-limit", range = 0:0.1:10, format = "{:.1f}", startvalue = 0.5),
(label = "y-limit", range = 0:0.1:10, format = "{:.1f}", startvalue = 0.5),
)
xlimy, ylimy = sg.sliders[1].value, sg.sliders[2].value # turn slider values into simple observable variables
df = DataFrame("xcoord" => rand(10)*10, "ycoord" => rand(10)*10) # create random dataframe
onany(xlimy, xlimy) do value_x, value_y # if any of the slider values change run this block of code
begin
@isdefined(scattery) && delete!(plot, scattery) # my hope was to check if there already is a scatter plot in my canvas. if so i wanted it to get deleted. this doesn't work
temp_df = filter(x -> (x.xcoord >= value_x) & (x.ycoord >= value_y), df) # filter df based on slider values
scattery = scatter!(plot, temp_df.xcoord, temp_df.ycoord) # draw a scatterplot on canvas
end
end
My plot doesnt behave as expected. whenever i change slider values the old scatterplot remains and a new scatterplot layer is written over it (you can tell by how the dot colours change). Also, the y-slider doesn’t seem to be working at all. What I’d like is one single scatterplot layer that automatically adjusts according to the slider values.
I also tried some solution with @lift, which I didnt get to work at all. can anyone help me out? Thanks in advance
using GLMakie, DataFrames
fig = Figure() # initialize canvas
plot = Axis(fig[1, 1]) # initialize plot
sg = SliderGrid( # create slider grid
fig[2, 1],
(label = "x-limit", range = 0:0.1:10, format = "{:.1f}", startvalue = 0.5),
(label = "y-limit", range = 0:0.1:10, format = "{:.1f}", startvalue = 0.5),
)
xlimy, ylimy = sg.sliders[1].value, sg.sliders[2].value # turn slider values into simple observable variables
df = DataFrame("xcoord" => rand(10)*10, "ycoord" => rand(10)*10) # create random dataframe
points = lift(xlimy,ylimy) do value_x,value_y
temp_df = filter(x -> (x.xcoord >= value_x) & (x.ycoord >= value_y), df)
[Point2f(x,y) for (x,y) in zip(temp_df.xcoord,temp_df.ycoord)]
end
scatter!(plot,points)
If you see, scatter! is called only once but the array of points is what evolves through observables. In your case, you were plotting something new every time the sliders moved. My implementation is not very well thought so it is not the best.
A couple tangential comments:
No need to import Makie, only GLMakie is needed. If you are following a tutorial where both are used you may be looking at something old.
Where you do, onany, you are using both time xlimy, I think you want ylimy in one of those.
A difference to what I tried before was the list comprehension returnin an array of Point2f objects, I wasnt aware this is how the points need to get defined. Do I need to know more of the GLMakie-compatible objects for other kinds of plots? is there e.g. another specific struct that we need for interactive 3D plotting?
Ah yes. In my experience it is useful to have both coordinates in a point and not as independent observables by themselves. If you do not sometimes it happens that one gets updated too fast before the other one and you will get an error because the vectors x and y are not the same length. If you have a Point2f this does not happen.
I think the documentation in Makie is super well written and thought so it is a great resource to go through those examples and tutorials Tutorials