Closed babaq closed 5 years ago
This can already be done with:
using GraphRecipes
using Plots
using LightGraphs
pyplot()
g = DiGraph(4)
add_edge!(g, 1, 2)
add_edge!(g, 2, 3)
add_edge!(g, 4, 3)
add_edge!(g, 1, 4)
graphplot(g, names=[1,2,3,4], markersize=2, arrow=arrow(:closed, :head, 1, 1))
For some reason, I can only make this work with the pyplot
backend, I will look into that problem.
The current behavior is suboptimal for two reasons:
graphplot
should have a method so that graphplot(g)
actually draws arrows like suggested by OPProblem 1 should be pretty easy to fix, but 2 will probably take a bit of doing.
I have had a bit of luck with the shorten
kwarg.
using GraphRecipes
using Plots
using LightGraphs
pyplot()
g = DiGraph(4)
add_edge!(g, 1, 2)
add_edge!(g, 2, 3)
add_edge!(g, 2, 4)
add_edge!(g, 4, 3)
add_edge!(g, 1, 4)
add_edge!(g, 4, 1)
graphplot(g, names=[1,2,3,4], markersize=0.1, arrow=arrow(:closed, :head, 1, 1), shorten=0.023)
You could probably make do by playing around with markersize
and shorten
until a better solution comes along.
@JackDevine there are two possibilities for a fix here:
marker
(or scatter
) functionality to show vertices. They are a point, but the extent is shown in screen coordinates not data coordinates, so you don't know where the edges of the vertex markers are (marker sizes also differ among backends). They can stretch a little with the position and size of the text, but as you can see it's imperfect - it's not completely centered on the font, and the stretched hexagons look a little weird. An ambitious change would be to draw the markers instead using shape
s, where you specify the vertices of the polygons explicitly - that would make alignment with text much easier I think. If you think this is too much, I think fixing the stretching and alignment of markers to be exact, and just stopping the arrows well before they reach the marker at all (so there's a little space) would still be a marked improvement. Thoughts?
Thanks for the pointers, I really appreciate it. I was actually about to ask if it was possible to get the extent of a marker, luckily you have answered that!
I will have an honest attempt at 2 and see how that goes.
Might be useful to have @diegozea and @daschw 's input here before you commit too much energy that ends up replicating functionality.
MATLAB looks to be doing 1 but it is not the most common representation. The idea of using shape
s instead of marker
s sounds good. Right now, I think that chorddiagram
is the only one using shape
instead of scatter
as seriestype
: https://github.com/JuliaPlots/GraphRecipes.jl/blob/e3180a85f1ffaed1055be6b6fc107ec7d407406e/src/graphs.jl#L397
Gephi (IMHO the world't best open source graph software) does both: https://gephi.org/images/screenshots/neighbours.png and https://gephi.org/images/screenshots/preview3.png
Thanks for the examples of libraries using strategy 1, I was not aware of those.
Right now, GraphRecipes has an arcshape
for chord diagrams as pointed out by @diegozea . However, to handle markershapes, we would need all of the functionality for the Shape
type in Plots.jl. I looked into copying the Shape
type into GraphRecipes, however, it looks to me like we would need multiple files worth of duplicated code to get everything.
Possible ways forward:
Shape
type into GraphRecipes. This would be a lot of duplicated code, which seems unfortunate to me.Shape
type into PlotUtils? Is this even an option or do I misunderstand the point of PlotUtils?Not quite sure why you'd need to copy the Shape
type?
There's a series recipe for shapes in Plots, so you can just pass a vector of vertices (ending with the first to form a closed polygon) then set the seriestype to shape
.
Thanks for explaining that, things are not as bad as I had thought.
That said we would still need to create vertices for all of the possible shapes that somebody could pass to the nodeshape
argument. Right now GraphRecipes has the functionality to handle arcs and circles, however, users might ask for any of :circle, :rect, :star5, :diamond, :hexagon, :cross, :xcross, :utriangle, :dtriangle, :rtriangle, :ltriangle, :pentagon, :heptagon, :octagon, :star4, :star6, :star7, :star8
Also, would we be OK with the nodes being distorted if the aspect ratio is not equal to one? Otherwise we would need to copy the scale
function from Plots.jl.
Hm, aspect_ratio
- the question is whether we could get away with always requiring that to be 1 when plotting graphs?
I don't think we need to reimplement every single marker shape, do you? circles, ellipses, rectangles and hexagons should do the trick
Yes, having the aspect ratio being 1 when plotting graphs might turn out to be OK. One thing that I have been considering is rescaling the x and y axes so that the xlims and ylims are the same.
I am glad that you think that all of the markershapes would be overkill. I agree with the list that you have provided, of course the documentation should mention that nodeshape
is a subset of markershape
. That can be a part of #59 .
Based on our discussion, I I now have enough information to try fixing this issue. First, I will finish off #65 .
Thank you for your input everyone, if you have anymore insights/suggestions then please let me know and I will try take them on board.
I think always fix aspect ratio=1
will prevent some use case. I found recently needed to plot a neural network of cortex estimated by imaging of neurons, in a 2d or 3d , that the positions of neurons are accurately set by user as x, y, z coordinates
. these positions may stretch along specific dimensions, like x=1:1000
, but y=20:90
. always setting aspect ratio=1
make some dimension impossible to see. also there may be some labelling or annotations needed alongside the graph, for example, several hline!()
that mark the layer borders of the cortex.
if we could combine the normal 2d/3d plots together with graph plot, that will open a lot of new use case, cause the node is usually a abstraction of real world entity and often plotted with real world coordinates and with other visual guides.
here is basically what I want to plot:
the positions of neurons are accurately set by user as x, y, z coordinates
Note that your use case is quite different from what GraphRecipes does. In GraphRecipes, the user gives graphplot
information about how nodes are connected and then graphplot
will work out the x, y, z positions itself. That is why graphplot
does not show the x an y axis scales. There are definitely some graph structures where graphplot
will calculate x, y positions that are very skewed in one direction. For example, if there is a graph that has a path from node 1 through to node n and no other connections, then graphplot
may return a layout where the xlims are very small and the ylims are very large. My suspicion is that these situations can be dealt with by rescaling the xlims and the ylims, although I could be wrong.
I am not sure that GraphRecipes is a good fit for your particular problem. If you already have the coordinates of your nodes and you want to use a range of visual guides, then I would recommend Plots.jl.
I guess I could do something similar in Plots, since it's just a scatter plot with line-arrow segments. for a general GraphRecipes, the only information is the node and edges, then the coordinates has to be worked out to sufficiently visualize the whole graph, the use case I mentioned is just a simpler one, cause the coordinates are already known. all other visual guide are just parts of the Plots, which maybe not shown for a general graph by default, but user should be able to add them as optional arguments.
Yes, you are right, it seems that I am always learning new things about GraphRecipes.
n = 20
x = rand(n); y = 30*rand(n)
g = WheelGraph(n)
graphplot(g, x=x, y=y)
vline!([0.5])
hline!([15])
Having aspect_ratio=1
would make this example look very bad.
I think that the solution is that if x
and y
are not provided, then we scale the xlims and ylims, otherwise we don't do any scaling.
Worst case scenario is that we would have to copy the scale
function from Plots and use that the same way that markers use it. That doesn't seem too bad to me.
This was fixed in #67
I tried to
graphplot(g)
whereg
is aSimpleDiGraph
inLigthGraphs
, but it seems doesn't do direction arrow connections but just lines between nodes.