Makie Broken Axis on Log Scale

Hello!

I have some data I’d like to plot with a broken axis. Basically there is one data point at the beginning that is way different than the rest.

I found this post about implementing a broken axis. I tried to adapt it in the MWE below but the result is not right.

Any ideas on how to get this to correctly plot?

data = [2.9037750420063544e-14
    0.010080615135321012
    0.009435526507153675
    0.011950631745492903
    0.020249366000687767
    0.029453566657917533
    0.03378641257881644
    0.04495876725797948
]
n = length(data)
times = 1:n

begin
    fig = Figure()

    below_break = (1E-15, 1E-13)
    above_break = (1E-4, 1E0)
    lims = Observable((below_break, above_break))

    gL = GridLayout(fig[1, 1])
    ax_top = Axis(gL[1, 1], yscale=log10)
    ax_bot = Axis(gL[2, 1], yscale=log10)

    on(lims) do (bottom, top)
        ylims!(ax_bot, bottom)
        ylims!(ax_top, top)
        rowsize!(gL, 1, Auto(top[2] - top[1]))
        rowsize!(gL, 2, Auto(bottom[2] - bottom[1]))
    end

    hidexdecorations!(ax_top, grid=false)
    ax_top.bottomspinevisible = false
    ax_bot.topspinevisible = false

    linkxaxes!(ax_top, ax_bot)
    rowgap!(gL, 10)

    angle = pi / 8
    linelength = 30

    segments = lift(
        @lift($(ax_top.yaxis.attributes.endpoints)[1]),
        @lift($(ax_bot.yaxis.attributes.endpoints)[2]),
        @lift($(ax_top.elements[:yoppositeline][1])[1]),
        @lift($(ax_bot.elements[:yoppositeline][1])[2]),
    ) do p1, p2, p3, p4
        ps = Point2f[p1, p2, p3, p4]

        map(ps) do p
            a = p + Point2f(cos(angle), sin(angle)) * 0.5 * linelength
            b = p - Point2f(cos(angle), sin(angle)) * 0.5 * linelength
            (a, b)
        end
    end

    linesegments!(fig.scene, segments, color=:black)

    scatterlines!(ax_bot, times, data)
    scatterlines!(ax_top, times, data)
    notify(lims)

    fig
end

Yeah the rowsize! commands set the size ratios of the limits in untransformed space so the size of the lower one is effectively zero. I think you need to take the differences of the log limits to get the right relative sizes.

Hey - thank you! I didn’t realize that was what rowsize! was doing. Given that, I just tweaked the numbers till they gave me something like I was happy with. You can see the result below. I also added some lines to make it clear where the break was.

data = [2.9037750420063544e-14
    0.010080615135321012
    0.009435526507153675
    0.011950631745492903
    0.020249366000687767
    0.029453566657917533
    0.03378641257881644
    0.04495876725797948
]
n = length(data)
times = 1:n

begin
    fig = Figure()

    below_break = (1E-15, 5E-12)
    above_break = (5E-4, 1E-1)
    lims = Observable((below_break, above_break))

    gL = GridLayout(fig[1, 1])
    ax_top = Axis(gL[1, 1], yscale=log10, xgridvisible=false, ygridvisible=false)
    ax_bot = Axis(gL[2, 1], yscale=log10, xgridvisible=false, ygridvisible=false)

    on(lims) do (bottom, top)
        ylims!(ax_bot, bottom)
        ylims!(ax_top, top)
        rowsize!(gL, 1, Auto(0.75))
        rowsize!(gL, 2, Auto(0.28))
    end

    hidexdecorations!(ax_top, grid=false)
    ax_top.bottomspinevisible = false
    ax_bot.topspinevisible = false

    linkxaxes!(ax_top, ax_bot)
    rowgap!(gL, 10)

    angle = pi / 8
    linelength = 30

    segments = lift(
        @lift($(ax_top.yaxis.attributes.endpoints)[1]),
        @lift($(ax_bot.yaxis.attributes.endpoints)[2]),
        @lift($(ax_top.elements[:yoppositeline][1])[1]),
        @lift($(ax_bot.elements[:yoppositeline][1])[2]),
    ) do p1, p2, p3, p4
        ps = Point2f[p1, p2, p3, p4]

        map(ps) do p
            a = p + Point2f(cos(angle), sin(angle)) * 0.5 * linelength
            b = p - Point2f(cos(angle), sin(angle)) * 0.5 * linelength
            (a, b)
        end
    end

    linesegments!(fig.scene, segments, color=:black)

    scatterlines!(ax_bot, times, data)
    scatterlines!(ax_top, times, data)
    hlines!(ax_top, 5E-4, linestyle=:dash, color=:gray)
    hlines!(ax_bot, 5E-12, linestyle=:dash, color=:gray)
    notify(lims)

    fig
end

I can start another topic if need be, but one other complication I am realizing is I need the y label to span correctly (span both top and bottom halves). Right now it will only be on the top or bottom half.

Edit:

Nevermind. I found a way to implement it using Label. See below.

Label(gL[1:2, 1, Left()], "Test", rotation=π / 2, padding=(0, 45, 0, 0))