Fix log-scale for 1D bar/bin/histogram series types
This commit is contained in:
parent
1188230641
commit
56a9389023
@ -156,6 +156,8 @@ const _markerAliases = Dict{Symbol,Symbol}(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const _allScales = [:identity, :ln, :log2, :log10, :asinh, :sqrt]
|
const _allScales = [:identity, :ln, :log2, :log10, :asinh, :sqrt]
|
||||||
|
const _logScales = [:ln, :log2, :log10]
|
||||||
|
const _logScaleBases = Dict(:ln => e, :log2 => 2.0, :log10 => 10.0)
|
||||||
const _scaleAliases = Dict{Symbol,Symbol}(
|
const _scaleAliases = Dict{Symbol,Symbol}(
|
||||||
:none => :identity,
|
:none => :identity,
|
||||||
:log => :log10,
|
:log => :log10,
|
||||||
|
|||||||
@ -277,6 +277,13 @@ function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
|
|||||||
attr[Symbol(letter,k)] = v
|
attr[Symbol(letter,k)] = v
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
for k in (:scale,), letter in (:x,:y,:z)
|
||||||
|
# Series recipes may need access to this information
|
||||||
|
lk = Symbol(letter,k)
|
||||||
|
if haskey(attr, lk)
|
||||||
|
kw[lk] = attr[lk]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
sp_attrs[sp] = attr
|
sp_attrs[sp] = attr
|
||||||
end
|
end
|
||||||
|
|||||||
143
src/recipes.jl
143
src/recipes.jl
@ -323,10 +323,11 @@ end
|
|||||||
|
|
||||||
# create a bar plot as a filled step function
|
# create a bar plot as a filled step function
|
||||||
@recipe function f(::Type{Val{:bar}}, x, y, z)
|
@recipe function f(::Type{Val{:bar}}, x, y, z)
|
||||||
nx, ny = length(x), length(y)
|
procx, procy, xscale, yscale, baseline = _preprocess_binlike(d, x, y)
|
||||||
|
nx, ny = length(procx), length(procy)
|
||||||
axis = d[:subplot][isvertical(d) ? :xaxis : :yaxis]
|
axis = d[:subplot][isvertical(d) ? :xaxis : :yaxis]
|
||||||
cv = [discrete_value!(axis, xi)[1] for xi=x]
|
cv = [discrete_value!(axis, xi)[1] for xi=procx]
|
||||||
x = if nx == ny
|
procx = if nx == ny
|
||||||
cv
|
cv
|
||||||
elseif nx == ny + 1
|
elseif nx == ny + 1
|
||||||
0.5diff(cv) + cv[1:end-1]
|
0.5diff(cv) + cv[1:end-1]
|
||||||
@ -337,9 +338,9 @@ end
|
|||||||
# compute half-width of bars
|
# compute half-width of bars
|
||||||
bw = d[:bar_width]
|
bw = d[:bar_width]
|
||||||
hw = if bw == nothing
|
hw = if bw == nothing
|
||||||
0.5mean(diff(x))
|
0.5mean(diff(procx))
|
||||||
else
|
else
|
||||||
Float64[0.5cycle(bw,i) for i=1:length(x)]
|
Float64[0.5cycle(bw,i) for i=1:length(procx)]
|
||||||
end
|
end
|
||||||
|
|
||||||
# make fillto a vector... default fills to 0
|
# make fillto a vector... default fills to 0
|
||||||
@ -347,17 +348,22 @@ end
|
|||||||
if fillto == nothing
|
if fillto == nothing
|
||||||
fillto = 0
|
fillto = 0
|
||||||
end
|
end
|
||||||
|
if (yscale in _logScales) && !all(_is_positive, fillto)
|
||||||
|
fillto = map(x -> _is_positive(x) ? typeof(baseline)(x) : baseline, fillto)
|
||||||
|
end
|
||||||
|
|
||||||
# create the bar shapes by adding x/y segments
|
# create the bar shapes by adding x/y segments
|
||||||
xseg, yseg = Segments(), Segments()
|
xseg, yseg = Segments(), Segments()
|
||||||
for i=1:ny
|
for i=1:ny
|
||||||
center = x[i]
|
yi = procy[i]
|
||||||
|
if !isnan(yi)
|
||||||
|
center = procx[i]
|
||||||
hwi = cycle(hw,i)
|
hwi = cycle(hw,i)
|
||||||
yi = y[i]
|
|
||||||
fi = cycle(fillto,i)
|
fi = cycle(fillto,i)
|
||||||
push!(xseg, center-hwi, center-hwi, center+hwi, center+hwi, center-hwi)
|
push!(xseg, center-hwi, center-hwi, center+hwi, center+hwi, center-hwi)
|
||||||
push!(yseg, yi, fi, fi, yi, yi)
|
push!(yseg, yi, fi, fi, yi, yi)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# widen limits out a bit
|
# widen limits out a bit
|
||||||
expand_extrema!(axis, widen(extrema(xseg.pts)...))
|
expand_extrema!(axis, widen(extrema(xseg.pts)...))
|
||||||
@ -384,9 +390,51 @@ end
|
|||||||
|
|
||||||
_bin_centers(v::AVec) = (v[1:end-1] + v[2:end]) / 2
|
_bin_centers(v::AVec) = (v[1:end-1] + v[2:end]) / 2
|
||||||
|
|
||||||
|
_is_positive(x) = (x > 0) && !(x ≈ 0)
|
||||||
|
|
||||||
|
_positive_else_nan{T}(::Type{T}, x::Real) = _is_positive(x) ? T(x) : T(NaN)
|
||||||
|
|
||||||
|
function _scale_adjusted_values{T<:AbstractFloat}(::Type{T}, V::AbstractVector, scale::Symbol)
|
||||||
|
if scale in _logScales
|
||||||
|
[_positive_else_nan(T, x) for x in V]
|
||||||
|
else
|
||||||
|
[T(x) for x in V]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _hist_ylim_lo{T<:Real}(ymin::T, yscale::Symbol)
|
||||||
|
if (yscale in _logScales)
|
||||||
|
ymin / T(_logScaleBases[yscale]^log10(2))
|
||||||
|
else
|
||||||
|
zero(T)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function _hist_ylim_hi{T<:Real}(ymax::T, yscale::Symbol)
|
||||||
|
if (yscale in _logScales)
|
||||||
|
ymax * T(_logScaleBases[yscale]^log10(2))
|
||||||
|
else
|
||||||
|
ymax * T(1.1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function _preprocess_binlike(d, x, y)
|
||||||
|
xscale = get(d, :xscale, :identity)
|
||||||
|
yscale = get(d, :yscale, :identity)
|
||||||
|
|
||||||
|
T = float(promote_type(eltype(x), eltype(y)))
|
||||||
|
edge = map(T, x)
|
||||||
|
weights = _scale_adjusted_values(T, y, yscale)
|
||||||
|
w_min = minimum(weights)
|
||||||
|
baseline = _hist_ylim_lo(isnan(w_min) ? one(T) : w_min, yscale)
|
||||||
|
edge, weights, xscale, yscale, baseline
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@recipe function f(::Type{Val{:barbins}}, x, y, z)
|
@recipe function f(::Type{Val{:barbins}}, x, y, z)
|
||||||
edge, weights = x, y
|
edge, weights, xscale, yscale, baseline = _preprocess_binlike(d, x, y)
|
||||||
if (d[:bar_width] == nothing)
|
if (d[:bar_width] == nothing)
|
||||||
bar_width := diff(edge)
|
bar_width := diff(edge)
|
||||||
end
|
end
|
||||||
@ -395,11 +443,11 @@ _bin_centers(v::AVec) = (v[1:end-1] + v[2:end]) / 2
|
|||||||
seriestype := :bar
|
seriestype := :bar
|
||||||
()
|
()
|
||||||
end
|
end
|
||||||
@deps barbins bins
|
@deps barbins bar
|
||||||
|
|
||||||
|
|
||||||
@recipe function f(::Type{Val{:scatterbins}}, x, y, z)
|
@recipe function f(::Type{Val{:scatterbins}}, x, y, z)
|
||||||
edge, weights = x, y
|
edge, weights, xscale, yscale, baseline = _preprocess_binlike(d, x, y)
|
||||||
xerror := diff(edge)/2
|
xerror := diff(edge)/2
|
||||||
x := _bin_centers(edge)
|
x := _bin_centers(edge)
|
||||||
y := weights
|
y := weights
|
||||||
@ -409,43 +457,65 @@ end
|
|||||||
@deps scatterbins scatter
|
@deps scatterbins scatter
|
||||||
|
|
||||||
|
|
||||||
function _stepbins_path(edge, weights)
|
function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::Symbol)
|
||||||
|
log_scale_x = xscale in _logScales
|
||||||
|
log_scale_y = yscale in _logScales
|
||||||
|
|
||||||
nbins = length(linearindices(weights))
|
nbins = length(linearindices(weights))
|
||||||
if length(linearindices(edge)) != nbins + 1
|
if length(linearindices(edge)) != nbins + 1
|
||||||
error("Edge vector must be 1 longer than weight vector")
|
error("Edge vector must be 1 longer than weight vector")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
x = eltype(edge)[]
|
||||||
|
y = eltype(weights)[]
|
||||||
|
|
||||||
it_e, it_w = start(edge), start(weights)
|
it_e, it_w = start(edge), start(weights)
|
||||||
px, it_e = next(edge, it_e)
|
a, it_e = next(edge, it_e)
|
||||||
py = zero(eltype(weights))
|
last_w = eltype(weights)(NaN)
|
||||||
|
i = 1
|
||||||
|
while (!done(edge, it_e) && !done(edge, it_e))
|
||||||
|
b, it_e = next(edge, it_e)
|
||||||
|
w, it_w = next(weights, it_w)
|
||||||
|
|
||||||
npathpts = 2 * nbins + 2
|
if (log_scale_x && a ≈ 0)
|
||||||
x = Vector{eltype(px)}(npathpts)
|
a = b/_logScaleBases[xscale]^3
|
||||||
y = Vector{eltype(py)}(npathpts)
|
end
|
||||||
|
|
||||||
x[1], y[1] = px, py
|
if isnan(w)
|
||||||
i = 2
|
if !isnan(last_w)
|
||||||
while (i < npathpts - 1)
|
push!(x, a)
|
||||||
py, it_w = next(weights, it_w)
|
push!(y, baseline)
|
||||||
x[i], y[i] = px, py
|
end
|
||||||
i += 1
|
else
|
||||||
px, it_e = next(edge, it_e)
|
if isnan(last_w)
|
||||||
x[i], y[i] = px, py
|
push!(x, a)
|
||||||
i += 1
|
push!(y, baseline)
|
||||||
|
end
|
||||||
|
push!(x, a)
|
||||||
|
push!(y, w)
|
||||||
|
push!(x, b)
|
||||||
|
push!(y, w)
|
||||||
|
end
|
||||||
|
|
||||||
|
a = b
|
||||||
|
last_w = w
|
||||||
|
end
|
||||||
|
if (last_w != baseline)
|
||||||
|
push!(x, a)
|
||||||
|
push!(y, baseline)
|
||||||
end
|
end
|
||||||
assert(i == npathpts)
|
|
||||||
x[end], y[end] = px, zero(py)
|
|
||||||
|
|
||||||
(x, y)
|
(x, y)
|
||||||
end
|
end
|
||||||
|
|
||||||
@recipe function f(::Type{Val{:stepbins}}, x, y, z)
|
|
||||||
edge, weights = x, y
|
|
||||||
|
|
||||||
|
@recipe function f(::Type{Val{:stepbins}}, x, y, z)
|
||||||
axis = d[:subplot][Plots.isvertical(d) ? :xaxis : :yaxis]
|
axis = d[:subplot][Plots.isvertical(d) ? :xaxis : :yaxis]
|
||||||
|
|
||||||
xpts, ypts = _stepbins_path(edge, weights)
|
edge, weights, xscale, yscale, baseline = _preprocess_binlike(d, x, y)
|
||||||
if !Plots.isvertical(d)
|
|
||||||
|
xpts, ypts = _stepbins_path(edge, weights, baseline, xscale, yscale)
|
||||||
|
if !isvertical(d)
|
||||||
xpts, ypts = ypts, xpts
|
xpts, ypts = ypts, xpts
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -453,7 +523,7 @@ end
|
|||||||
if d[:markershape] != :none
|
if d[:markershape] != :none
|
||||||
@series begin
|
@series begin
|
||||||
seriestype := :scatter
|
seriestype := :scatter
|
||||||
x := Plots._bin_centers(edge)
|
x := _bin_centers(edge)
|
||||||
y := weights
|
y := weights
|
||||||
fillrange := nothing
|
fillrange := nothing
|
||||||
label := ""
|
label := ""
|
||||||
@ -468,7 +538,8 @@ end
|
|||||||
x := xpts
|
x := xpts
|
||||||
y := ypts
|
y := ypts
|
||||||
seriestype := :path
|
seriestype := :path
|
||||||
ylims --> [0, 1.1 * maximum(weights)]
|
|
||||||
|
ylims --> [baseline, _hist_ylim_hi(maximum(weights), yscale)]
|
||||||
()
|
()
|
||||||
end
|
end
|
||||||
Plots.@deps stepbins path
|
Plots.@deps stepbins path
|
||||||
@ -568,9 +639,11 @@ end
|
|||||||
|
|
||||||
if d[:seriestype] == :scatterbins
|
if d[:seriestype] == :scatterbins
|
||||||
# Workaround, error bars currently not set correctly by scatterbins
|
# Workaround, error bars currently not set correctly by scatterbins
|
||||||
|
edge, weights, xscale, yscale, baseline = _preprocess_binlike(d, h.edges[1], h.weights)
|
||||||
|
info("xscale = $xscale, yscale = $yscale")
|
||||||
xerror --> diff(h.edges[1])/2
|
xerror --> diff(h.edges[1])/2
|
||||||
seriestype := :scatter
|
seriestype := :scatter
|
||||||
(Plots._bin_centers(h.edges[1]), h.weights)
|
(Plots._bin_centers(edge), weights)
|
||||||
else
|
else
|
||||||
(h.edges[1], h.weights)
|
(h.edges[1], h.weights)
|
||||||
end
|
end
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user