# Plotting with Observables in Makie

**URL:** https://discourse.julialang.org/t/plotting-with-observables-in-makie/70939
**Category:** Visualization
**Tags:** makie
**Created:** [November 4, 2021, 8:16am UTC](https://discourse.julialang.org/t/plotting-with-observables-in-makie/70939 "2021-11-04T08:16:14Z")
**Posts on this page:** 7
**Page:** 1

<div class="post-metadata">

### Author: ![jm4h](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jm4h/32/27442_2.png) [@jm4h](https://discourse.julialang.org/u/jm4h)
#### Post date: [November 4, 2021, 8:16am UTC](https://discourse.julialang.org/t/plotting-with-observables-in-makie/70939/1 "2021-11-04T08:16:14Z")

</div>

I want to make an interactive plot with sliders, where the displayed data changes when my sliders get moved. That seems to work when the observable plotted is an array of positions.  
But is it possible if the observable is something else, say a dataframe?

For example, this works nicely (adapted from an old Makie issue post):

```julia
using GLMakie
fig = Figure()
ax = Axis(fig[1,1:2])
s1 = Slider(fig[2,1], range = 0.1:0.1:10, startvalue = 3)
s2 = Slider(fig[2,2], range = LinRange(-2pi, 2pi, 100), startvalue = 0.0)
data = lift(s2.value) do v
    map(LinRange(0, 2pi, 100)) do x
        4f0 .* Point2f0(sin(x) + (sin(x * v) .* 0.1), cos(x) + (cos(x * v) .* 0.1))
    end
end
p = scatter!(fig[1,1:2], data, markersize = s1.value)

```

 ![image](https://global.discourse-cdn.com/julialang/original/3X/6/6/66379a0b04fadfff5c53e5088a291c5aa2246d3d.png)

But what would I do if the data Observable is not a nice positions array? Then updates don’t seem to work right:

```julia
data = lift(s2.value) do v
    xs = [4f0 .* (sin(x) + (sin(x * v) .* 0.1)) for x in LinRange(0, 2pi, 100)]
    ys = [4f0 .* (cos(x) + (cos(x * v) .* 0.1)) for x in LinRange(0, 2pi, 100)]
    ys2 = [4f0 .* (cos(x) + (cos(x * v) .* 0.3)) for x in LinRange(0, 2pi, 100)]
    [xs, ys, ys2]
end
p = scatter!(fig[1,1:2], data[][1], data[][2], markersize = s1.value)

```

There the first slider still changes the point size, and the second slider still changes the underlying data, but it does not get replotted. What could I do here?

I tried using

```julia
p = lift(data) do d
    scatter!(d[1], d[2], markersize = s1.value)
end

```

but that adds entirely new plots to the graph, i.e. just overlays new points into the plot, whenever the slider is moved.

What’s the best way to get a plot that updates correctly, if say the Observable is an array, or maybe even a DataFrame?

The only way I could think about doing this is creating a new separate observable (linked to the data observable) that converts each data part I want to plot into a positions array.

```julia
data = lift(s2.value) do v
    xs = [4f0 .* (sin(x) + (sin(x * v) .* 0.1)) for x in LinRange(0, 2pi, 100)]
    ys = [4f0 .* (cos(x) + (cos(x * v) .* 0.1)) for x in LinRange(0, 2pi, 100)]
    ys2 = [4f0 .* (cos(x) + (cos(x * v) .* 0.3)) for x in LinRange(0, 2pi, 100)]
    [xs, ys, ys2]
end
data1 = lift(data) do v
    map(i -> Point2f0(data[][1][i], data[][2][i]), 1:1:length(data[][1]))
end
data2 = lift(data) do v
    map(i -> Point2f0(data[][1][i], data[][3][i]), 1:1:length(data[][1]))
end
p = scatter!(fig[1,1:2], data1, markersize = s1.value)
p = scatter!(fig[1,1:2], data2, markersize = s1.value)

```

That works but seems very kludgy. And the selection of which columns to plot is hard coded.  
Is this really the only way to do this? Or is there a way to say just pass say the first and second column of the data array to scatter!(), and still have it update with a data change?

Side question, is there a way to autoscale the y axis to the data while the slider is being moved?

---

<div class="post-metadata">

### Author: ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)
#### Post date: [November 7, 2021, 10:40am UTC](https://discourse.julialang.org/t/plotting-with-observables-in-makie/70939/2 "2021-11-07T10:40:41Z")

</div>

> [@jm4h](#):
>
> `scatter!(fig[1,1:2], data[][1], data[][2]`

When you do this, you disable the observable functionality because you grab the plain array from inside the observable. Plots can only update automatically if they are passed observables.

If you have a dataframe that should send updates to a plot, you could do it like this: Put the dataframe in one observable. Make, e.g., two observables with `Symbol`s that decide which columns to plot. Then create a vector of points by lifting those three observables:

```julia
df_obs = Observable(DataFrame(x = [1, 2, 3], y = [4, 5, 6], z = [7, 8, 9])
col_1 = Observable(:x)
col_2 = Observable(:y)

data = @lift(Point2f.($df_obs[:, $col_1], $df_obs[:, $col_2]))

scatter(data)

```

Now you can plot x against z instead by doing `col_2[] = :z`. Or you mutate your dataframe somehow, but this will not directly trigger the Observable. An observable is only triggered if you replace its content with the `obs[] = x` syntax (but you don’t have a new dataframe). So if you do something inside the dataframe and want to update the plot, you just do `notify(df_obs)` instead.

---

<div class="post-metadata">

### Author: ![jm4h](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jm4h/32/27442_2.png) [@jm4h](https://discourse.julialang.org/u/jm4h)
#### Post date: [November 9, 2021, 5:57pm UTC](https://discourse.julialang.org/t/plotting-with-observables-in-makie/70939/3 "2021-11-09T17:57:22Z")

</div>

Those are great suggestions, thanks for the ideas! That’s exactly what I was looking for.

It would be nice if scatter!() had some sort of simpler syntax that would work for dataframe observable columns, or in general for quantities derived from observables.

Maybe something along the lines of plot routines having a method accepting a {observable, quantity derived from observable} pair as x, y or z data. If the observable changes, it would update the plot using the derived quantity. E.g. passing [df\_obs, df\_obs.col\_1] .

---

<div class="post-metadata">

### Author: ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)
#### Post date: [November 9, 2021, 9:18pm UTC](https://discourse.julialang.org/t/plotting-with-observables-in-makie/70939/4 "2021-11-09T21:18:18Z")

</div>

That’s kind of what the `@lift` macro is for, you could also plot `scatter(@lift($df_obs.some_column))` I think

---

<div class="post-metadata">

### Author: ![ederag](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ederag/32/4106_2.png) [@ederag](https://discourse.julialang.org/u/ederag)
#### Post date: [November 10, 2021, 8:12am UTC](https://discourse.julialang.org/t/plotting-with-observables-in-makie/70939/5 "2021-11-10T08:12:38Z")

</div>

As a complement for other readers, the relevant section of the manual is [Observables & Interaction](https://makie.juliaplots.org/stable/documentation/nodes/index.html#observables_interaction).

---

<div class="post-metadata">

### Author: ![jm4h](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jm4h/32/27442_2.png) [@jm4h](https://discourse.julialang.org/u/jm4h)
#### Post date: [November 11, 2021, 2:22am UTC](https://discourse.julialang.org/t/plotting-with-observables-in-makie/70939/6 "2021-11-11T02:22:10Z")

</div>

Thanks again! That’s what I was trying to do originally, but I couldn’t find the magic incantation for getting the dataframe column from the observable inside `@lift`.  
Using your example line gives me:

```julia
fig = Figure()
ax = Axis(fig[1,1])
df_obs = Observable(DataFrame(x = [1, 2, 3], y = [4, 5, 6]))
scatter!(@lift($df_obs.x), @lift($df_obs.y))

```

which works nicely with scatter(x, y), and the plot updates when the data is modified.  
(e.g. `df_obs[].y[1]=5` and then `notify(df_obs)` or `df_obs[]=df_obs[]`)

I think I get it now!

It was the syntax that was pretty confusing to me: In the REPL, `df_obs.x` gives an error (Field x not found), which makes sense since `df_obs` is an observable not a dataframe. So I must use `df_obs[].x` to get the dataframe column.  
But inside the `@lift` macro, using `$df_obs.x` works! I didn’t expect that. I guess it’s not interpreted as `$(df_obs.x)`, which would be an error, but as `($df_obs).x`, where **`$df_obs` gives me what’s contained inside the observable - without using `[]` -** and not the observable itself.

---

<div class="post-metadata">

### Author: ![jules](https://avatars.discourse-cdn.com/v4/letter/j/41988e/32.png) [@jules](https://discourse.julialang.org/u/jules)
#### Post date: [November 11, 2021, 6:49am UTC](https://discourse.julialang.org/t/plotting-with-observables-in-makie/70939/7 "2021-11-11T06:49:10Z")

</div>

In the macro you mark all expressions that should evaluate to an observable with $ and then you don’t have to specifically extract their value as well, just like you work with the extracted values inside the lift function itself.
