Is there a function like range()
but the steps are multiplicative?
Something like
logrange(start,stepmul,length) = cumprod([start,(ones(length)*stepmul)...])
Is there a function like range()
but the steps are multiplicative?
Something like
logrange(start,stepmul,length) = cumprod([start,(ones(length)*stepmul)...])
You can use broadcasting to apply a log transform to a range:
julia> logrange(start,stepmul,length) = start .* stepmul .^ (0:(length-1))
logrange (generic function with 1 method)
julia> logrange(3, 4, 5)
5-element Vector{Int64}:
3
12
48
192
768
Thereās a PR to add it to base Add a lazy `LogRange` function by mcabbott Ā· Pull Request #39071 Ā· JuliaLang/julia Ā· GitHub. It tuns out that getting it perfect is surprisingly difficult.
The tricky thing about a function which takes the ratio (stepmul
above) is that it will tend to create an expectation that this be interpreted exactly.
Linear ranges like 0:0.1:1
do this, by some serious black magic. It knows you mean 1//10
even though that isnāt precisely represented by any Float64. Doing cumsum(fill(0.1, 10))
will miss the āobviousā endpoint of 1.0
.
Similar things go wrong for ratio 0.1
:
julia> logrange1(100, 0.1, 5) # from 1st message, with stepmul
6-element Vector{Float64}:
100.0
10.0
1.0
0.1
0.010000000000000002
0.0010000000000000002
julia> logrange2(100, 0.1, 5) # from 2nd message above
5-element Vector{Float64}:
100.0
10.0
1.0000000000000002
0.10000000000000002
0.010000000000000002
julia> logrange(100, ratio=0.1, length=5) # lazy PowerRange, somewhere in PR
5-element PowerRange{Float64}:
100.0
10.0
1.0000000000000002
0.10000000000000002
0.010000000000000002
The linked PR avoids this by taking the first & last values. Then thereās no chance that it doesnāt stop where you want, it can simply guarantee to hit 0.01
exactly, without enquiring further. (It tries hard to be accurate in between, but isnāt perfect.)
julia> Base.logrange(100, 0.01, length=5)
5-element LogRange{Float64, Base.TwicePrecision{Float64}}:
100.0, 10.0, 1.0, 0.1, 0.01
julia> collect(ans)
5-element Vector{Float64}:
100.0
10.0
1.0
0.1
0.01
And apart from such details, in real use I think itās common to do that, and then decide you want more / less points (on your plot), or a wider / narrower range (to exclude the bad ones). With the ratio specified, you have to experiment a bit. Specifying the ends and length seems to me more often useful.
FlexiMaps.jl provides logrange already, lazy/general/performant:
julia> using FlexiMaps
julia> maprange(log, 0.1, 100, length=4) # ā [0.1, 1, 10, 100]
Specifying the step ratio directly would also be useful sometimes, but not supported (yet?).