diff --git a/src/Plots.jl b/src/Plots.jl index 06ab4eef..e2c2e05f 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -127,7 +127,8 @@ export PlotRecipe, # EllipseRecipe, spy, - arcdiagram + arcdiagram, + chorddiagram # corrplot diff --git a/src/recipes.jl b/src/recipes.jl index 72645cdc..e35f3fc6 100644 --- a/src/recipes.jl +++ b/src/recipes.jl @@ -138,57 +138,11 @@ end abline!(args...; kw...) = abline!(current(), args...; kw...) -# ------------------------------------------------- -# Arc Diagram +# ================================================= +# Arc and chord diagrams -curvecolor(value, min, max, grad) = getColorZ(grad, (value-min)/(max-min)) - -"Plots a clockwise arc, from source to destiny, colored by weight" -function arc!(source, destiny, weight, min, max, grad) - radius = (destiny - source) / 2 - arc = Plots.partialcircle(0, π, 30, radius) - x, y = Plots.unzip(arc) - plot!(x .+ radius .+ source, y, line = (curvecolor(weight, min, max, grad), 0.5, 2)) -end - -""" -`arcdiagram(source, destiny, weight[, grad])` - -Plots an arc diagram, form `source` to `destiny` (clockwise), using `weight` to determine the colors. -""" -function arcdiagram(source, destiny, weight, grad=ColorGradient(:bluesreds)) - - if length(source) == length(destiny) == length(weight) - - vertices = vcat(source, destiny) - - xmin, xmax = extrema(vertices) - plot(xlim=(xmin - 0.5, xmax + 0.5)) - - wmin,wmax = extrema(weight) - - for (i, j, value) in zip(source,destiny,weight) - arc!(i, j, value, wmin, wmax, grad) - end - - scatter!(vertices, zeros(length(vertices)), leg=false) - - else - - throw(ArgumentError("source, destiny and weight should have the same length")) - - end -end - -""" -`arcdiagram(mat[, grad])` - -Plots an arc diagram of a matrix, form rows to columns (clockwise), -using the values on the matrix as weights to determine the colors. -Doesn't show edges with value zero if the input is sparse. -For simmetric matrices, only the upper triangular values are used. -""" -function arcdiagram{T}(mat::AbstractArray{T,2}, grad=ColorGradient(:bluesreds)) +"Takes an adjacency matrix and returns source, destiny and weight lists" +function mat2list{T}(mat::AbstractArray{T,2}) nrow, ncol = size(mat) # rows are sources and columns are destinies nosymmetric = !issym(mat) # plots only triu for symmetric matrices @@ -220,9 +174,152 @@ function arcdiagram{T}(mat::AbstractArray{T,2}, grad=ColorGradient(:bluesreds)) end end - resize!(source, idx-1) - resize!(destiny, idx-1) - resize!(weight, idx-1) - - arcdiagram(source, destiny, weight, grad) + resize!(source, idx-1), resize!(destiny, idx-1), resize!(weight, idx-1) end + +# ------------------------------------------------- +# Arc Diagram + +curvecolor(value, min, max, grad) = getColorZ(grad, (value-min)/(max-min)) + +"Plots a clockwise arc, from source to destiny, colored by weight" +function arc!(source, destiny, weight, min, max, grad) + radius = (destiny - source) / 2 + arc = Plots.partialcircle(0, π, 30, radius) + x, y = Plots.unzip(arc) + plot!(x .+ radius .+ source, y, line = (curvecolor(weight, min, max, grad), 0.5, 2), legend=false) +end + +""" +`arcdiagram(source, destiny, weight[, grad])` + +Plots an arc diagram, form `source` to `destiny` (clockwise), using `weight` to determine the colors. +""" +function arcdiagram(source, destiny, weight; kargs...) + + args = Dict(kargs) + grad = pop!(args, :grad, ColorGradient([colorant"darkred", colorant"darkblue"])) + + if length(source) == length(destiny) == length(weight) + + vertices = unique(vcat(source, destiny)) + sort!(vertices) + + xmin, xmax = extrema(vertices) + plot(xlim=(xmin - 0.5, xmax + 0.5), legend=false) + + wmin,wmax = extrema(weight) + + for (i, j, value) in zip(source,destiny,weight) + arc!(i, j, value, wmin, wmax, grad) + end + + scatter!(vertices, zeros(length(vertices)); legend=false, args...) + + else + + throw(ArgumentError("source, destiny and weight should have the same length")) + + end +end + +""" +`arcdiagram(mat[, grad])` + +Plots an arc diagram from an adjacency matrix, form rows to columns (clockwise), +using the values on the matrix as weights to determine the colors. +Doesn't show edges with value zero if the input is sparse. +For simmetric matrices, only the upper triangular values are used. +""" +arcdiagram{T}(mat::AbstractArray{T,2}; kargs...) = arcdiagram(mat2list(mat)...; kargs...) + +# ------------------------------------------------- +# Chord diagram + +arcshape(θ1, θ2) = Shape(vcat(Plots.partialcircle(θ1, θ2, 15, 1.1), + reverse(Plots.partialcircle(θ1, θ2, 15, 0.9)))) + +colorlist(grad, ::Void) = :darkgray + +function colorlist(grad, z) + zmin, zmax = extrema(z) + RGBA{Float64}[getColorZ(grad, (zi-zmin)/(zmax-zmin)) for zi in z]' +end + +""" +`chorddiagram(source, destiny, weight[, grad, zcolor, group])` + +Plots a chord diagram, form `source` to `destiny`, +using `weight` to determine the edge colors using `grad`. +`zcolor` or `group` can be used to determine the node colors. +""" +function chorddiagram(source, destiny, weight; kargs...) + + args=Dict(kargs) + grad = pop!(args, :grad, ColorGradient([colorant"darkred", colorant"darkblue"])) + zcolor= pop!(args, :zcolor, nothing) + group = pop!(args, :group, nothing) + + if zcolor !== nothing && group !== nothing + throw(ErrorException("group and zcolor can not be used together.")) + end + + if length(source) == length(destiny) == length(weight) + + plt = plot(xlim=(-2,2), ylim=(-2,2), legend=false, grid=false, + xticks=nothing, yticks=nothing, + xlim=(-1.2,1.2), ylim=(-1.2,1.2)) + + nodemin, nodemax = extrema(vcat(source, destiny)) + + weightmin, weightmax = extrema(weight) + + A = 1.5π # Filled space + B = 0.5π # White space (empirical) + + Δα = A / nodemax + Δβ = B / nodemax + + δ = Δα + Δβ + + for i in 1:length(source) + curve = BezierCurve(P2[ (cos((source[i ]-1)*δ + 0.5Δα), sin((source[i ]-1)*δ + 0.5Δα)), (0,0), + (cos((destiny[i]-1)*δ + 0.5Δα), sin((destiny[i]-1)*δ + 0.5Δα)) ]) + plot!(curve_points(curve), line = (Plots.curvecolor(weight[i], weightmin, weightmax, grad), 1, 1)) + end + + if group === nothing + c = colorlist(grad, zcolor) + elseif length(group) == nodemax + + idx = collect(0:(nodemax-1)) + + for g in group + plot!([arcshape(n*δ, n*δ + Δα) for n in idx[group .== g]]; args...) + end + + return plt + + else + throw(ErrorException("group should the ", nodemax, " elements.")) + end + + plot!([arcshape(n*δ, n*δ + Δα) for n in 0:(nodemax-1)]; mc=c, args...) + + return plt + + else + throw(ArgumentError("source, destiny and weight should have the same length")) + end +end + +""" +`chorddiagram(mat[, grad, zcolor, group])` + +Plots a chord diagram from an adjacency matrix, +using the values on the matrix as weights to determine edge colors. +Doesn't show edges with value zero if the input is sparse. +For simmetric matrices, only the upper triangular values are used. +`zcolor` or `group` can be used to determine the node colors. +""" +chorddiagram(mat::AbstractMatrix; kargs...) = chorddiagram(mat2list(mat)...; kargs...)