Makie.jl: [Feature request] Log scales

Created on 1 May 2019  ·  5Comments  ·  Source: JuliaPlots/Makie.jl

Log scales would be nice. @SebastianM-C has put together an example of how it can be done, but I would like to integrate it more into the plotting pipeline - maybe as an Attribute of an Axis.

scale = (:identity, :identity) or (:identity, :identity, :identity) as default and then you could have :log, :log2, :exp10, :asinh, :sqrt, ... depending on the scaling function

AbstractPlotting enhancement planning

Most helpful comment

I did a long example how to to make a matlab-like log10 plot using MakieLayout.
I don't know how to handle the internals of Makie, therefore for sure my code can be optimized and improved. But for sinple codes, is enough.

using AbstractPlotting
using CairoMakie
using FileIO
using MakieLayout
using UnicodeFun
using IterTools

function createLog10_AxisTicks(axes_interest, raw_data_interest; isX=false)
        min_Log10Value = minimum(log10.(raw_data_interest))
        max_Log10Value = maximum(log10.(raw_data_interest))
        if isX
                default_tick_values = MakieLayout.get_tickvalues(axes_interest.xticks.val, min_Log10Value,max_Log10Value)
        else
                default_tick_values = MakieLayout.get_tickvalues(axes_interest.yticks.val, min_Log10Value,max_Log10Value)
        end
        ticks_to_display = unique(round.(Int, default_tick_values)) # I want to avoid non-integer numbers, like "3.5"
        full_tick_span = range(minimum(ticks_to_display)-1, maximum(ticks_to_display)+1, step=1)

        tick_ranges, tick_labels = [], []

        isFirstInterval = true
        for interval_limits in partition(full_tick_span, 2, 1)
           # create latex text for the interval
           raw_text = raw"10^{".*string.(interval_limits).*"}"
           latex_text = to_latex.(raw_text)

           # compute numbers inside interval in log10 scale
           values_inside_interval = collect(log10.(range(10.0^interval_limits[1], 10.0^interval_limits[2], length=10)))
           labels_inside_interval = [" " for _=1:10] # do not show the values in the middle, but I need some marker

           if interval_limits[1] ∈ ticks_to_display
                   labels_inside_interval[1] = latex_text[1]
           end
           if interval_limits[2] ∈ ticks_to_display
                   labels_inside_interval[end] = latex_text[end]
           end

           if isFirstInterval
                   [push!(tick_ranges, values_inside_interval[n]) for n = 1:10 ]
                   [push!(tick_labels, labels_inside_interval[n]) for n = 1:10 ]
                   isFirstInterval = false
           else
                   # I want to avoid repetitions of values at the interval limits
                   # Therefore, I ignore the first element
                   [push!(tick_ranges, values_inside_interval[n]) for n = 2:10 ]
                   [push!(tick_labels, labels_inside_interval[n]) for n = 2:10 ]
           end
        end
        return tick_ranges, tick_labels
end
xscale_log10(axes_interest, raw_data_interest) = createLog10_AxisTicks(axes_interest, raw_data_interest;isX=true)
yscale_log10(axes_interest, raw_data_interest) = createLog10_AxisTicks(axes_interest, raw_data_interest;isX=false)


x = range(4,10, step=0.001)
y1 = exp.(-x)
y2 = exp.(-x/2)
y3 = x.^(-1.5)

scene, layout = layoutscene(1, 1, 30, resolution = (500, 500))
ax1 = layout[1, 1] = LAxis(scene, title=" log10 example ")

scatter!(ax1, x, log10.(y1), color=:blue)
scatter!(ax1, x, log10.(y2), color=:green)
scatter!(ax1, x, log10.(y3), color=:purple)
scene 

allYdata = vcat(y1, y2, y3) # I don't know how to access data inside ax1 structure, then, I passed it as argument
ax1.xlabel = "x"
ax1.ylabel = "y"
ax1.yticks = yscale_log10(ax1, allYdata)
scene

save("log10example.png", scene)

All 5 comments

For a logscale on the Oy axis, based on the discussion in JuliaPlots/Makie.jl#551, I used something like this:

using UnicodeFun

function log_ticks(lims, n)
    a = round(lims[1], RoundNearest)
    b = round(lims[2], RoundNearest)
    r = range(a, b, length=n)
    l = raw"10^{".*string.(r).*"}"
    t = to_latex.(l)
    return r, t
