Plotting multiple lines between different points

I have set of vertices, and for each vertex, a set of other vertices “adjacent” to it, given by a dictionary, For example:

adj[[2,3]] = [[3,2],[1,0],[2,1]]

What I want to do is to plot all the adjacencies. But this doesn’t work:

using Plots
scatter([0],[0])
for v in vertices
    for a in adj[v]
        plot!(v,a)
    end
end

This outputs nothing - no errors, not even the initial point.

What should I be doing?

Thanks,
Alasdair

The plot command needs at least two coordinates to draw a line between, such as

plot([x1, x2], [y1, y2])

This draws a line from (x1,y1) to (x2,y2)

The immediate problem is that within the loop the object is modified but never returned. That can be fixed.

using Plots

# Sample data based on your example
vertices = [[2, 3]]
adj = Dict(
    [2, 3] => [[3, 2], [1, 0], [2, 1]]
)

# 1. Initialize an empty plot (or a scatter plot of vertices)
# 'legend = false' keeps it clean since loops create many series
plot(legend = false, aspect_ratio = :equal) 

# 2. Loop and add the lines
for v in vertices
    for a in adj[v]
        # X-coordinates: [v[1], a[1]]
        # Y-coordinates: [v[2], a[2]]
        plot!([v[1], a[1]], [v[2], a[2]], color = :blue, linewidth = 2)
    end
end

# 3. Scatter the vertices on top so they stand out
# (Using unzip-style syntax to get all X and Y coordinates)
xs = [v[1] for v in vertices]
ys = [v[2] for v in vertices]
scatter!(xs, ys, color = :red, markersize = 6)

# 4. CRITICAL: Force the plot to display
current()

But that’s not what you should be doing to plot graph objects. It’s fine for toy examples but for real work there’s another toolkit.

using Graphs
using GraphRecipes
using Plots

# Your original data
adj = Dict(
    [2, 3] => [[3, 2], [1, 0], [2, 1]]
)

# 1. Properly collect unique coordinate vectors without flattening them
all_vertices = unique(vcat(collect(keys(adj)), collect(values(adj))...))

# 2. Create the mapping dictionaries
vertex_to_id = Dict(v => i for (i, v) in enumerate(all_vertices))
id_to_vertex = Dict(i => v for (i, v) in enumerate(all_vertices))

# 3. Initialize the graph
g = SimpleGraph(length(all_vertices))

# 4. Add the edges
for (v, neighbors) in adj
    for n in neighbors
        add_edge!(g, vertex_to_id[v], vertex_to_id[n])
    end
end

# 5. Extract coordinates (This will work perfectly now!)
locs_x = [id_to_vertex[i][1] for i in 1:nv(g)]
locs_y = [id_to_vertex[i][2] for i in 1:nv(g)]

# 6. Plot
graphplot(g, 
          x = locs_x, 
          y = locs_y, 
          curves = false, 
          nodeshape = :circle, 
          names = [string(v) for v in all_vertices], 
          fontsize = 8,
          nodesize = 0.15)

Thank you very much! Yes - that works perfectly. And the current command at the end (which I didn’t know) is very helpful. Without it, the plots don’t display. Curiously, other plots do display without current; I don’t know why it’s required here. (FWIW, I’m using Julia with IJulia with Jupyter.)

The command to initialize the plot also is a nice touch I’ve never used before: I usually just create a plot of something, and then add to it.

Anyway, it all works beautifully - thank you again.

As to using Graph libraries, my need is not just to plot the vertices and adjacencies, but to plot them at specific places, to bring out the translational symmetry of the diagram. Probably there is functionality to plot graph vertices at specific coordinates, but I’m not sure then that I would be any better off than doing it all from scratch. The command you give:

all_vertices = unique(vcat(collect(keys(adj)), collect(values(adj))...))

is far more complex than anything I could manage!

Alasdair

It’s about what your last expression returns - in your original code your last expression was a loop, which returns nothing in Julia:

julia> for x ∈ 1:5
           plot(rand(5), rand(5))
       end

julia> typeof(ans)
Nothing

but plot commands such as plot or scatter (or their mutating variants) will return the plot object, which in turn is then displayed.

So maybe somewhat ironically where @technocrat 's code says CRITICAL: Force the plot to display it is not actually necessary to force the plot to display, because the prior scatter! call will already return the current plot object.