I’ve created bank and depositor agents and would now like to render as a graph with banks as the nodes and the depositors forming the edges that change at every time step. A snippet of the data frame is inserted below. For example, at time 0 depositor #1 withdraws from bank #405.
The problem is the bank “FOR loop.” If the “vertex_counter” is inside the FOR loop, there’s a “BoundsError.” If the “vertex_counter” is outside the FOR loop, there’s an “UnderVarError.” I’d appreciate your insights. Thank you.
vertex_map = Dict{Int, Int}()
vertex_counter = 1 # ??? vertex_counter before loop or after loop? Neither works.
for bank in banks #vertex_counter = 1
vertex_map[bank] = vertex_counter
add_vertex!(g)
social_network = df[df.bank_id .== bank, :social_network][1]
if haskey(social_network_groups, social_network)
push!(social_network_groups[social_network], vertex_counter)
else
social_network_groups[social_network] = [vertex_counter]
end
vertex_counter += 1
end
Function to update the network at each time step
function update_network!(g, df, t)
# Clear existing edges
for e in edges(g)
rem_edge!(g, edges(g))
end
# Add edges from depositors to banks based on the current time step
for row in eachrow(df)
if row.time <= t
depositor_id = row.depositor_id
bank_vertex = vertex_map[row.bank_id]
add_edge!(g, depositor_id, bank_vertex)
end
end
end
Function to plot the network
function plot_network(g, social_network_groups, group_colors, t)
# Assign colors to vertices based on their social_network group
vertex_colors = fill(“black”, nv(g))
for (group, vertices) in social_network_groups
for vertex in vertices
vertex_colors[vertex] = group_colors[group]
end
end
# Plot the graph with vertex colors using Plots.jl and GraphRecipes.jl
graphplot(g, names=1:nv(g), nodecolor=vertex_colors, method=:spring, title="Time Step: $t")
#i = 1
for group in keys(social_network_groups)
i = 1
group_colors[group] = hex(color_palette[I])
i += 1
end
Simulate the network evolution
max_time = maximum(df.time)
for t in 1:max_time
update_network!(g, df, t)
plot_network(g, social_network_groups, group_colors, t)
sleep(1) # Pause for a second to visualize the evolution
end
‘’’
Could you edit your post to improve the readability? You seem to currently have written the code using > (e.g.
This is a comment
str = “Hello world”
println(str)
), while to get proper formatting you should use triple backticks (```, not apostrophes (‘’') or quotes (“”)) and no > (e.g.
# This is a comment
str = "Hello world"
println(str)
).
Error message / stacktrace
Could you post the full error message and stacktrace? This would help pinpoint the location of the problem. Also, in the case of vertex_counter outside of the for loop, if would help to know which variable is undefined .
Data
It would also help if you could share the data for the DataFrame (if legally possible), or even better, generate (fake) data, for a minimal working example. That way others can also debug the code.
For the same reason please also mention the dependencies you are using (presumably using CSV, DataFrames, Graphs, Plots, GraphRecipes).
Some notes
If you define vertex_counter inside the loop as
for bank in banks
vertex_counter = 1
# Do stuff with vertex_counter
vertex_counter += 1
end
then vertex_counter will always have a value of 1, as you reset it at the start of each iteration.
In fact, you do not need a variable vertex_counter at all, as this information is already contained in g and can be extracted using nv(g). Alternatively, since you just increment vertex_counter by 1 at the end of every iteration, you could use for (vertex_counter, bank) in enumerate(banks).
(The line
social_network = df[df.bank_id .== bank, :social_network][1]
looks for all rows in the DataFramedf whose bank_id equals bank, extracts the social_network value for those rows, and then takes the first value, i.e. throws away all other found rows. Instead you could use something like df[findfirst(df.bank_id .== bank), :social_network] to directly obtain only the first relevant row. It’s probably also a bit more efficient (though not optimal in this regard as we still materialise the BitVector - you could use a generator), though that’s probably not very important at this point.)
In
for e in edges(g)
rem_edge!(g, edges(g))
end
you probably want rem_edge!(g, e). I would expect the current code to throw an error (but am not familiar with Graphs.jl).
Thank you. I’ll go through your feedback with care.
I see the backtick now; it’s with the Tilda character.
Here’s the error message with the counter outside the bank FOR loop:
┌ Warning: Assignment to vertex_counter in soft scope is ambiguous because a global variable by the same name exists: vertex_counter will be treated as a new local. Disambiguate by using local vertex_counter to suppress this warning or global vertex_counter to assign to the existing global variable.
└ @ ~/Documents/dissertation/Julia/Network.jl:39
ERROR: UndefVarError: vertex_counter not defined
Stacktrace:
[1] top-level scope
@ ~/Documents/dissertation/Julia/Network.jl:31
Since I restarted my laptop, I’m now getting a different error message with the counter inside the bank FOR loop:
Warning: Assignment to vertex_counter in soft scope is ambiguous because a global variable by the same name exists: vertex_counter will be treated as a new local. Disambiguate by using local vertex_counter to suppress this warning or global vertex_counter to assign to the existing global variable.
└ @ ~/Documents/dissertation/Julia/Network.jl:30
ERROR: Unknown color: 000000
Stacktrace:
[1] error(::String, ::String)
@ Base ./error.jl:44
[2] _parse_colorant(desc::String)
@ Colors ~/.julia/packages/Colors/E2qak/src/parse.jl:151
[3] parse(::Type{RGBA{Float64}}, desc::String)
@ Colors ~/.julia/packages/Colors/E2qak/src/parse.jl:207
[4] plot_color
@ ~/.julia/packages/PlotUtils/wo8RM/src/colors.jl:5 [inlined]
[5] |>
@ ./operators.jl:917 [inlined]
[6] get_series_color(c::String, sp::Plots.Subplot{Plots.GRBackend}, n::Int64, seriestype::Symbol)
@ Plots ~/.julia/packages/Plots/kLeqV/src/args.jl:1982
[7] _update_series_attributes!(plotattributes::RecipesPipeline.DefaultsDict, plt::Plots.Plot{…}, sp::Plots.Subplot{…})
@ Plots ~/.julia/packages/Plots/kLeqV/src/args.jl:2078
[8] add_series!(plt::Plots.Plot{Plots.GRBackend}, plotattributes::RecipesPipeline.DefaultsDict)
@ Plots ~/.julia/packages/Plots/kLeqV/src/pipeline.jl:377
[9] _process_seriesrecipe(plt::Any, plotattributes::Any)
@ RecipesPipeline ~/.julia/packages/RecipesPipeline/BGM3l/src/series_recipe.jl:46
[10] _process_seriesrecipes!(plt::Any, kw_list::Any)
@ RecipesPipeline ~/.julia/packages/RecipesPipeline/BGM3l/src/series_recipe.jl:27
[11] recipe_pipeline!(plt::Any, plotattributes::Any, args::Any)
@ RecipesPipeline ~/.julia/packages/RecipesPipeline/BGM3l/src/RecipesPipeline.jl:99
[12] _plot!(plt::Plots.Plot, plotattributes::Any, args::Any)
@ Plots ~/.julia/packages/Plots/kLeqV/src/plot.jl:223
[13] plot#188
@ ~/.julia/packages/Plots/kLeqV/src/plot.jl:102 [inlined]
[14] plot
@ ~/.julia/packages/Plots/kLeqV/src/plot.jl:93 [inlined]
[15] graphplot
@ ~/.julia/packages/RecipesBase/BRe07/src/RecipesBase.jl:380 [inlined]
[16] plot_network(g::SimpleDiGraph{…}, social_network_groups::Dict{…}, group_colors::Dict{…}, t::Int64)
@ Main ~/Documents/dissertation/Julia/Network.jl:70
[17] top-level scope
@ ~/Documents/dissertation/Julia/Network.jl:88
Some type information was truncated. Use show(err) to see complete types.
#File to create a network graph from the DepositorBankData.csv file
using Pkg
Pkg.activate(".")
Pkg.instantiate()
using DataFrames
using Graphs
using CSV
using GraphPlot
using Plots
using GraphRecipes
using Colors
# Load the data from the CSV file
df = CSV.read("//Users/teresafranks/Documents/Dissertation/Charts/DepositorBankData.csv", DataFrame)
# Create a directed graph
g = SimpleDiGraph()
# Add vertices for banks and group them by social_network
banks = unique(df.bank_id)
social_network_groups = Dict{Int, Vector{Int}}()
# Create a mapping from bank IDs to graph vertices
vertex_map = Dict{Int, Int}()
#vertex_counter = 1 # ???? vertex_counter before loop or after loop? Neither works.
for bank in banks
vertex_counter = 1
vertex_map[bank] = vertex_counter
add_vertex!(g)
social_network = df[df.bank_id .== bank, :social_network][1]
if haskey(social_network_groups, social_network)
push!(social_network_groups[social_network], vertex_counter)
else
social_network_groups[social_network] = [vertex_counter]
end
vertex_counter += 1
end
# Function to update the network at each time step
function update_network!(g, df, t)
# Clear existing edges
for e in edges(g)
rem_edge!(g, edges(g))
end
# Add edges from depositors to banks based on the current time step
for row in eachrow(df)
if row.time <= t
depositor_id = row.depositor_id
bank_vertex = vertex_map[row.bank_id]
add_edge!(g, depositor_id, bank_vertex)
end
end
end
# Function to plot the network
function plot_network(g, social_network_groups, group_colors, t)
# Assign colors to vertices based on their social_network group
vertex_colors = fill("black", nv(g))
for (group, vertices) in social_network_groups
for vertex in vertices
vertex_colors[vertex] = group_colors[group]
end
end
# Plot the graph with vertex colors using Plots.jl and GraphRecipes.jl
graphplot(g, names=1:nv(g), nodecolor=vertex_colors, method=:spring, title="Time Step: $t")
end
# Define colors for different social_network groups
group_colors = Dict{Int, String}()
color_palette = distinguishable_colors(length(keys(social_network_groups)))
#i = 1
for group in keys(social_network_groups)
i = 1
group_colors[group] = hex(color_palette[i])
i += 1
end
# Simulate the network evolution
max_time = maximum(df.time)
for t in 1:max_time
update_network!(g, df, t)
plot_network(g, social_network_groups, group_colors, t)
sleep(1) # Pause for a second to visualize the evolution
end
Perhaps this message file size is too big, since I can’t upload the data frames. I’ll send in a separate message. Thanks again.
I see, you’re running your code as a script. In general you should then place (almost) all of the code inside functions (e.g. a main function which you call at the end of your script). This will help for performance, and also get rid of your scope issue when defining vertex_counter (and later i) outside of the for loop.
Example
When run as a script
x = 0
for i = 1:5
x += 1
end
println(x)
indeed throws an exception
┌ Warning: Assignment to `x` in soft scope is ambiguous because a global variable by the same name exists: `x` will be treated as a new local. Disambiguate by using `local x` to suppress this warning or `global x` to assign to the existing global variable.
└ @ (...)\example.jl:3
ERROR: LoadError: UndefVarError: `x` not defined
Stacktrace:
[1] top-level scope
@ (...)\example.jl:3
in expression starting at (...)\example.jl:2
while
function main()
x = 0
for i = 1:5
x += 1
end
println(x)
end
main()
runs fine:
5
If you then define your variables outside of the loop (which you should, keeping the first note in my previous post in mind), you will still run into the second error. The solution here is to simply not use hex.
Example
using Plots
function main()
x = rand(10) |> sort
y = rand(10)
color_palette = distinguishable_colors(10)
plot(x, y, color=hex.(color_palette))
end
main()
will throw
ERROR: Unknown color: 000000
but
using Plots
function main()
x = rand(10) |> sort
y = rand(10)
color_palette = distinguishable_colors(10)
plot(x, y, color=color_palette)
end
main()