From fde5825783ac99cef8991b81d8bc67fb37ef34ca Mon Sep 17 00:00:00 2001 From: Bernd_Mac <20151553+bernd1995@users.noreply.github.com> Date: Mon, 17 May 2021 17:20:43 +0200 Subject: [PATCH 1/4] First attempt at histogram3d/legoplot --- src/recipes.jl | 70 ++++++++++++++++++++++++++++++++++++++++++++++- src/shorthands.jl | 19 +++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/recipes.jl b/src/recipes.jl index 952bf0aa..6901acb8 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -987,6 +987,74 @@ end end +# --------------------------------------------------------------------------- +# Histogram 3D + +# Check if all neighbours have the same value +# TODO: Improve this somehow +function _allneighbours(arr, I::CartesianIndex, val = 0.0) + arr[I] == val && + arr[I + CartesianIndex(1,0)] == val && + arr[I + CartesianIndex(0,1)] == val && + arr[I - CartesianIndex(1,0)] == val && + arr[I - CartesianIndex(0,1)] == val && + arr[I + CartesianIndex(1,1)] == val && + arr[I + CartesianIndex(1,-1)] == val && + arr[I - CartesianIndex(1,1)] == val && + arr[I - CartesianIndex(1,-1)] == val +end + +@recipe function f(::Type{Val{:bins3d}}, x, y, z) + edge_x, edge_y, weights = x, y, z.surf + + if !plotattributes[:show_empty_bins] + edge_x = repeat(edge_x,inner=(4))[2:end-1] + edge_y = repeat(edge_y,inner=(4))[2:end-1] + float_weights = spzeros(length(edge_x),length(edge_y)) + float_weights[2:end-1,2:end-1] = repeat(float(weights),inner=(4,4)) + m = falses(size(float_weights)) + for I in CartesianIndices((2:length(edge_x)-1,2:length(edge_y)-1)) + m[I] = _allneighbours(float_weights,I,0) + end + float_weights[m] .= NaN + else + edge_x = repeat(edge_x,inner=(2)) + edge_y = repeat(edge_y,inner=(2)) + float_weights = spzeros(length(edge_x),length(edge_y)) + float_weights[2:end-1,2:end-1] .= repeat(float(weights),inner=(2,2)) + end + + x := edge_x + y := edge_y + z := Surface(permutedims(float_weights)) + #seriescolor := cgrad([:blue,:blue]) + colorbar := false + seriestype := :surface + linealpha := 1.0 + linecolor := :black + + () + +end +Plots.@deps bins3d surface wireframe + + +@recipe function f(::Type{Val{:histogram3d}}, x, y, z) + h = _make_hist( + (x, y), + plotattributes[:bins], + normed = plotattributes[:normalize], + weights = plotattributes[:weights], + ) + x := h.edges[1] + y := h.edges[2] + z := Surface(h.weights) + seriestype := :bins3d + () +end +@deps histogram3d bins3d + + # --------------------------------------------------------------------------- # pie @recipe function f(::Type{Val{:pie}}, x, y, z) @@ -1430,7 +1498,7 @@ end @recipe function f(shapes::AVec{Shape}) seriestype --> :shape - # For backwards compatibility, column vectors of segmenting attributes are + # For backwards compatibility, column vectors of segmenting attributes are # interpreted as having one element per shape for attr in union(_segmenting_array_attributes, _segmenting_vector_attributes) v = get(plotattributes, attr, nothing) diff --git a/src/shorthands.jl b/src/shorthands.jl index dfcc2302..b03e201a 100644 --- a/src/shorthands.jl +++ b/src/shorthands.jl @@ -107,6 +107,25 @@ julia> histogram2d(randn(10_000),randn(10_000)) """ @shorthands histogram2d +""" + histogram3d(x,y) + histogram3d!(x,y) + +Plot a three-dimensional histogram. + +# Arguments + +- `bins`: Number of bins (if an `Integer`) or bin edges (if an `AbtractVector`) +- `weights`: Vector of weights for the values in `x`. Each entry of x contributes + its weight to the height of its bin. + +# Example +```julia-repl +julia> histogram3d(randn(10_000),randn(10_000)) +``` +""" +@shorthands histogram3d + """ density(x) density!(x) From c85aa418854eaa9a3d8035abcb6e949c10e62eef Mon Sep 17 00:00:00 2001 From: Bernd_Mac <20151553+bernd1995@users.noreply.github.com> Date: Tue, 18 May 2021 17:26:29 +0200 Subject: [PATCH 2/4] Added histogram3d where needed --- src/args.jl | 8 +++++--- src/axes.jl | 2 +- src/subplots.jl | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/args.jl b/src/args.jl index d00cf8b9..9bc00b1d 100644 --- a/src/args.jl +++ b/src/args.jl @@ -51,12 +51,12 @@ const _axesAliases = Dict{Symbol,Symbol}( ) const _3dTypes = [ - :path3d, :scatter3d, :surface, :wireframe, :contour3d, :volume, :mesh3d + :path3d, :scatter3d, :surface, :wireframe, :contour3d, :volume, :mesh3d, :histogram3d ] const _allTypes = vcat([ :none, :line, :path, :steppre, :stepmid, :steppost, :sticks, :scatter, :heatmap, :hexbin, :barbins, :barhist, :histogram, :scatterbins, - :scatterhist, :stepbins, :stephist, :bins2d, :histogram2d, :histogram3d, + :scatterhist, :stepbins, :stephist, :bins2d, :histogram2d, :density, :bar, :hline, :vline, :contour, :pie, :shape, :image ], _3dTypes) @@ -94,13 +94,15 @@ const _typeAliases = Dict{Symbol,Symbol}( :imagesc => :image, :hist => :histogram, :hist2d => :histogram2d, + :hist3d => :histogram3d, + :lego => :histogram3d, :bezier => :curves, :bezier_curves => :curves, ) add_non_underscore_aliases!(_typeAliases) -const _histogram_like = [:histogram, :barhist, :barbins] +const _histogram_like = [:histogram, :barhist, :barbins, :histogram3d] const _line_like = [:line, :path, :steppre, :stepmid, :steppost] const _surface_like = [:contour, :contourf, :contour3d, :heatmap, :surface, :wireframe, :image] diff --git a/src/axes.jl b/src/axes.jl index 3d6236fb..b1cbfd2c 100644 --- a/src/axes.jl +++ b/src/axes.jl @@ -501,7 +501,7 @@ function widen(lmin, lmax, scale = :identity) end # figure out if widening is a good idea. -const _widen_seriestypes = (:line, :path, :steppre, :stepmid, :steppost, :sticks, :scatter, :barbins, :barhist, :histogram, :scatterbins, :scatterhist, :stepbins, :stephist, :bins2d, :histogram2d, :bar, :shape, :path3d, :scatter3d) +const _widen_seriestypes = (:line, :path, :steppre, :stepmid, :steppost, :sticks, :scatter, :barbins, :barhist, :histogram, :scatterbins, :scatterhist, :stepbins, :stephist, :bins2d, :histogram2d, :bar, :shape, :path3d, :scatter3d, :histogram3d) function default_should_widen(axis::Axis) should_widen = false diff --git a/src/subplots.jl b/src/subplots.jl index 35167349..ae893a85 100644 --- a/src/subplots.jl +++ b/src/subplots.jl @@ -49,6 +49,7 @@ function should_add_to_legend(series::Series) :hexbin, :bins2d, :histogram2d, + :histogram3d, :hline, :vline, :contour, From 516cdb6b925e60a31d44193a1fa1980b12c92a1b Mon Sep 17 00:00:00 2001 From: Bernd_Mac <20151553+bernd1995@users.noreply.github.com> Date: Tue, 18 May 2021 17:26:52 +0200 Subject: [PATCH 3/4] Added alias legoplot --- src/shorthands.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shorthands.jl b/src/shorthands.jl index b03e201a..cf3e30c0 100644 --- a/src/shorthands.jl +++ b/src/shorthands.jl @@ -125,6 +125,7 @@ julia> histogram3d(randn(10_000),randn(10_000)) ``` """ @shorthands histogram3d +@shorthands legoplot """ density(x) From 148287402e076e4cd683ce49a311958c1256392e Mon Sep 17 00:00:00 2001 From: Bernd_Mac <20151553+bernd1995@users.noreply.github.com> Date: Tue, 18 May 2021 17:27:34 +0200 Subject: [PATCH 4/4] Added different possibility of plotting by creating blocks for each bar --- src/recipes.jl | 62 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/src/recipes.jl b/src/recipes.jl index 6901acb8..49172f6c 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -1007,6 +1007,7 @@ end @recipe function f(::Type{Val{:bins3d}}, x, y, z) edge_x, edge_y, weights = x, y, z.surf + # Create the bins if !plotattributes[:show_empty_bins] edge_x = repeat(edge_x,inner=(4))[2:end-1] edge_y = repeat(edge_y,inner=(4))[2:end-1] @@ -1024,19 +1025,62 @@ end float_weights[2:end-1,2:end-1] .= repeat(float(weights),inner=(2,2)) end + # pyplot can't handle sparse arrays it seems + # Maybe get rid off sparse arrays at all, but if any bins-entry is large the additional points for plotting may use up a lot of memory otherwise for zeros and NaNs + if backend_name() == :pyplot + float_weights = Surface(permutedims(Array(float_weights))) + else + float_weights = Surface(permutedims(float_weights)) + end + + # Handle input of one seriescolor only (maybe exchangeable by get_series_color()?) + if isa(plotattributes[:seriescolor], Symbol) && plotattributes[:seriescolor] != :auto + seriescolor := cgrad([plotattributes[:seriescolor], plotattributes[:seriescolor]]) + end + + seriestype := :surface + colorbar := false x := edge_x y := edge_y - z := Surface(permutedims(float_weights)) - #seriescolor := cgrad([:blue,:blue]) - colorbar := false - seriestype := :surface - linealpha := 1.0 - linecolor := :black + z := float_weights () +end +Plots.@deps bins3d surface #wireframe mesh3d + + + +@recipe function f(::Type{Val{:bricks3d}}, x, y, z) + edge_x, edge_y, weights = x, y, z.surf + + x_step = edge_x[2] - edge_x[1] + y_step = edge_y[2] - edge_y[1] + x_len = length(x) + y_len = length(y) + + temp_x = vec([0.0 0.0 1.0 1.0]).*x_step .+ edge_x' + temp_y = vec([1.0 1.0 0.0 0.0]).*y_step .+ edge_y' + + z_help = [1.0 0.0 0.0 1.0; + 0.0 1.0 1.0 0.0; + 0.0 1.0 1.0 0.0; + 1.0 0.0 0.0 1.0]; + + for (i, c) in enumerate(weights) + itx = (i-1)%y_len + 1 + ity = floor(Int64,i/x_len) + 1 + @series begin + seriestype := :surface + x := temp_x[:,itx] + y := temp_y[:,ity] + z := c == 0 ? Surface(permutedims(NaN .* z_help)) : Surface(permutedims(c .* z_help)) + () + end + end + end -Plots.@deps bins3d surface wireframe +Plots.@deps bins3d surface #wireframe mesh3d @recipe function f(::Type{Val{:histogram3d}}, x, y, z) @@ -1049,10 +1093,10 @@ Plots.@deps bins3d surface wireframe x := h.edges[1] y := h.edges[2] z := Surface(h.weights) - seriestype := :bins3d + seriestype := :bricks3d#:bins3d () end -@deps histogram3d bins3d +@deps histogram3d bins3d bricks3d # ---------------------------------------------------------------------------