end

r, t = log_ticks(ey, 5)
ranges = Node((xrange, r))
labels = Node((xlabel, t))

plot(..., axis = (
            names = (axisnames=("x", "log₁₀(y)"),),
            ticks = (
                ranges = ranges,
                labels = labels
            ),
        ),
)

Perhaps a better API would be passing a Union{Function, Symbol} but we don't guarantee optimized ticks for functions

This might be better to lump in with the axis refactor that will be necessary for layouting.

has there been any progress on this?

I did a long example how to to make a matlab-like log10 plot using MakieLayout.
I don't know how to handle the internals of Makie, therefore for sure my code can be optimized and improved. But for sinple codes, is enough.

using AbstractPlotting
using CairoMakie
using FileIO
using MakieLayout
using UnicodeFun
using IterTools

function createLog10_AxisTicks(axes_interest, raw_data_interest; isX=false)
        min_Log10Value = minimum(log10.(raw_data_interest))
        max_Log10Value = maximum(log10.(raw_data_interest))
        if isX
                default_tick_values = MakieLayout.get_tickvalues(axes_interest.xticks.val, min_Log10Value,max_Log10Value)
        else
                default_tick_values = MakieLayout.get_tickvalues(axes_interest.yticks.val, min_Log10Value,max_Log10Value)
        end
        ticks_to_display = unique(round.(Int, default_tick_values)) # I want to avoid non-integer numbers, like "3.5"
        full_tick_span = range(minimum(ticks_to_display)-1, maximum(ticks_to_display)+1, step=1)

        tick_ranges, tick_labels = [], []

        isFirstInterval = true
        for interval_limits in partition(full_tick_span, 2, 1)
           # create latex text for the interval
           raw_text = raw"10^{".*string.(interval_limits).*"}"
           latex_text = to_latex.(raw_text)

           # compute numbers inside interval in log10 scale
           values_inside_interval = collect(log10.(range(10.0^interval_limits[1], 10.0^interval_limits[2], length=10)))
           labels_inside_interval = [" " for _=1:10] # do not show the values in the middle, but I need some marker

           if interval_limits[1] ∈ ticks_to_display
                   labels_inside_interval[1] = latex_text[1]
           end
           if interval_limits[2] ∈ ticks_to_display
                   labels_inside_interval[end] = latex_text[end]
           end

           if isFirstInterval
                   [push!(tick_ranges, values_inside_interval[n]) for n = 1:10 ]
                   [push!(tick_labels, labels_inside_interval[n]) for n = 1:10 ]
                   isFirstInterval = false
           else
                   # I want to avoid repetitions of values at the interval limits
                   # Therefore, I ignore the first element
                   [push!(tick_ranges, values_inside_interval[n]) for n = 2:10 ]
                   [push!(tick_labels, labels_inside_interval[n]) for n = 2:10 ]
           end
        end
        return tick_ranges, tick_labels
end
xscale_log10(axes_interest, raw_data_interest) = createLog10_AxisTicks(axes_interest, raw_data_interest;isX=true)
yscale_log10(axes_interest, raw_data_interest) = createLog10_AxisTicks(axes_interest, raw_data_interest;isX=false)


x = range(4,10, step=0.001)
y1 = exp.(-x)
y2 = exp.(-x/2)
y3 = x.^(-1.5)

scene, layout = layoutscene(1, 1, 30, resolution = (500, 500))
ax1 = layout[1, 1] = LAxis(scene, title=" log10 example ")

scatter!(ax1, x, log10.(y1), color=:blue)
scatter!(ax1, x, log10.(y2), color=:green)
scatter!(ax1, x, log10.(y3), color=:purple)
scene 

allYdata = vcat(y1, y2, y3) # I don't know how to access data inside ax1 structure, then, I passed it as argument
ax1.xlabel = "x"
ax1.ylabel = "y"
ax1.yticks = yscale_log10(ax1, allYdata)
scene

save("log10example.png", scene)
Was this page helpful?
0 / 5 - 0 ratings

Related issues

SebastianM-C picture SebastianM-C  ·  3Comments

daviehh picture daviehh  ·  7Comments

phlavenk picture phlavenk  ·  6Comments

MartinOtter picture MartinOtter  ·  5Comments

AlexisRenchon picture AlexisRenchon  ·  3Comments