Skip to content

Commit

Permalink
Merge branch 'master' of github.com:EcoJulia/EcologicalNetworksPlots.jl
Browse files Browse the repository at this point in the history
  • Loading branch information
tpoisot committed Nov 26, 2020
2 parents e122c63 + b12869d commit a2209a9
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 54 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/TagBot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ jobs:
steps:
- uses: JuliaRegistries/TagBot@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
token: ${{ secrets.TOKEN }}
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "EcologicalNetworksPlots"
uuid = "9f7a259d-73a7-556d-a7a2-3eb122d3865b"
authors = ["Timothée Poisot <[email protected]>"]
version = "0.0.8"
version = "0.0.9"

[deps]
EcologicalNetworks = "f03a62fe-f8ab-5b77-a061-bb599b765229"
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ generates force-directed layouts, circular layouts, bipartite layouts,
heatmaps, as well as layouts based on node properties. Note that `Plots.jl`
*must* be installed in the project, and loaded, for this package to work.

**Documentation:** [Stable](https://ecojulia.github.io/EcologicalNetworksPlots.jl/stable/) `//` [Latest](https://ecojulia.github.io/EcologicalNetworksPlots.jl/latest/)
[![latest doc](https://img.shields.io/badge/documentation-stable-brightgreen)](https://ecojulia.github.io/EcologicalNetworksPlots.jl/stable/) [![latest doc](https://img.shields.io/badge/documentation-latest-green)](https://ecojulia.github.io/EcologicalNetworksPlots.jl/latest/)

![CI](https://github.com/EcoJulia/EcologicalNetworksPlots.jl/workflows/CI/badge.svg?branch=master)
![CI](https://github.com/EcoJulia/EcologicalNetworksPlots.jl/workflows/CI/badge.svg?branch=master) [![codecov](https://codecov.io/gh/EcoJulia/EcologicalNetworksPlots.jl/branch/master/graph/badge.svg?token=HKaubLliPG)](https://codecov.io/gh/EcoJulia/EcologicalNetworksPlots.jl)

[![DOI](https://zenodo.org/badge/143920106.svg)](https://zenodo.org/badge/latestdoi/143920106)
[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active) [![DOI](https://zenodo.org/badge/143920106.svg)](https://zenodo.org/badge/latestdoi/143920106)
4 changes: 2 additions & 2 deletions docs/src/advanced/subsets.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ scatter!(I, N[core3], mc=:red)
We can also use this ability to show the modular structure of a network:

```@example default
N = convert(BinaryNetwork, Umod)
N = web_of_life("M_PA_003")
I = initial(RandomInitialLayout, N)
for step in 1:2000
position!(SpringElectric(1.2; gravity=0.75), I, N)
end
# Modularity
_, P = brim(lp(N)...)
B, P = brim(lp(convert(BinaryNetwork, N))...)
plot(I, N, aspectratio=1)
scatter!(I, N, msw=0.0, nodefill=P, c=:Set2)
```
44 changes: 18 additions & 26 deletions docs/src/layouts/forcedirected.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ this network has disconnected components, and we have no gravity, we expect that
they will be quite far from one another:

```@example default
for step in 1:5000
for step in 1:(100richness(N))
position!(ForceDirectedLayout(0.3, 0.3; gravity=0.0), I, N)
end
plot(I, N, aspectratio=1)
Expand All @@ -42,7 +42,7 @@ We can turn gravity on just a little bit:

```@example default
I = initial(RandomInitialLayout, N)
for step in 1:5000
for step in 1:(100richness(N))
position!(ForceDirectedLayout(0.3, 0.3; gravity=0.2), I, N)
end
plot(I, N, aspectratio=1)
Expand All @@ -54,7 +54,7 @@ on some more):

```@example default
I = initial(RandomInitialLayout, N)
for step in 1:5000
for step in 1:(100richness(N))
position!(ForceDirectedLayout(0.3, 0.75; gravity=0.4), I, N)
end
plot(I, N, aspectratio=1)
Expand All @@ -76,7 +76,7 @@ The Fruchterman-Rheingold method is the default:

```@example default
I = initial(RandomInitialLayout, N)
for step in 1:5000
for step in 1:(100richness(N))
position!(FruchtermanRheingold(0.3; gravity=0.2), I, N)
end
plot(I, N, aspectratio=1)
Expand All @@ -88,7 +88,7 @@ at showing the modules and long paths in a network.

```@example default
I = initial(RandomInitialLayout, N)
for step in 1:5000
for step in 1:(100richness(N))
position!(ForceAtlas2(0.8; gravity=0.2), I, N)
end
plot(I, N, aspectratio=1)
Expand All @@ -101,7 +101,7 @@ visualisation:

```@example default
I = initial(RandomInitialLayout, N)
for step in 1:5000
for step in 1:(100richness(N))
position!(SpringElectric(1.2; gravity=0.2), I, N)
end
plot(I, N, aspectratio=1)
Expand All @@ -120,7 +120,7 @@ All nodes repel at the same force, no impact of edge weight:
I = initial(RandomInitialLayout, N)
L = SpringElectric(1.2; gravity=0.2)
L.degree = false
for step in 1:5000
for step in 1:(100richness(N))
position!(L, I, N)
end
plot(I, N, aspectratio=1)
Expand All @@ -134,7 +134,7 @@ I = initial(RandomInitialLayout, N)
L = SpringElectric(1.2; gravity=0.2)
L.degree = false
L.δ = 0.1
for step in 1:5000
for step in 1:(100richness(N))
position!(L, I, N)
end
plot(I, N, aspectratio=1)
Expand All @@ -148,7 +148,7 @@ I = initial(RandomInitialLayout, N)
L = SpringElectric(1.2; gravity=0.2)
L.degree = false
L.δ = 2.0
for step in 1:5000
for step in 1:(100richness(N))
position!(L, I, N)
end
plot(I, N, aspectratio=1)
Expand All @@ -162,7 +162,7 @@ I = initial(RandomInitialLayout, N)
L = SpringElectric(1.2; gravity=0.2)
L.degree = true
L.δ = 2.0
for step in 1:5000
for step in 1:(100richness(N))
position!(L, I, N)
end
plot(I, N, aspectratio=1)
Expand All @@ -176,7 +176,7 @@ I = initial(RandomInitialLayout, N)
L = SpringElectric(1.2; gravity=0.2)
L.degree = true
L.δ = 0.2
for step in 1:5000
for step in 1:(100richness(N))
position!(L, I, N)
end
plot(I, N, aspectratio=1)
Expand All @@ -187,24 +187,16 @@ scatter!(I, N, bipartite=true)

One convenient way to plot food webs is to prevent them from moving on the *y*
axis, so that every species remains at its trophic level. This can be done by
changing the `move` field (as `ForceDirectedLayout` is a mutable type). Note
that in this example, we *update* the layout after plotting, by replacing the
fractional trophic level by the actual trophic level (and we color the nodes by
their degree, which is covered more in-depth in the next section of this
documentation).
changing the `move` field (as `ForceDirectedLayout` is a mutable type).

```@example default
Fweb = simplify(nz_stream_foodweb()[1])
I = initial(FoodwebInitialLayout, Fweb)
N = simplify(nz_stream_foodweb()[5])
I = initial(FoodwebInitialLayout, N)
L = SpringElectric(1.2; gravity=0.05)
L.move = (true, false)
for step in 1:5000
position!(L, I, Fweb)
end
tl = trophic_level(Fweb)
for s in species(Fweb)
I[s].y = tl[s]
for step in 1:(100richness(N))
position!(L, I, N)
end
plot(I, Fweb)
scatter!(I, Fweb, nodefill=degree(Fweb), c=:YlGn)
plot(I, N)
scatter!(I, N, nodefill=omnivory(N), nodesize=degree(N), c=:YlGn)
```
31 changes: 20 additions & 11 deletions src/forcedirected.jl
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,15 @@ function attract!(LA::T, n1::NodePosition, n2::NodePosition, fa) where {T <: For
δx = n1.x - n2.x
δy = n1.y - n2.y
Δ = sqrt(δx^2.0+δy^2.0)
Δ = Δ == 0.0 ? 0.0001 : Δ
if LA.move[1]
n1.vx -= δx/Δ*fa(Δ)
n2.vx += δx/Δ*fa(Δ)
end
if LA.move[2]
n1.vy -= δy/Δ*fa(Δ)
n2.vy += δy/Δ*fa(Δ)
if !iszero(Δ)
if LA.move[1]
n1.vx -= δx/Δ*fa(Δ)
n2.vx += δx/Δ*fa(Δ)
end
if LA.move[2]
n1.vy -= δy/Δ*fa(Δ)
n2.vy += δy/Δ*fa(Δ)
end
end
end

Expand All @@ -125,9 +126,10 @@ Update the position of a node
"""
function update!(n::NodePosition)
Δ = sqrt(n.vx^2.0+n.vy^2.0)
Δ = Δ == 0.0 ? 0.0001 : Δ
n.x += n.vx/Δ*min(Δ, 0.01)
n.y += n.vy/Δ*min(Δ, 0.01)
if !iszero(Δ)
n.x += n.vx/Δ*min(Δ, 0.01)
n.y += n.vy/Δ*min(Δ, 0.01)
end
stop!(n)
end

Expand All @@ -138,6 +140,13 @@ One iteration of the force-directed layout routine. Because these algorithms can
take some time to converge, it may be useful to stop every 500 iterations to
have a look at the results. Note that to avoid oscillations, the maximum
displacement at any given time is set to 0.01 units.
These layouts tend to have O(N³) complexity, where N is the number of nodes in
the network. This is because repulsion required to do (N×(N-1))/2 visits on
pairs of nodes, and an optimal layout usually requires s×N steps to converge.
With the maximal displacement set to 0.01, we have found that k ≈ 100 gives
acceptable results. This will depend on the complexity of the network, and its
connectance, as well as the degree and edge strengths distributions.
"""
function position!(LA::ForceDirectedLayout, L::Dict{K,NodePosition}, N::T) where {T <: EcologicalNetworks.AbstractEcologicalNetwork} where {K}

Expand Down
27 changes: 17 additions & 10 deletions src/initial_layouts.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
"""
initial(::Type{RandomInitialLayout}, N::T) where {T <: EcologicalNetworks.AbstractEcologicalNetwork}
Random disposition of nodes in the unit square. This is a good starting
point for any force-directed layout.
Random disposition of nodes in a circle. This is a good starting point for any
force-directed layout. The circle is scaled so that its radius is twice the
square root of the network richness, which helps most layouts converge faster.
"""
function initial(::Type{RandomInitialLayout}, N::T) where {T <: EcologicalNetworks.AbstractEcologicalNetwork}
return Dict([s => NodePosition() for s in species(N)])
L = Dict([s => NodePosition() for s in species(N)])
_adj = 2sqrt(richness(N))
for s in species(N)
L[s].x *= _adj
L[s].y *= _adj
end
return L
end

"""
Expand All @@ -26,16 +33,16 @@ end
initial(::Type{FoodwebInitialLayout}, N::T) where {T <: EcologicalNetworks.AbstractUnipartiteNetwork}
Random disposition of nodes on trophic levels for food webs. Note that the
*fractional* trophic level is used, but the layout can be modified afterwards
to use the continuous levels.
continuous trophic level is used, but the layout can be modified afterwards to
use another measure of trophic rank.
"""
function initial(::Type{FoodwebInitialLayout}, N::T) where {T <: EcologicalNetworks.AbstractUnipartiteNetwork}
level = NodePosition[]
tl = fractional_trophic_level(N)
for (i, s) in enumerate(species(N))
push!(level, NodePosition(rand(), float(tl[s]), 0.0, 0.0))
L = initial(RandomInitialLayout, N)
tl = trophic_level(N)
for s in species(N)
L[s].y = tl[s]
end
return Dict(zip(species(N), level))
return L
end

"""
Expand Down

2 comments on commit a2209a9

@tpoisot
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/25347

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.0.9 -m "<description of version>" a2209a949b9ae0a0206dd1940c80109345abe1e7
git push origin v0.0.9

Please sign in to comment.