-
-
Notifications
You must be signed in to change notification settings - Fork 354
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to plot a partial annulus? #163
Comments
Maybe related to #143 ? |
I think creating custom shapes might be better (for the backends that support it anyways). Here's something to get you started: using Plots; gadfly(size=(300,300),leg=false)
arcshape(θ1, θ2) = Shape(vcat(Plots.partialcircle(θ1, θ2, 10, 1),
reverse(Plots.partialcircle(θ1, θ2, 10, 0.8))))
plot([0],[0],marker = (100, arcshape(0,0.25π))) |
Will Plotly support custom markets in the future? |
I don't know... I certainly want that to happen. |
This is cool @spencerlyon2! However I don't think these can be used as "markers" within Plotly, correct? They are just for drawing on the figure? |
Ahh that is true. You could kinda get a working version by constructing many of these shapes, but it wouldn't come with all the same niceties that actual trace markers have |
See: plotly/plotly.js#330 |
@tbreloff Do you know why are these markers unaligned? |
What is the relation between the marker size ( |
That's weird. Does it do that for gadfly?
|
Yes, It works fine with Gadfly The code is: using Plots; gadfly()
arcshape(θ1, θ2) = Shape(vcat(Plots.partialcircle(θ1, θ2, 10, 1),
reverse(Plots.partialcircle(θ1, θ2, 10, 0.8))))
plt = plot(xlim=(-1,1), ylim=(-1,1))
A = 1.5π # Filled space
B = 0.5π # White space
N = 10 # Number of nodes
Δα = A / N
Δβ = B / N
δ = Δα + Δβ
n = 1
α = 0.0
while n <= N
plot!(plt, x=[0], y=[0], linetype=:scatter, marker=(100, arcshape(α, α + Δα)), legend=false)
α += δ
n += 1
end
plt |
How do I determine the radius using the first element of the marker tuple? |
Ok my guess is that PyPlot is normalizing the shape coordinates based on some calculation of approximate area. When all the shapes are the same you won't notice, but in this case it takes precise coordinates and perturbs them. I'll try to dig into the PyPlot docs to see if there's an argument to turn that behavior off.
|
The order of that marker tuple doesn't matter, but Plots "figures out" that you intend it to refer to the marker size. This happens during argument preprocessing. If you look at the implementations in MLPlots.jl, you'll see an alternative approach to defining Plots recipes, which can use the preprocessed args.
|
From the docs: verts a list of (x, y) pairs used for Path vertices. The center of the marker is located at (0,0) and the size is normalized.
|
For reference, probably related: matplotlib/matplotlib#1980
|
I couldn't make it... :/ The markers don't respect the ratio between edges... The following is my code: arcshape(θ1, θ2) = Shape(vcat(Plots.partialcircle(θ1, θ2, 10, 1.1),
reverse(Plots.partialcircle(θ1, θ2, 10, 0.9))))
function chorddiagram(source, destiny, weight, grad=ColorGradient(:bluesreds))
if length(source) == length(destiny) == length(weight)
vertices = unique(vcat(source, destiny))
wmin, wmax = extrema(weight)
plt = plot(xlim=(-2,2), ylim=(-2,2), legend=false)
A = 1.5π # Filled space
B = 0.5π # White space
N = length(vertices) # Number of nodes
Δα = A / N
Δβ = B / N
δ = Δα + Δβ
n = 1
α = 0.0
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!(plt, curve_points(curve), line = (Plots.curvecolor(weight[i], wmin, wmax, grad), 0.5, 2))
end
while n <= N
plot!(plt, x=[ 0 ], y=[ 0 ], linetype=:scatter, color=:lightblue, marker=(55, arcshape(α, α + Δα)), legend=false)
α += δ
n += 1
end
return plt
else
throw(ArgumentError("source, destiny and weight should have the same length"))
end
end |
Yes you figured it out... You need an aspect ratio with equal sides. I would also add the arguments: (grid=false, xticks=nothing, yticks=nothing, xlim=(-1.2,1.2), ylim=(-1.2,1.2)) to the first plot command
|
Outside the chord diagram:
|
@tbreloff I change the code, because I would like to use
Code: using Plots; gadfly()
arcshape(θ1, θ2) = Shape(vcat(Plots.partialcircle(θ1, θ2, 15, 1.1),
reverse(Plots.partialcircle(θ1, θ2, 15, 0.9))))
function chorddiagram(source, destiny, weight; grad=ColorGradient(:bluesreds), size=(400,400))
# Needs an aspect ratio with equal sides.
ms = minimum(size)
size_plot = (ms, ms)
# Empirical marker size
size_marker = ms < 150 ? ms/4 : 0.375ms
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), size=size_plot)
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), 0.5, 2))
end
plot!(x=zeros(Int, nodemax), y=zeros(Int, nodemax), linetype=:scatter,
marker = (size_marker, [arcshape(n*δ, n*δ + Δα) for n in 0:(nodemax-1)]))
return plt
else
throw(ArgumentError("source, destiny and weight should have the same length"))
end
end |
I suspect this is an issue with Gadfly... I've noticed that it doesn't always respect z-order of the layers.
One method you could consider... collect the keywords in a dictionary, remove the ones specific to chorddiagram, then splat the dictionary into the plot commands. This is trickier if you want different args for each command. You'll have to explain exactly what you want to do (and there might be a better way altogether)
The better solution is to allow plotting |
I just pushed up some changes to the dev branch, adding a new linetype In your current method, you can make the following change to use the new functionality (only implemented in Gadfly currently!) # plot!(x=zeros(Int, nodemax), y=zeros(Int, nodemax), linetype=:scatter,
# marker = (size_marker, [arcshape(n*δ, n*δ + Δα) for n in 0:(nodemax-1)]))
plot!([arcshape(n*δ, n*δ + Δα) for n in 0:(nodemax-1)]') Your graph should work now, even with an uneven aspect ratio. |
Thanks! 👍 Do you know what the following error means?
I'm doing:
|
I want to be able to color nodes/markers/shapes using a vector of values ( |
Are you sure you checked out latest Plots dev branch and restarted julia?
|
Yes, I did it again, but I'm still getting that error using the |
Oh I'm sorry. You just need a semicolon before splatting the args... Otherwise they won't be treated as keyword args.
|
Ok... I'll need to review what happens with zcolor/group. For now you could always do that yourself and plot each shape individually with its own color. You can use getColorZ(grad, val)
|
I don't think you should pass zcolor into the plot function... I think you should handle it something like: colorlist(grad, ::Void) = :lightblue
function colorlist(grad, z)
zmin, zmax = extrema(z)
RGBA{Float64}[getColorZ(grad, (zi-zmin)/(zmax-zmin)) for zi in z]'
end
function chorddiagram(source, destiny, weight; kw...)
...
c = colorlist(grad, pop!(args, :zcolor, nothing))
@show c
plot!([arcshape(n*δ, n*δ + Δα) for n in 0:(nodemax-1)]', mc=c)
...
end For the group arg... I'm a little confused what you expect the plot to look like, which makes it hard to suggest something. Currently, you're plotting |
I think ideally I want to support this, but I'll have to change how I handle shapes. Lets put it on the todo list. |
@tbreloff How can I select colors (like |
I solved indexing |
Can't you just pass in the colors directly? Do you want the colors to be automatically generated in a specific way?
|
I'm using
to pass colors, is that fine? |
Well that depends... Do you just want the colors to be "sufficiently different"? If so then I think you should plot one series per group and don't set the color.
|
Ok I took the liberty to make a few changes to your method which are more in line with what I'm thinking... it's still not as clean as I'd like, but I suppose it could be cleaned up later. using Plots; gadfly()
arcshape(θ1, θ2) = Shape(vcat(Plots.partialcircle(θ1, θ2, 15, 1.1),
reverse(Plots.partialcircle(θ1, θ2, 15, 0.9))))
colorlist(grad, ::Void) = :lightblue
function colorlist(grad, z)
zmin, zmax = extrema(z)
RGBA{Float64}[getColorZ(grad, (zi-zmin)/(zmax-zmin)) for zi in z]'
end
function chorddiagram(source, destiny, weight; kw...)
if !(length(source) == length(destiny) == length(weight))
throw(ArgumentError("source, destiny and weight should have the same length"))
end
args=Dict(kw)
grad = pop!(args, :grad, ColorGradient(:bluesreds))
size = pop!(args, :size, (400,400))
group = pop!(args, :group, nothing)
# setup plot
plt = plot(grid=false, xticks=nothing, yticks=nothing, xlim=(-1.2,1.2), ylim=(-1.2,1.2))
# ?? do we really want to have `nodemax` shapes?? what if the user enters something really strange?
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
δ = Δα + Δβ
# plot the arcs
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Δα)) ])
c = Plots.curvecolor(weight[i], weightmin, weightmax, grad)
plot!(curve_points(curve), line = (c, 0.5, 2), lab = "")
end
# plot the shapes
shapes = [arcshape(n*δ, n*δ + Δα) for n in 0:(nodemax-1)]
if group != nothing
for g in sort(unique(group))
# plot only those shapes in group g
plot!(shapes[group .== g], lab = "Group $g")
end
else
# plot the shapes the way we were originally... each as its own series,
# with colors optionally selected using a gradient
plot!(shapes', mc = colorlist(grad, pop!(args, :zcolor, nothing)), lab = "")
end
plt
end
n = 5
chorddiagram(1:n, (1:n)+1, 1:n, group = [1,1,2,2,2,3]) Note to self: Compose doesn't handle NaNs for polygons when drawing PNGs... need to fix and submit a PR similar to GiovineItalia/Compose.jl#189 |
Hi!
I can plot the lower and upper partial circles, but I can't fill it.
A single wide line looks ugly, doesn't it?
I want to do a chord diagram/circos plot.
Best.
The text was updated successfully, but these errors were encountered: