Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add symlog and pseudolog #1109

Merged
merged 2 commits into from
Jul 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions docs/src/makielayout/axis.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ The default tick type is `LinearTicks(n)`, where `n` is the target number of tic

```@example
using CairoMakie
CairoMakie.activate!() # hide
Makie.inline!(true) # hide

fig = Figure()
for (i, n) in enumerate([2, 5, 9])
Expand All @@ -195,6 +197,8 @@ A common scenario is plotting a trigonometric function which should be marked at

```@example
using CairoMakie
CairoMakie.activate!() # hide
Makie.inline!(true) # hide

lines(0..20, sin, axis = (xticks = MultiplesTicks(4, pi, "π"),))
```
Expand All @@ -203,6 +207,8 @@ Here are a couple of examples that show off different settings for ticks and for

```@example
using CairoMakie
CairoMakie.activate!() # hide
Makie.inline!(true) # hide

scene, layout = layoutscene(resolution = (1200, 900))

Expand Down Expand Up @@ -240,6 +246,8 @@ The default minor tick type is `IntervalsBetween(n, mirror = true)` where `n` gi

```@example
using CairoMakie
CairoMakie.activate!() # hide
Makie.inline!(true) # hide

theme = Attributes(
Axis = (
Expand Down Expand Up @@ -271,6 +279,8 @@ To hide spines, you can use `hidespines!`.

```@example
using CairoMakie
CairoMakie.activate!() # hide
Makie.inline!(true) # hide

f = Figure()

Expand All @@ -290,6 +300,8 @@ It's common, e.g., to hide everything but the grid lines in facet plots.

```@example
using CairoMakie
CairoMakie.activate!() # hide
Makie.inline!(true) # hide

f = Figure()

Expand All @@ -312,6 +324,8 @@ Take care that the axis limits always stay inside the limits appropriate for the

```@example
using CairoMakie
CairoMakie.activate!() # hide
Makie.inline!(true) # hide

data = LinRange(0.01, 0.99, 200)

Expand All @@ -330,10 +344,14 @@ end
f
```

### Pseudolog and symlog scales

Some plotting functions, like barplots or density plots, have offset parameters which are usually zero, which you have to set to some non-zero value explicitly so they work in `log` axes.

```@example
using CairoMakie
CairoMakie.activate!() # hide
Makie.inline!(true) # hide

processors = ["VAX-11/780", "Sun-4/260", "PowerPC 604",
"Alpha 21164", "Intel Pentium III", "Intel Xeon"]
Expand All @@ -347,6 +365,33 @@ ylims!(0.5, 10000)
current_figure()
```

Another option are pseudolog and symlog scales.
Pseudolog is similar to log, but modified in order to work for zero and for negative values.
The `pseudolog10` function is defined as `sign(x) * log10(abs(x) + 1)`.

Another option for symmetric log scales including zero is the symmetric log scale `Symlog10`, which combines a normal log scale with a linear scale between two boundary values around zero.

```@example
using CairoMakie
CairoMakie.activate!() # hide
Makie.inline!(true) # hide

f = Figure(resolution = (800, 700))

lines(f[1, 1], -100:0.1:100, axis = (
yscale = Makie.pseudolog10,
title = "Pseudolog scale",
yticks = [-100, -10, -1, 0, 1, 10, 100]))

lines(f[2, 1], -100:0.1:100, axis = (
yscale = Makie.Symlog10(10.0),
title = "Symlog10 with linear scaling between -10 and 10",
yticks = [-100, -10, 0, 10, 100]))

f
```


## Controlling Axis aspect ratios

If you're plotting images, you might want to force a specific aspect ratio
Expand Down
42 changes: 40 additions & 2 deletions src/layouting/transformation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -313,12 +313,50 @@ apply_transform(f::typeof(identity), r::Rect) = r
apply_transform(f::NTuple{2, typeof(identity)}, r::Rect) = r
apply_transform(f::NTuple{3, typeof(identity)}, r::Rect) = r


pseudolog10(x) = sign(x) * log10(abs(x) + 1)
inv_pseudolog10(x) = sign(x) * (exp10(abs(x)) - 1)

struct Symlog10
low::Float64
high::Float64
function Symlog10(low, high)
if !(low < 0 && high > 0)
error("Low bound needs to be smaller than 0 and high bound larger than 0. You gave $low, $high.")
end
new(Float64(low), Float64(high))
end
end
function Symlog10(x)
Symlog10(-x, x)
end
function (s::Symlog10)(x)
if x > 0
x <= s.high ? x / s.high * log10(s.high) : log10(x)
elseif x < 0
x >= s.low ? x / abs(s.low) * log10(abs(s.low)) : sign(x) * log10(abs(x))
else
x
end
end
function inv_symlog10(x, low, high)
if x > 0
l = log10(high)
x <= l ? x / l * high : exp10(x)
elseif x < 0
l = sign(x) * log10(abs(low))
x >= l ? x / l * abs(s.low) : sign(x) * exp10(abs(x))
else
x
end
end

inverse_transform(::typeof(identity)) = identity
inverse_transform(::typeof(log10)) = exp10
inverse_transform(::typeof(log)) = exp
inverse_transform(::typeof(log2)) = exp2
inverse_transform(::typeof(sqrt)) = x -> x ^ 2
inverse_transform(::typeof(pseudolog10)) = inv_pseudolog10
inverse_transform(F::Tuple) = map(inverse_transform, F)

inverse_transform(::typeof(logit)) = logistic
inverse_transform(x) = nothing
inverse_transform(s::Symlog10) = x -> inv_symlog10(x, s.low, s.high)
5 changes: 5 additions & 0 deletions src/makielayout/layoutables/axis.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1279,8 +1279,13 @@ defaultlimits(::typeof(log)) = (1.0, exp(3.0))
defaultlimits(::typeof(identity)) = (0.0, 10.0)
defaultlimits(::typeof(sqrt)) = (0.0, 100.0)
defaultlimits(::typeof(Makie.logit)) = (0.01, 0.99)
defaultlimits(::typeof(Makie.pseudolog10)) = (0.0, 100.0)
defaultlimits(::Makie.Symlog10) = (0.0, 100.0)

defined_interval(::typeof(identity)) = OpenInterval(-Inf, Inf)
defined_interval(::Union{typeof(log2), typeof(log10), typeof(log)}) = OpenInterval(0.0, Inf)
defined_interval(::typeof(sqrt)) = Interval{:closed,:open}(0, Inf)
defined_interval(::typeof(Makie.logit)) = OpenInterval(0.0, 1.0)
defined_interval(::typeof(Makie.pseudolog10)) = OpenInterval(-Inf, Inf)
defined_interval(::Makie.Symlog10) = OpenInterval(-Inf, Inf)