Created
February 5, 2023 12:52
-
-
Save behinger/8df41a3e051979a8e8ee0068f1aac6b8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
dodge(x,y,args...;[plot_fun=scatter!,kwargs...]) | |
dodges plot items if they are on the same `x` (or `y` if specified) value. | |
## Attributes | |
### Specific to `dodge` | |
- `plot_fun::Function = scatter!` in place plotting function to be called after dodging the x/y values | |
- `dodge::Vector{Int} = Makie.automatic`. Indicates to which group each point belongs, e.g. [1, 1, 2, 3, 2, 2] | |
- `n_dodge::Int = Makie.automatic` maximal number of dodglings. This indicates over how many "dodglings" `dodge_width` should be split | |
- `dodge_width::Real=0.1` how much should each x be dodged maximally to the left and right (typically a proportion between 0 and 1) | |
- `dodge_y::Bool=false` should we dodge x, or rather y? | |
### Generic | |
All other arguments are simply forwarded to the `plot_fun` | |
### Example | |
```julia | |
x = [1,3,4, 1,3,4., 4,3,1] | |
y = [1.1,1.9,2.8,1,2,3,3.05,2.05,1.05] | |
c = ["red","red","red","green","green","green","blue","blue","blue"] | |
low,high = repeat([0.2],length(x)) | |
dodge(x,y,low,high;plot_fun=errorbars!,color=c) | |
dodge!(x,y;color=c, plot_fun=scatter!) | |
current_figure() | |
``` | |
""" | |
@recipe(Dodge,x,y) do scene | |
Theme(; | |
#default_theme(scene, fun)..., | |
#dodge = MakieCore.automatic, # grouping vector what to dodge | |
#n_dodge = MakieCore.automatic, | |
plot_fun = scatter!, | |
dodge=Makie.automatic, # dodge grouping | |
n_dodge = Makie.automatic, # how many elements to dodge per X | |
dodge_width = 0.1, # in % diff | |
dodge_y = false, # dodge in y instead of x direction | |
) | |
end | |
function CairoMakie.plot!(sc::Dodge) | |
N = length(sc) | |
@extract(sc,(plot_fun,dodge_y,dodge_width,dodge,n_dodge)) | |
di = dodge_y[] ? sc[:y] : sc[:x] | |
di = lift(di,dodge,n_dodge,dodge_width) do di,dodge,n_dodge,dodge_width | |
return dodge_this(di,dodge,n_dodge,dodge_width) | |
#return x | |
end | |
# replace either x or y | |
x = dodge_y[] ? sc[:x] : di | |
y = dodge_y[] ? di : sc[:y] | |
# call the dodged plottingfunction | |
plot_fun[](sc,x,y,[x for x in sc[3:N]]...;sc.attributes...) | |
sc | |
end | |
""" | |
get_ndodge(x,dodge::AbstractVector,n_dodge::Makie.Automatic) | |
get_ndodge(x,dodge::Makie.Automatic,n_dodge::Makie.Automatic) | |
get_ndodge(x,dodge,n_dodge::Int) = n_dodge | |
extracts n_dodge. Three possibilities: | |
1) n_dodge is specified, simply returns it | |
2) dodge is a vector and specified, we return maximum(dodge) | |
3) w calculate n_dodge by countinng unique x-values | |
""" | |
get_ndodge(x,dodge::AbstractVector,n_dodge::Makie.Automatic) = maximum(dodge) | |
function get_ndodge(x,dodge::Makie.Automatic,n_dodge::Makie.Automatic) | |
un = unique(x) | |
un_c = [count(==(element),x) for element in un ] | |
n_dodge = maximum(un_c) | |
return n_dodge | |
end | |
get_ndodge(x,dodge,n_dodge::Int) = n_dodge | |
""" | |
dodge_this(x::AbstractVector,dodge,n_dodge,dodge_width::Real) | |
Adds a tiny bit (`(dodge_width * Δ)/n_dodge`) to each dataentry `x` | |
- `x` typically x or y values (if `dodge_y = true` in `dodge!`) | |
- `dodge` either `Makie.automatic` or a Vector of Integers. Indicates to which group each point in `x` belongs, e.g. [1 1 2 3 2 2] | |
- `n_dodge` maximal number of dodglings | |
- `dodge_width` how much should each x be dodged maximally to the left and right (proportion) | |
Δ is a unspecified quantity that relates to the distances between x. if `dodge` is provided, `Δ=1`. If `dodge` is automatic, we calculate the distance between the x -values using `abs.(diff(sort(unique(x))))` and use that distance. If different distances are found, we will use the maximal distance to calculate Δ | |
""" | |
function dodge_this(x::AbstractVector,dodge,n_dodge,dodge_width::Real) | |
# first find out what the maximal dodge-number is | |
n_dodge=get_ndodge(x,dodge,n_dodge) | |
if n_dodge == 1 | |
# nothing to do here | |
@warn "nothing to dodge, n_dodge was $n_dodge" | |
return x | |
end | |
# get the distances Δ(xᵢ,xᵢ₊₁) | |
if isa(dodge,Makie.Automatic) | |
# we need to "unsort" because it is not always the case that the identical x-values are next to each other | |
Δ = unique(abs.(diff(sort(unique(x))))) | |
if length(Δ)>1 | |
Δ = maximum(Δ) | |
@warn "position values are not equally distanced, using the maximal distance to apply dodge_width to: Δ*dodge_width=$Δ*$dodge_width" | |
end | |
#ix = sortperm(x) | |
#dodge = invperm(ix) | |
un_x = unique(x) | |
ix = map(y->findall(y.==x),un_x) | |
dodge = zeros(Int,length(x)) | |
for i = 1:length(ix) | |
dodge[ix[i]].=1:length(ix[i]) | |
end | |
else | |
Δ = 1 | |
end | |
@assert length(dodge)==1 || (length(x) == length(dodge)) "length(x) $(length(x)), and length(dodge) $(length(dodge)) should be equal, dodge should be only a single dodge-'category'" | |
# this generates the normalized dodge-distance between -1 and 1, scales it by the calculated Δ and repeats it for each unique x-entrances. Finally we have to "shuffle" it, in case x is not sorted. | |
Δx=(range(-1,1,length=n_dodge) .* (dodge_width*Δ/n_dodge))[dodge] | |
x = x .+ Δx | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment